Skip to content

Commit cd3e75b

Browse files
authored
Add setup-key improvements (#420)
- Add support to key deletion - Add custom and unlimited expiration
1 parent f8281c8 commit cd3e75b

File tree

9 files changed

+102
-40
lines changed

9 files changed

+102
-40
lines changed

src/modules/activity/ActivityDescription.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@ export default function ActivityDescription({ event }: Props) {
4747
</div>
4848
);
4949

50+
if (event.activity_code == "setupkey.delete")
51+
return (
52+
<div className={"inline"}>
53+
Setup-Key <Value> {m.name}</Value> with key <Value>{m.key}</Value> was
54+
deleted
55+
</div>
56+
);
57+
5058
if (event.activity_code == "setupkey.add")
5159
return (
5260
<div className={"inline"}>
Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1-
export default function EmptyRow() {
2-
return <div className={"text-nb-gray-600"}>-</div>;
1+
import { cn } from "@utils/helpers";
2+
3+
type Props = {
4+
className?: string;
5+
};
6+
7+
export default function EmptyRow({ className }: Readonly<Props>) {
8+
return <div className={cn("text-nb-gray-600", className)}>-</div>;
39
}
Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Button from "@components/Button";
22
import { notify } from "@components/Notification";
33
import { useApiCall } from "@utils/api";
4-
import { Trash2 } from "lucide-react";
4+
import { Trash2, Undo2Icon } from "lucide-react";
55
import * as React from "react";
66
import { useSWRConfig } from "swr";
77
import { useDialog } from "@/contexts/DialogProvider";
@@ -10,16 +10,26 @@ import { SetupKey } from "@/interfaces/SetupKey";
1010
type Props = {
1111
setupKey: SetupKey;
1212
};
13-
export default function SetupKeyActionCell({ setupKey }: Props) {
13+
export default function SetupKeyActionCell({ setupKey }: Readonly<Props>) {
1414
const { confirm } = useDialog();
15-
const deleteRequest = useApiCall<SetupKey>("/setup-keys/" + setupKey.id);
15+
const request = useApiCall<SetupKey>("/setup-keys/" + setupKey.id);
1616
const { mutate } = useSWRConfig();
1717

1818
const handleRevoke = async () => {
19+
const choice = await confirm({
20+
title: `Revoke '${setupKey?.name || "Setup Key"}'?`,
21+
description:
22+
"Are you sure you want to revoke the setup key? This action cannot be undone.",
23+
confirmText: "Revoke",
24+
cancelText: "Cancel",
25+
type: "danger",
26+
});
27+
if (!choice) return;
28+
1929
notify({
2030
title: setupKey?.name || "Setup Key",
2131
description: "Setup key was successfully revoked",
22-
promise: deleteRequest
32+
promise: request
2333
.put({
2434
name: setupKey?.name || "Setup Key",
2535
type: setupKey.type,
@@ -37,30 +47,43 @@ export default function SetupKeyActionCell({ setupKey }: Props) {
3747
});
3848
};
3949

40-
const handleConfirm = async () => {
50+
const handleDelete = async () => {
4151
const choice = await confirm({
42-
title: `Revoke '${setupKey?.name || "Setup Key"}'?`,
52+
title: `Delete '${setupKey?.name || "Setup Key"}'?`,
4353
description:
44-
"Are you sure you want to revoke the setup key? This action cannot be undone.",
45-
confirmText: "Revoke",
54+
"Are you sure you want to delete the setup key? This action cannot be undone.",
55+
confirmText: "Delete",
4656
cancelText: "Cancel",
4757
type: "danger",
4858
});
4959
if (!choice) return;
50-
handleRevoke().then();
60+
61+
notify({
62+
title: setupKey?.name || "Setup Key",
63+
description: "Setup key was successfully deleted",
64+
promise: request.del().then(() => {
65+
mutate("/setup-keys");
66+
mutate("/groups");
67+
}),
68+
loadingMessage: "Deleting the setup key...",
69+
});
5170
};
5271

5372
return (
5473
<div className={"flex justify-end pr-4"}>
5574
<Button
5675
variant={"danger-outline"}
5776
size={"sm"}
58-
onClick={handleConfirm}
77+
onClick={handleRevoke}
5978
disabled={setupKey.revoked || !setupKey.valid}
6079
>
61-
<Trash2 size={16} />
80+
<Undo2Icon size={16} />
6281
Revoke
6382
</Button>
83+
<Button variant={"danger-outline"} size={"sm"} onClick={handleDelete}>
84+
<Trash2 size={16} />
85+
Delete
86+
</Button>
6487
</div>
6588
);
6689
}

src/modules/setup-keys/SetupKeyGroupsCell.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import GroupsRow from "@/modules/common-table-rows/GroupsRow";
99
type Props = {
1010
setupKey: SetupKey;
1111
};
12-
export default function SetupKeyGroupsCell({ setupKey }: Props) {
12+
export default function SetupKeyGroupsCell({ setupKey }: Readonly<Props>) {
1313
const [modal, setModal] = useState(false);
1414
const request = useApiCall<SetupKey>("/setup-keys/" + setupKey.id);
1515
const { mutate } = useSWRConfig();

src/modules/setup-keys/SetupKeyKeyCell.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ type Props = {
55
text: string;
66
};
77

8-
export default function SetupKeyKeyCell({ text }: Props) {
8+
export default function SetupKeyKeyCell({ text }: Readonly<Props>) {
99
return (
1010
<div className={"flex"}>
1111
<Badge variant={"gray"} className={"text-xs font-mono"}>

src/modules/setup-keys/SetupKeyModal.tsx

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@ type Props = {
4242
setOpen: (open: boolean) => void;
4343
};
4444
const copyMessage = "Setup-Key was copied to your clipboard!";
45-
export default function SetupKeyModal({ children, open, setOpen }: Props) {
45+
export default function SetupKeyModal({
46+
children,
47+
open,
48+
setOpen,
49+
}: Readonly<Props>) {
4650
const [successModal, setSuccessModal] = useState(false);
4751
const [setupKey, setSetupKey] = useState<SetupKey>();
4852
const [, copy] = useCopyToClipboard(setupKey?.key);
@@ -131,7 +135,7 @@ type ModalProps = {
131135
onSuccess?: (setupKey: SetupKey) => void;
132136
};
133137

134-
export function SetupKeyModalContent({ onSuccess }: ModalProps) {
138+
export function SetupKeyModalContent({ onSuccess }: Readonly<ModalProps>) {
135139
const setupKeyRequest = useApiCall<SetupKey>("/setup-keys", true);
136140
const { mutate } = useSWRConfig();
137141

@@ -149,18 +153,10 @@ export function SetupKeyModalContent({ onSuccess }: ModalProps) {
149153
return reusable ? "Unlimited" : "1";
150154
}, [reusable]);
151155

152-
const expiresInError = useMemo(() => {
153-
const expires = parseInt(expiresIn);
154-
if (expires < 1 || expires > 365) {
155-
return "Days should be between 1 and 365";
156-
}
157-
return "";
158-
}, [expiresIn]);
159-
160156
const isDisabled = useMemo(() => {
161157
const trimmedName = trim(name);
162-
return trimmedName.length === 0 || expiresInError.length > 0;
163-
}, [name, expiresInError]);
158+
return trimmedName.length === 0;
159+
}, [name]);
164160

165161
const submit = () => {
166162
if (!selectedGroups) return;
@@ -174,7 +170,7 @@ export function SetupKeyModalContent({ onSuccess }: ModalProps) {
174170
.post({
175171
name,
176172
type: reusable ? "reusable" : "one-off",
177-
expires_in: parseInt(expiresIn ? expiresIn : "7") * 24 * 60 * 60, // Days to seconds, defaults to 7 days
173+
expires_in: parseInt(expiresIn || "0") * 24 * 60 * 60, // Days to seconds, defaults to 7 days
178174
revoked: false,
179175
auto_groups: groups.map((group) => group.id),
180176
usage_limit: reusable ? parseInt(usageLimit) : 1,
@@ -253,15 +249,16 @@ export function SetupKeyModalContent({ onSuccess }: ModalProps) {
253249
<div className={"flex justify-between"}>
254250
<div>
255251
<Label>Expires in</Label>
256-
<HelpText>Should be between 1 and 365 days.</HelpText>
252+
<HelpText>
253+
Days until the key expires. <br />
254+
Leave empty for no expiration.
255+
</HelpText>
257256
</div>
258257
<Input
259-
maxWidthClass={"max-w-[200px]"}
260-
placeholder={"7"}
258+
maxWidthClass={"max-w-[202px]"}
259+
placeholder={"Unlimited"}
261260
min={1}
262-
max={365}
263261
value={expiresIn}
264-
error={expiresInError}
265262
errorTooltip={true}
266263
type={"number"}
267264
data-cy={"setup-key-expire-in-days"}

src/modules/setup-keys/SetupKeyNameCell.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ type Props = {
55
valid: boolean;
66
secret?: string;
77
};
8-
export default function SetupKeyNameCell({ name, valid, secret }: Props) {
8+
export default function SetupKeyNameCell({
9+
name,
10+
valid,
11+
secret,
12+
}: Readonly<Props>) {
913
return (
1014
<ActiveInactiveRow
1115
active={valid || false}

src/modules/setup-keys/SetupKeyTypeCell.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { Repeat1 } from "lucide-react";
55
type Props = {
66
reusable: boolean;
77
};
8-
export default function SetupKeyTypeCell({ reusable }: Props) {
8+
export default function SetupKeyTypeCell({ reusable }: Readonly<Props>) {
99
return (
1010
<div className={"flex"}>
1111
<Badge className={"text-xs"} variant={"gray"}>

src/modules/setup-keys/SetupKeysTable.tsx

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@ import DataTableRefreshButton from "@components/table/DataTableRefreshButton";
88
import { DataTableRowsPerPage } from "@components/table/DataTableRowsPerPage";
99
import GetStartedTest from "@components/ui/GetStartedTest";
1010
import { ColumnDef, SortingState } from "@tanstack/react-table";
11+
import dayjs from "dayjs";
1112
import { ExternalLinkIcon, PlusCircle } from "lucide-react";
1213
import { usePathname } from "next/navigation";
1314
import React, { useState } from "react";
1415
import { useSWRConfig } from "swr";
1516
import SetupKeysIcon from "@/assets/icons/SetupKeysIcon";
1617
import { useLocalStorage } from "@/hooks/useLocalStorage";
1718
import { SetupKey } from "@/interfaces/SetupKey";
19+
import EmptyRow from "@/modules/common-table-rows/EmptyRow";
1820
import ExpirationDateRow from "@/modules/common-table-rows/ExpirationDateRow";
1921
import LastTimeRow from "@/modules/common-table-rows/LastTimeRow";
2022
import SetupKeyActionCell from "@/modules/setup-keys/SetupKeyActionCell";
@@ -94,7 +96,15 @@ export const SetupKeysTableColumns: ColumnDef<SetupKey>[] = [
9496
header: ({ column }) => {
9597
return <DataTableHeader column={column}>Expires</DataTableHeader>;
9698
},
97-
cell: ({ row }) => <ExpirationDateRow date={row.original.expires} />,
99+
cell: ({ row }) => {
100+
let expires = dayjs(row.original.expires);
101+
let isNeverExpiring = expires?.year() == 1 || false;
102+
return !isNeverExpiring ? (
103+
<ExpirationDateRow date={row.original.expires} />
104+
) : (
105+
<EmptyRow className={"px-3"} />
106+
);
107+
},
98108
},
99109

100110
{
@@ -116,7 +126,7 @@ export default function SetupKeysTable({
116126
setupKeys,
117127
isLoading,
118128
headingTarget,
119-
}: Props) {
129+
}: Readonly<Props>) {
120130
const { mutate } = useSWRConfig();
121131
const path = usePathname();
122132

@@ -216,6 +226,20 @@ export default function SetupKeysTable({
216226
{(table) => (
217227
<>
218228
<ButtonGroup disabled={setupKeys?.length == 0}>
229+
<ButtonGroup.Button
230+
onClick={() => {
231+
table.setPageIndex(0);
232+
table.getColumn("valid")?.setFilterValue(undefined);
233+
}}
234+
disabled={setupKeys?.length == 0}
235+
variant={
236+
table.getColumn("valid")?.getFilterValue() == undefined
237+
? "tertiary"
238+
: "secondary"
239+
}
240+
>
241+
All
242+
</ButtonGroup.Button>
219243
<ButtonGroup.Button
220244
onClick={() => {
221245
table.setPageIndex(0);
@@ -233,16 +257,16 @@ export default function SetupKeysTable({
233257
<ButtonGroup.Button
234258
onClick={() => {
235259
table.setPageIndex(0);
236-
table.getColumn("valid")?.setFilterValue("");
260+
table.getColumn("valid")?.setFilterValue(false);
237261
}}
238262
disabled={setupKeys?.length == 0}
239263
variant={
240-
table.getColumn("valid")?.getFilterValue() != true
264+
table.getColumn("valid")?.getFilterValue() == false
241265
? "tertiary"
242266
: "secondary"
243267
}
244268
>
245-
All
269+
Expired
246270
</ButtonGroup.Button>
247271
</ButtonGroup>
248272
<DataTableRowsPerPage

0 commit comments

Comments
 (0)