added the key manager card to the team page

This commit is contained in:
csawatzky 2025-04-24 12:54:47 -06:00
parent 08b100d035
commit 960275eb75
5 changed files with 202 additions and 5 deletions

View file

@ -16,6 +16,7 @@ import { useEffect, useState } from "react";
import { useLocation, useNavigate, useParams } from 'react-router-dom';
import { getSignatureAccentColour } from "services/whiteLabel";
import TeamActions from "teams/TeamActions";
import TeamKeyManager from "teams/TeamKeyManager";
import ObjectUsers from "user/ObjectUsers";
import UserAvatar from "user/UserAvatar";
import { or } from "utils/types";
@ -236,12 +237,12 @@ export default function TeamPage() {
refreshCallback={() => {}}
/>
<Grid2 container spacing={1}>
<Grid2 size={{ xs: 12, sm: 6 }}>
<Grid2 size={{ xs: 12, sm: 4 }}>
<Card className={ isMobile ? classes.cardMobile : classes.card }>
<Chat objectKey={team.key()} type={pond.NoteType.NOTE_TYPE_TEAM}/>
</Card>
</Grid2>
<Grid2 size={{ xs: 12, sm: 6 }}>
<Grid2 size={{ xs: 12, sm: 4 }}>
<Card className={ isMobile ? classes.cardMobile : classes.card }>
<List>
<Divider />
@ -256,6 +257,11 @@ export default function TeamPage() {
</List>
</Card>
</Grid2>
<Grid2 size={{ xs: 12, sm: 4 }}>
<Card className={ isMobile ? classes.cardMobile : classes.card }>
<TeamKeyManager teamId={teamKey} permissions={team.permissions ?? []} />
</Card>
</Grid2>
</Grid2>
</PageContainer>
);

View file

@ -35,7 +35,7 @@ export {
// useStripeAPI,
useDataDogProxyAPI,
useMineAPI,
// useKeyManagerAPI,
useKeyManagerAPI, //TODO: update api with resolve,reject
useTerminalAPI, //TODO: update api with resolve,reject
useGateAPI,
useObjectHeaterAPI, //TODO: update api with resolve,reject

View file

@ -0,0 +1,47 @@
import { AxiosResponse } from "axios";
import { useHTTP } from "hooks";
import { pond } from "protobuf-ts/pond";
import { createContext, PropsWithChildren, useContext } from "react";
import { pondURL } from "./pond";
export interface IKeyManagerAPIContext {
addKey: (teamID: string) => Promise<any>;
listKeys: (teamID: string) => Promise<AxiosResponse<pond.ListAPIKeysResponse>>;
removeKey: (teamID: string, key: string) => Promise<AxiosResponse<pond.RemoveAPIKeyResponse>>;
}
export const KeyManagerAPIContext = createContext<IKeyManagerAPIContext>(
{} as IKeyManagerAPIContext
);
interface Props {}
export default function KeyManagerProvider(props: PropsWithChildren<Props>) {
const { children } = props;
const { get, del, post } = useHTTP();
const addKey = (teamID: string) => {
return post(pondURL("/teams/" + teamID + "/apiKeys"));
};
const removeKey = (teamID: string, key: string) => {
return del<pond.RemoveAPIKeyResponse>(pondURL("/teams/" + teamID + "/apiKeys?key=" + key));
};
const listKeys = (teamID: string) => {
return get<pond.ListAPIKeysResponse>(pondURL("/teams/" + teamID + "/apiKeys"));
};
return (
<KeyManagerAPIContext.Provider
value={{
addKey,
listKeys,
removeKey
}}>
{children}
</KeyManagerAPIContext.Provider>
);
}
export const useKeyManagerAPI = () => useContext(KeyManagerAPIContext);

View file

@ -33,6 +33,7 @@ import TaskProvider, { useTaskAPI } from "./taskAPI";
import DataDogProvider, { useDataDogProxyAPI } from "./datadogProxyAPI";
import ContractProvider, { useContractAPI } from "./contractAPI";
import UsageProvider, { useUsageAPI } from "./usageAPI";
import KeyManagerProvider, { useKeyManagerAPI } from "./keyManagerAPI";
// import NoteProvider from "providers/noteAPI";
export const pondURL = (partial: string, demo: boolean = false): string => {
@ -82,7 +83,9 @@ export default function PondProvider(props: PropsWithChildren<any>) {
<DataDogProvider>
<ContractProvider>
<UsageProvider>
{children}
<KeyManagerProvider>
{children}
</KeyManagerProvider>
</UsageProvider>
</ContractProvider>
</DataDogProvider>
@ -151,5 +154,6 @@ export {
useTaskAPI,
useDataDogProxyAPI,
useContractAPI,
useUsageAPI
useUsageAPI,
useKeyManagerAPI
};

View file

@ -0,0 +1,140 @@
import {
Box,
Button,
DialogActions,
DialogContent,
DialogTitle,
Divider,
Grid2 as Grid,
IconButton,
Typography
} from "@mui/material";
import ResponsiveDialog from "common/ResponsiveDialog";
import DeleteIcon from "products/AgIcons/Delete";
import { pond } from "protobuf-ts/pond";
import { useKeyManagerAPI } from "providers";
import React, { useState } from "react";
interface Props {
teamId: string;
permissions: pond.Permission[];
}
export default function TeamKeyManager(props: Props) {
const { permissions, teamId } = props;
const [keyList, setKeyList] = useState<string[]>([]);
const [openRemove, setOpenRemove] = useState(false);
const [removeIndex, setRemoveIndex] = useState(0);
const keyManagerAPI = useKeyManagerAPI();
const listKeys = () => {
keyManagerAPI.listKeys(teamId).then(resp => {
if (resp.data.keys.length > 0) {
let keys = resp.data.keys;
setKeyList([...keys]);
}
});
};
const addKey = () => {
keyManagerAPI.addKey(teamId).then(resp => {
let keys = keyList;
keys.push(resp.data.key);
setKeyList([...keys]);
});
};
const removeKey = (i: number) => {
keyManagerAPI.removeKey(teamId, keyList[i]).then(resp => {
let keys = keyList;
keys.splice(i, 1);
setKeyList([...keys]);
});
};
const header = () => {
return (
<Grid container justifyContent="space-between" alignItems="center">
<Grid>
<Typography>API Keys</Typography>
</Grid>
<Grid>
<Button
disabled={!permissions.includes(pond.Permission.PERMISSION_WRITE)}
style={{ marginRight: 5 }}
variant="contained"
color="primary"
onClick={listKeys}>
Display Keys
</Button>
<Button
disabled={!permissions.includes(pond.Permission.PERMISSION_WRITE)}
variant="contained"
color="primary"
onClick={addKey}>
Add New Key
</Button>
</Grid>
</Grid>
);
};
const removalConfirmation = () => {
return (
<ResponsiveDialog
open={openRemove}
onClose={() => {
setOpenRemove(false);
}}>
<DialogTitle>Remove API Key</DialogTitle>
<DialogContent>
Are you sure you want to remove {keyList[removeIndex]} as an api key?
</DialogContent>
<DialogActions>
<Button
onClick={() => {
setOpenRemove(false);
}}>
Cancel
</Button>
<Button
variant="contained"
style={{ backgroundColor: "red" }}
onClick={() => {
removeKey(removeIndex);
setOpenRemove(false);
}}>
Delete
</Button>
</DialogActions>
</ResponsiveDialog>
);
};
return (
<Box padding={2} style={{ display: "flex", flexDirection: "column", overflow: "hidden" }}>
{removalConfirmation()}
{header()}
<Divider />
<Grid style={{ padding: 10 }} container justifyContent="space-between" alignItems="center">
{keyList.map((k, i) => (
<React.Fragment key={k}>
<Grid size={10}>
{k}
</Grid>
<Grid size={2}>
<IconButton
disabled={!permissions.includes(pond.Permission.PERMISSION_WRITE)}
onClick={() => {
setOpenRemove(true);
setRemoveIndex(i);
}}>
<DeleteIcon />
</IconButton>
</Grid>
</React.Fragment>
))}
</Grid>
</Box>
);
}