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 USER_ADD_ERROR_ALERT = "Error when adding new user";
|
||||||
export const SIGNIN_ERROR_ALERT = "Error signing in";
|
export const SIGNIN_ERROR_ALERT = "Error signing in";
|
||||||
export const DEL_KEY_ERROR_ALERT = "Error on delete key";
|
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 UPLOAD_ERROR_ALERT = "Error uploading file";
|
||||||
export const WRONG_FILE_ERROR_ALERT = "Invalid file type";
|
export const WRONG_FILE_ERROR_ALERT = "Invalid file type";
|
||||||
export const UPLOAD_ALERT_LIST = "Please upload a JSON file";
|
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_EDIT_SUCCESS_ALERT = "Success! User edited!";
|
||||||
export const USER_ADD_SUCCESS_ALERT = "Success! New user added!";
|
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 = "Success! Key deleted!";
|
||||||
|
export const DEL_KEY_SUCCESS_ALERT_PLURAL = "Success! Keys deleted!";
|
||||||
export const FLOW_BUILD_SUCCESS_ALERT = `Flow built successfully`;
|
export const FLOW_BUILD_SUCCESS_ALERT = `Flow built successfully`;
|
||||||
export const SAVE_SUCCESS_ALERT = "Changes saved 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 SIGN_UP_SUCCESS = "Account created! Await admin activation. ";
|
||||||
|
|
||||||
export const API_PAGE_PARAGRAPH_1 =
|
export const API_PAGE_PARAGRAPH =
|
||||||
"Your secret API keys are listed below. Please note that we do not display your secret API keys again after you generate them.";
|
"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_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_USER_KEYS =
|
export const API_PAGE_USER_KEYS =
|
||||||
"This user does not have any keys assigned at the moment.";
|
"This user does not have any keys assigned at the moment.";
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,7 @@ import {
|
||||||
DEL_KEY_SUCCESS_ALERT,
|
DEL_KEY_SUCCESS_ALERT,
|
||||||
} from "../../constants/alerts_constants";
|
} from "../../constants/alerts_constants";
|
||||||
import {
|
import {
|
||||||
API_PAGE_PARAGRAPH_1,
|
API_PAGE_PARAGRAPH,
|
||||||
API_PAGE_PARAGRAPH_2,
|
|
||||||
API_PAGE_USER_KEYS,
|
API_PAGE_USER_KEYS,
|
||||||
LAST_USED_SPAN_1,
|
LAST_USED_SPAN_1,
|
||||||
LAST_USED_SPAN_2,
|
LAST_USED_SPAN_2,
|
||||||
|
|
@ -128,9 +127,7 @@ export default function ApiKeysPage() {
|
||||||
API keys
|
API keys
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-muted-foreground">
|
<p className="text-muted-foreground">
|
||||||
{API_PAGE_PARAGRAPH_1}
|
{API_PAGE_PARAGRAPH}
|
||||||
<br />
|
|
||||||
{API_PAGE_PARAGRAPH_2}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2"></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 { Button } from "../../../../components/ui/button";
|
||||||
|
|
||||||
import { ColDef, ColGroupDef, SelectionChangedEvent } from "ag-grid-community";
|
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 AddNewVariableButton from "../../../../components/addNewVariableButtonComponent/addNewVariableButton";
|
||||||
import Dropdown from "../../../../components/dropdownComponent";
|
import Dropdown from "../../../../components/dropdownComponent";
|
||||||
import ForwardedIconComponent from "../../../../components/genericIconComponent";
|
import ForwardedIconComponent from "../../../../components/genericIconComponent";
|
||||||
import TableComponent from "../../../../components/tableComponent";
|
import TableComponent from "../../../../components/tableComponent";
|
||||||
import { Badge } from "../../../../components/ui/badge";
|
import { Badge } from "../../../../components/ui/badge";
|
||||||
import { Card, CardContent } from "../../../../components/ui/card";
|
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 useAlertStore from "../../../../stores/alertStore";
|
||||||
import { useGlobalVariablesStore } from "../../../../stores/globalVariablesStore/globalVariables";
|
import { useGlobalVariablesStore } from "../../../../stores/globalVariablesStore/globalVariables";
|
||||||
import { cn } from "../../../../utils/utils";
|
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() {
|
export default function ApiKeysPage() {
|
||||||
const globalVariablesEntries = useGlobalVariablesStore(
|
const [loadingKeys, setLoadingKeys] = useState(true);
|
||||||
(state) => state.globalVariablesEntries,
|
const [selectedRows, setSelectedRows] = useState<string[]>([]);
|
||||||
);
|
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
||||||
const removeGlobalVariable = useGlobalVariablesStore(
|
|
||||||
(state) => state.removeGlobalVariable,
|
|
||||||
);
|
|
||||||
const globalVariables = useGlobalVariablesStore(
|
|
||||||
(state) => state.globalVariables,
|
|
||||||
);
|
|
||||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||||
const getVariableId = useGlobalVariablesStore((state) => state.getVariableId);
|
const { userData } = useContext(AuthContext);
|
||||||
|
const [userId, setUserId] = useState("");
|
||||||
const BadgeRenderer = (props) => {
|
const keysList = useRef([]);
|
||||||
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;
|
|
||||||
}[]
|
|
||||||
>([]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const rows: Array<{
|
getKeys();
|
||||||
type: string | undefined;
|
}, [userData]);
|
||||||
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]);
|
|
||||||
|
|
||||||
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 (
|
return (
|
||||||
<Dropdown options={options} value={value} onSelect={onValueChange}>
|
<div className="text-xs">
|
||||||
<div className="-mt-1.5 w-full"></div>
|
<span>
|
||||||
</Dropdown>
|
{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,
|
headerCheckboxSelection: true,
|
||||||
checkboxSelection: true,
|
checkboxSelection: true,
|
||||||
showDisabledCheckboxes: true,
|
showDisabledCheckboxes: true,
|
||||||
headerName: "Variable Name",
|
headerName: "Name",
|
||||||
field: "name",
|
field: "name",
|
||||||
|
cellRenderer: TableAutoCellRender,
|
||||||
flex: 2,
|
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",
|
headerName: "Key",
|
||||||
field: "default_fields",
|
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,
|
flex: 1,
|
||||||
editable: false,
|
|
||||||
resizable: 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 (
|
return (
|
||||||
<div className="flex h-full w-full flex-col justify-between gap-6">
|
<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 items-center justify-between gap-4 space-y-0.5">
|
||||||
<div className="flex w-full flex-col">
|
<div className="flex w-full flex-col">
|
||||||
<h2 className="flex items-center text-lg font-semibold tracking-tight">
|
<h2 className="flex items-center text-lg font-semibold tracking-tight">
|
||||||
Global Variables
|
API Keys
|
||||||
<ForwardedIconComponent
|
<ForwardedIconComponent
|
||||||
name="Globe"
|
name="Key"
|
||||||
className="ml-2 h-5 w-5 text-primary"
|
className="ml-2 h-5 w-5 text-primary"
|
||||||
/>
|
/>
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">{API_PAGE_PARAGRAPH}</p>
|
||||||
Manage global variables and assign them to fields.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-shrink-0 items-center gap-2">
|
<div className="flex flex-shrink-0 items-center gap-2">
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -152,7 +157,7 @@ export default function ApiKeysPage() {
|
||||||
variant="primary"
|
variant="primary"
|
||||||
className="group px-2"
|
className="group px-2"
|
||||||
disabled={selectedRows.length === 0}
|
disabled={selectedRows.length === 0}
|
||||||
onClick={removeVariables}
|
onClick={handleDeleteKey}
|
||||||
>
|
>
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="Trash2"
|
name="Trash2"
|
||||||
|
|
@ -161,12 +166,19 @@ export default function ApiKeysPage() {
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</Button>
|
</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">
|
<Button data-testid="api-key-button-store" variant="primary">
|
||||||
<IconComponent name="Plus" className="mr-2 w-4" />
|
<IconComponent name="Plus" className="mr-2 w-4" />
|
||||||
Add New
|
Add New
|
||||||
</Button>
|
</Button>
|
||||||
</AddNewVariableButton>
|
</SecretKeyModal>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -177,14 +189,14 @@ export default function ApiKeysPage() {
|
||||||
overlayNoRowsTemplate="No data available"
|
overlayNoRowsTemplate="No data available"
|
||||||
onSelectionChanged={(event: SelectionChangedEvent) => {
|
onSelectionChanged={(event: SelectionChangedEvent) => {
|
||||||
setSelectedRows(
|
setSelectedRows(
|
||||||
event.api.getSelectedRows().map((row) => row.name),
|
event.api.getSelectedRows().map((row) => row.id),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
rowSelection="multiple"
|
rowSelection="multiple"
|
||||||
suppressRowClickSelection={true}
|
suppressRowClickSelection={true}
|
||||||
pagination={true}
|
pagination={true}
|
||||||
columnDefs={colDefs}
|
columnDefs={columnDefs}
|
||||||
rowData={rowData}
|
rowData={keysList.current}
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue