Added table of API Keys at settings page
This commit is contained in:
parent
c8d70d34e3
commit
24ba353a10
4 changed files with 129 additions and 121 deletions
|
|
@ -23,6 +23,7 @@ export const USER_EDIT_ERROR_ALERT = "Error on edit user";
|
|||
export const USER_ADD_ERROR_ALERT = "Error when adding new user";
|
||||
export const SIGNIN_ERROR_ALERT = "Error signing in";
|
||||
export const DEL_KEY_ERROR_ALERT = "Error on delete key";
|
||||
export const DEL_KEY_ERROR_ALERT_PLURAL = "Error on delete keys";
|
||||
export const UPLOAD_ERROR_ALERT = "Error uploading file";
|
||||
export const WRONG_FILE_ERROR_ALERT = "Invalid file type";
|
||||
export const UPLOAD_ALERT_LIST = "Please upload a JSON file";
|
||||
|
|
@ -54,6 +55,7 @@ export const USER_DEL_SUCCESS_ALERT = "Success! User deleted!";
|
|||
export const USER_EDIT_SUCCESS_ALERT = "Success! User edited!";
|
||||
export const USER_ADD_SUCCESS_ALERT = "Success! New user added!";
|
||||
export const DEL_KEY_SUCCESS_ALERT = "Success! Key deleted!";
|
||||
export const DEL_KEY_SUCCESS_ALERT_PLURAL = "Success! Keys deleted!";
|
||||
export const FLOW_BUILD_SUCCESS_ALERT = `Flow built successfully`;
|
||||
export const SAVE_SUCCESS_ALERT = "Changes saved successfully!";
|
||||
|
||||
|
|
|
|||
|
|
@ -613,11 +613,8 @@ export const FETCH_ERROR_DESCRIPION =
|
|||
|
||||
export const SIGN_UP_SUCCESS = "Account created! Await admin activation. ";
|
||||
|
||||
export const API_PAGE_PARAGRAPH_1 =
|
||||
"Your secret API keys are listed below. Please note that we do not display your secret API keys again after you generate them.";
|
||||
|
||||
export const API_PAGE_PARAGRAPH_2 =
|
||||
"Do not share your API key with others, or expose it in the browser or other client-side code.";
|
||||
export const API_PAGE_PARAGRAPH =
|
||||
"Your secret API keys are listed below. Do not share your API key with others, or expose it in the browser or other client-side code.";
|
||||
|
||||
export const API_PAGE_USER_KEYS =
|
||||
"This user does not have any keys assigned at the moment.";
|
||||
|
|
|
|||
|
|
@ -22,8 +22,7 @@ import {
|
|||
DEL_KEY_SUCCESS_ALERT,
|
||||
} from "../../constants/alerts_constants";
|
||||
import {
|
||||
API_PAGE_PARAGRAPH_1,
|
||||
API_PAGE_PARAGRAPH_2,
|
||||
API_PAGE_PARAGRAPH,
|
||||
API_PAGE_USER_KEYS,
|
||||
LAST_USED_SPAN_1,
|
||||
LAST_USED_SPAN_2,
|
||||
|
|
@ -128,9 +127,7 @@ export default function ApiKeysPage() {
|
|||
API keys
|
||||
</h2>
|
||||
<p className="text-muted-foreground">
|
||||
{API_PAGE_PARAGRAPH_1}
|
||||
<br />
|
||||
{API_PAGE_PARAGRAPH_2}
|
||||
{API_PAGE_PARAGRAPH}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2"></div>
|
||||
|
|
|
|||
|
|
@ -2,149 +2,154 @@ import IconComponent from "../../../../components/genericIconComponent";
|
|||
import { Button } from "../../../../components/ui/button";
|
||||
|
||||
import { ColDef, ColGroupDef, SelectionChangedEvent } from "ag-grid-community";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useContext, useEffect, useRef, useState } from "react";
|
||||
import AddNewVariableButton from "../../../../components/addNewVariableButtonComponent/addNewVariableButton";
|
||||
import Dropdown from "../../../../components/dropdownComponent";
|
||||
import ForwardedIconComponent from "../../../../components/genericIconComponent";
|
||||
import TableComponent from "../../../../components/tableComponent";
|
||||
import { Badge } from "../../../../components/ui/badge";
|
||||
import { Card, CardContent } from "../../../../components/ui/card";
|
||||
import { deleteGlobalVariable } from "../../../../controllers/API";
|
||||
import {
|
||||
deleteApiKey,
|
||||
deleteGlobalVariable,
|
||||
getApiKey,
|
||||
} from "../../../../controllers/API";
|
||||
import useAlertStore from "../../../../stores/alertStore";
|
||||
import { useGlobalVariablesStore } from "../../../../stores/globalVariablesStore/globalVariables";
|
||||
import { cn } from "../../../../utils/utils";
|
||||
import {
|
||||
API_PAGE_PARAGRAPH,
|
||||
LAST_USED_SPAN_1,
|
||||
LAST_USED_SPAN_2,
|
||||
} from "../../../../constants/constants";
|
||||
import TableAutoCellRender from "../../../../components/tableAutoCellRender";
|
||||
import {
|
||||
DEL_KEY_SUCCESS_ALERT,
|
||||
DEL_KEY_ERROR_ALERT,
|
||||
DEL_KEY_SUCCESS_ALERT_PLURAL,
|
||||
DEL_KEY_ERROR_ALERT_PLURAL,
|
||||
} from "../../../../constants/alerts_constants";
|
||||
import { AuthContext } from "../../../../contexts/authContext";
|
||||
import { ApiKey } from "../../../../types/components";
|
||||
import SecretKeyModal from "../../../../modals/secretKeyModal";
|
||||
|
||||
export default function ApiKeysPage() {
|
||||
const globalVariablesEntries = useGlobalVariablesStore(
|
||||
(state) => state.globalVariablesEntries,
|
||||
);
|
||||
const removeGlobalVariable = useGlobalVariablesStore(
|
||||
(state) => state.removeGlobalVariable,
|
||||
);
|
||||
const globalVariables = useGlobalVariablesStore(
|
||||
(state) => state.globalVariables,
|
||||
);
|
||||
const [loadingKeys, setLoadingKeys] = useState(true);
|
||||
const [selectedRows, setSelectedRows] = useState<string[]>([]);
|
||||
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const getVariableId = useGlobalVariablesStore((state) => state.getVariableId);
|
||||
|
||||
const BadgeRenderer = (props) => {
|
||||
return props.value !== "" ? (
|
||||
<div>
|
||||
<Badge variant="outline" size="md" className="font-normal">
|
||||
{props.value}
|
||||
</Badge>
|
||||
</div>
|
||||
) : (
|
||||
<div></div>
|
||||
);
|
||||
};
|
||||
|
||||
const [rowData, setRowData] = useState<
|
||||
{
|
||||
type: string | undefined;
|
||||
id: string;
|
||||
name: string;
|
||||
default_fields: string | undefined;
|
||||
}[]
|
||||
>([]);
|
||||
const { userData } = useContext(AuthContext);
|
||||
const [userId, setUserId] = useState("");
|
||||
const keysList = useRef([]);
|
||||
|
||||
useEffect(() => {
|
||||
const rows: Array<{
|
||||
type: string | undefined;
|
||||
id: string;
|
||||
name: string;
|
||||
default_fields: string | undefined;
|
||||
}> = [];
|
||||
if (globalVariablesEntries === undefined) return;
|
||||
globalVariablesEntries.forEach((entrie) => {
|
||||
const globalVariableObj = globalVariables[entrie];
|
||||
rows.push({
|
||||
type: globalVariableObj.type,
|
||||
id: globalVariableObj.id,
|
||||
default_fields: (globalVariableObj.default_fields ?? []).join(", "),
|
||||
name: entrie,
|
||||
});
|
||||
});
|
||||
setRowData(rows);
|
||||
}, [globalVariables]);
|
||||
getKeys();
|
||||
}, [userData]);
|
||||
|
||||
const DropdownEditor = ({ options, value, onValueChange }) => {
|
||||
function getKeys() {
|
||||
setLoadingKeys(true);
|
||||
if (userData) {
|
||||
getApiKey()
|
||||
.then((keys: [ApiKey]) => {
|
||||
keysList.current = keys["api_keys"].map((apikey: ApiKey) => ({
|
||||
...apikey,
|
||||
last_used_at: apikey.last_used_at ?? "Never",
|
||||
}));
|
||||
setUserId(keys["user_id"]);
|
||||
setLoadingKeys(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
setLoadingKeys(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function resetFilter() {
|
||||
getKeys();
|
||||
}
|
||||
|
||||
function handleDeleteKey() {
|
||||
Promise.all(selectedRows.map((selectedRow) => deleteApiKey(selectedRow)))
|
||||
.then((res) => {
|
||||
resetFilter();
|
||||
setSuccessData({
|
||||
title:
|
||||
selectedRows.length === 1
|
||||
? DEL_KEY_SUCCESS_ALERT
|
||||
: DEL_KEY_SUCCESS_ALERT_PLURAL,
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
setErrorData({
|
||||
title:
|
||||
selectedRows.length === 1
|
||||
? DEL_KEY_ERROR_ALERT
|
||||
: DEL_KEY_ERROR_ALERT_PLURAL,
|
||||
list: [error["response"]["data"]["detail"]],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function lastUsedMessage() {
|
||||
return (
|
||||
<Dropdown options={options} value={value} onSelect={onValueChange}>
|
||||
<div className="-mt-1.5 w-full"></div>
|
||||
</Dropdown>
|
||||
<div className="text-xs">
|
||||
<span>
|
||||
{LAST_USED_SPAN_1}
|
||||
<br></br> {LAST_USED_SPAN_2}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
// Column Definitions: Defines the columns to be displayed.
|
||||
const [colDefs, setColDefs] = useState<(ColDef<any> | ColGroupDef<any>)[]>([
|
||||
}
|
||||
|
||||
const columnDefs = [
|
||||
{
|
||||
headerCheckboxSelection: true,
|
||||
checkboxSelection: true,
|
||||
showDisabledCheckboxes: true,
|
||||
headerName: "Variable Name",
|
||||
headerName: "Name",
|
||||
field: "name",
|
||||
cellRenderer: TableAutoCellRender,
|
||||
flex: 2,
|
||||
}, //This column will be twice as wide as the others
|
||||
{
|
||||
field: "type",
|
||||
cellRenderer: BadgeRenderer,
|
||||
cellEditor: DropdownEditor,
|
||||
cellEditorParams: {
|
||||
options: ["Generic", "Credential"],
|
||||
},
|
||||
flex: 1,
|
||||
editable: false,
|
||||
},
|
||||
// {
|
||||
// field: "value",
|
||||
// cellEditor: "agLargeTextCellEditor",
|
||||
// flex: 2,
|
||||
// editable: false,
|
||||
// },
|
||||
{
|
||||
headerName: "Apply To Fields",
|
||||
field: "default_fields",
|
||||
headerName: "Key",
|
||||
field: "api_key",
|
||||
cellRenderer: TableAutoCellRender,
|
||||
flex: 1,
|
||||
},
|
||||
{
|
||||
headerName: "Created",
|
||||
field: "created_at",
|
||||
cellRenderer: TableAutoCellRender,
|
||||
flex: 1,
|
||||
},
|
||||
{
|
||||
headerName: "Last Used",
|
||||
field: "last_used_at",
|
||||
cellRenderer: TableAutoCellRender,
|
||||
flex: 1,
|
||||
},
|
||||
{
|
||||
headerName: "Total Uses",
|
||||
field: "total_uses",
|
||||
cellRenderer: TableAutoCellRender,
|
||||
flex: 1,
|
||||
editable: false,
|
||||
resizable: false,
|
||||
},
|
||||
]);
|
||||
|
||||
const [selectedRows, setSelectedRows] = useState<string[]>([]);
|
||||
|
||||
async function removeVariables() {
|
||||
const deleteGlobalVariablesPromise = selectedRows.map(async (row) => {
|
||||
const id = getVariableId(row);
|
||||
const deleteGlobalVariables = deleteGlobalVariable(id!);
|
||||
await deleteGlobalVariables;
|
||||
});
|
||||
Promise.all(deleteGlobalVariablesPromise)
|
||||
.then(() => {
|
||||
selectedRows.forEach((row) => {
|
||||
removeGlobalVariable(row);
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
setErrorData({
|
||||
title: `Error deleting global variables.`,
|
||||
});
|
||||
});
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full flex-col justify-between gap-6">
|
||||
<div className="flex w-full items-center justify-between gap-4 space-y-0.5">
|
||||
<div className="flex w-full flex-col">
|
||||
<h2 className="flex items-center text-lg font-semibold tracking-tight">
|
||||
Global Variables
|
||||
API Keys
|
||||
<ForwardedIconComponent
|
||||
name="Globe"
|
||||
name="Key"
|
||||
className="ml-2 h-5 w-5 text-primary"
|
||||
/>
|
||||
</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Manage global variables and assign them to fields.
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">{API_PAGE_PARAGRAPH}</p>
|
||||
</div>
|
||||
<div className="flex flex-shrink-0 items-center gap-2">
|
||||
<Button
|
||||
|
|
@ -152,7 +157,7 @@ export default function ApiKeysPage() {
|
|||
variant="primary"
|
||||
className="group px-2"
|
||||
disabled={selectedRows.length === 0}
|
||||
onClick={removeVariables}
|
||||
onClick={handleDeleteKey}
|
||||
>
|
||||
<IconComponent
|
||||
name="Trash2"
|
||||
|
|
@ -161,12 +166,19 @@ export default function ApiKeysPage() {
|
|||
)}
|
||||
/>
|
||||
</Button>
|
||||
<AddNewVariableButton>
|
||||
<SecretKeyModal
|
||||
title="Create new secret key"
|
||||
cancelText="Cancel"
|
||||
confirmationText="Create secret key"
|
||||
icon={"Key"}
|
||||
data={userId}
|
||||
onCloseModal={getKeys}
|
||||
>
|
||||
<Button data-testid="api-key-button-store" variant="primary">
|
||||
<IconComponent name="Plus" className="mr-2 w-4" />
|
||||
Add New
|
||||
</Button>
|
||||
</AddNewVariableButton>
|
||||
</SecretKeyModal>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -177,14 +189,14 @@ export default function ApiKeysPage() {
|
|||
overlayNoRowsTemplate="No data available"
|
||||
onSelectionChanged={(event: SelectionChangedEvent) => {
|
||||
setSelectedRows(
|
||||
event.api.getSelectedRows().map((row) => row.name),
|
||||
event.api.getSelectedRows().map((row) => row.id),
|
||||
);
|
||||
}}
|
||||
rowSelection="multiple"
|
||||
suppressRowClickSelection={true}
|
||||
pagination={true}
|
||||
columnDefs={colDefs}
|
||||
rowData={rowData}
|
||||
columnDefs={columnDefs}
|
||||
rowData={keysList.current}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue