Skip to content

Commit

Permalink
Add setup-key improvements (#420)
Browse files Browse the repository at this point in the history
- Add support to key deletion
- Add custom and unlimited expiration
  • Loading branch information
mlsmaycon authored Nov 1, 2024
1 parent f8281c8 commit cd3e75b
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 40 deletions.
8 changes: 8 additions & 0 deletions src/modules/activity/ActivityDescription.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ export default function ActivityDescription({ event }: Props) {
</div>
);

if (event.activity_code == "setupkey.delete")
return (
<div className={"inline"}>
Setup-Key <Value> {m.name}</Value> with key <Value>{m.key}</Value> was
deleted
</div>
);

if (event.activity_code == "setupkey.add")
return (
<div className={"inline"}>
Expand Down
10 changes: 8 additions & 2 deletions src/modules/common-table-rows/EmptyRow.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
export default function EmptyRow() {
return <div className={"text-nb-gray-600"}>-</div>;
import { cn } from "@utils/helpers";

type Props = {
className?: string;
};

export default function EmptyRow({ className }: Readonly<Props>) {
return <div className={cn("text-nb-gray-600", className)}>-</div>;
}
45 changes: 34 additions & 11 deletions src/modules/setup-keys/SetupKeyActionCell.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Button from "@components/Button";
import { notify } from "@components/Notification";
import { useApiCall } from "@utils/api";
import { Trash2 } from "lucide-react";
import { Trash2, Undo2Icon } from "lucide-react";
import * as React from "react";
import { useSWRConfig } from "swr";
import { useDialog } from "@/contexts/DialogProvider";
Expand All @@ -10,16 +10,26 @@ import { SetupKey } from "@/interfaces/SetupKey";
type Props = {
setupKey: SetupKey;
};
export default function SetupKeyActionCell({ setupKey }: Props) {
export default function SetupKeyActionCell({ setupKey }: Readonly<Props>) {
const { confirm } = useDialog();
const deleteRequest = useApiCall<SetupKey>("/setup-keys/" + setupKey.id);
const request = useApiCall<SetupKey>("/setup-keys/" + setupKey.id);
const { mutate } = useSWRConfig();

const handleRevoke = async () => {
const choice = await confirm({
title: `Revoke '${setupKey?.name || "Setup Key"}'?`,
description:
"Are you sure you want to revoke the setup key? This action cannot be undone.",
confirmText: "Revoke",
cancelText: "Cancel",
type: "danger",
});
if (!choice) return;

notify({
title: setupKey?.name || "Setup Key",
description: "Setup key was successfully revoked",
promise: deleteRequest
promise: request
.put({
name: setupKey?.name || "Setup Key",
type: setupKey.type,
Expand All @@ -37,30 +47,43 @@ export default function SetupKeyActionCell({ setupKey }: Props) {
});
};

const handleConfirm = async () => {
const handleDelete = async () => {
const choice = await confirm({
title: `Revoke '${setupKey?.name || "Setup Key"}'?`,
title: `Delete '${setupKey?.name || "Setup Key"}'?`,
description:
"Are you sure you want to revoke the setup key? This action cannot be undone.",
confirmText: "Revoke",
"Are you sure you want to delete the setup key? This action cannot be undone.",
confirmText: "Delete",
cancelText: "Cancel",
type: "danger",
});
if (!choice) return;
handleRevoke().then();

notify({
title: setupKey?.name || "Setup Key",
description: "Setup key was successfully deleted",
promise: request.del().then(() => {
mutate("/setup-keys");
mutate("/groups");
}),
loadingMessage: "Deleting the setup key...",
});
};

return (
<div className={"flex justify-end pr-4"}>
<Button
variant={"danger-outline"}
size={"sm"}
onClick={handleConfirm}
onClick={handleRevoke}
disabled={setupKey.revoked || !setupKey.valid}
>
<Trash2 size={16} />
<Undo2Icon size={16} />
Revoke
</Button>
<Button variant={"danger-outline"} size={"sm"} onClick={handleDelete}>
<Trash2 size={16} />
Delete
</Button>
</div>
);
}
2 changes: 1 addition & 1 deletion src/modules/setup-keys/SetupKeyGroupsCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import GroupsRow from "@/modules/common-table-rows/GroupsRow";
type Props = {
setupKey: SetupKey;
};
export default function SetupKeyGroupsCell({ setupKey }: Props) {
export default function SetupKeyGroupsCell({ setupKey }: Readonly<Props>) {
const [modal, setModal] = useState(false);
const request = useApiCall<SetupKey>("/setup-keys/" + setupKey.id);
const { mutate } = useSWRConfig();
Expand Down
2 changes: 1 addition & 1 deletion src/modules/setup-keys/SetupKeyKeyCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ type Props = {
text: string;
};

export default function SetupKeyKeyCell({ text }: Props) {
export default function SetupKeyKeyCell({ text }: Readonly<Props>) {
return (
<div className={"flex"}>
<Badge variant={"gray"} className={"text-xs font-mono"}>
Expand Down
33 changes: 15 additions & 18 deletions src/modules/setup-keys/SetupKeyModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ type Props = {
setOpen: (open: boolean) => void;
};
const copyMessage = "Setup-Key was copied to your clipboard!";
export default function SetupKeyModal({ children, open, setOpen }: Props) {
export default function SetupKeyModal({
children,
open,
setOpen,
}: Readonly<Props>) {
const [successModal, setSuccessModal] = useState(false);
const [setupKey, setSetupKey] = useState<SetupKey>();
const [, copy] = useCopyToClipboard(setupKey?.key);
Expand Down Expand Up @@ -131,7 +135,7 @@ type ModalProps = {
onSuccess?: (setupKey: SetupKey) => void;
};

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

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

const expiresInError = useMemo(() => {
const expires = parseInt(expiresIn);
if (expires < 1 || expires > 365) {
return "Days should be between 1 and 365";
}
return "";
}, [expiresIn]);

const isDisabled = useMemo(() => {
const trimmedName = trim(name);
return trimmedName.length === 0 || expiresInError.length > 0;
}, [name, expiresInError]);
return trimmedName.length === 0;
}, [name]);

const submit = () => {
if (!selectedGroups) return;
Expand All @@ -174,7 +170,7 @@ export function SetupKeyModalContent({ onSuccess }: ModalProps) {
.post({
name,
type: reusable ? "reusable" : "one-off",
expires_in: parseInt(expiresIn ? expiresIn : "7") * 24 * 60 * 60, // Days to seconds, defaults to 7 days
expires_in: parseInt(expiresIn || "0") * 24 * 60 * 60, // Days to seconds, defaults to 7 days
revoked: false,
auto_groups: groups.map((group) => group.id),
usage_limit: reusable ? parseInt(usageLimit) : 1,
Expand Down Expand Up @@ -253,15 +249,16 @@ export function SetupKeyModalContent({ onSuccess }: ModalProps) {
<div className={"flex justify-between"}>
<div>
<Label>Expires in</Label>
<HelpText>Should be between 1 and 365 days.</HelpText>
<HelpText>
Days until the key expires. <br />
Leave empty for no expiration.
</HelpText>
</div>
<Input
maxWidthClass={"max-w-[200px]"}
placeholder={"7"}
maxWidthClass={"max-w-[202px]"}
placeholder={"Unlimited"}
min={1}
max={365}
value={expiresIn}
error={expiresInError}
errorTooltip={true}
type={"number"}
data-cy={"setup-key-expire-in-days"}
Expand Down
6 changes: 5 additions & 1 deletion src/modules/setup-keys/SetupKeyNameCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ type Props = {
valid: boolean;
secret?: string;
};
export default function SetupKeyNameCell({ name, valid, secret }: Props) {
export default function SetupKeyNameCell({
name,
valid,
secret,
}: Readonly<Props>) {
return (
<ActiveInactiveRow
active={valid || false}
Expand Down
2 changes: 1 addition & 1 deletion src/modules/setup-keys/SetupKeyTypeCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Repeat1 } from "lucide-react";
type Props = {
reusable: boolean;
};
export default function SetupKeyTypeCell({ reusable }: Props) {
export default function SetupKeyTypeCell({ reusable }: Readonly<Props>) {
return (
<div className={"flex"}>
<Badge className={"text-xs"} variant={"gray"}>
Expand Down
34 changes: 29 additions & 5 deletions src/modules/setup-keys/SetupKeysTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ import DataTableRefreshButton from "@components/table/DataTableRefreshButton";
import { DataTableRowsPerPage } from "@components/table/DataTableRowsPerPage";
import GetStartedTest from "@components/ui/GetStartedTest";
import { ColumnDef, SortingState } from "@tanstack/react-table";
import dayjs from "dayjs";
import { ExternalLinkIcon, PlusCircle } from "lucide-react";
import { usePathname } from "next/navigation";
import React, { useState } from "react";
import { useSWRConfig } from "swr";
import SetupKeysIcon from "@/assets/icons/SetupKeysIcon";
import { useLocalStorage } from "@/hooks/useLocalStorage";
import { SetupKey } from "@/interfaces/SetupKey";
import EmptyRow from "@/modules/common-table-rows/EmptyRow";
import ExpirationDateRow from "@/modules/common-table-rows/ExpirationDateRow";
import LastTimeRow from "@/modules/common-table-rows/LastTimeRow";
import SetupKeyActionCell from "@/modules/setup-keys/SetupKeyActionCell";
Expand Down Expand Up @@ -94,7 +96,15 @@ export const SetupKeysTableColumns: ColumnDef<SetupKey>[] = [
header: ({ column }) => {
return <DataTableHeader column={column}>Expires</DataTableHeader>;
},
cell: ({ row }) => <ExpirationDateRow date={row.original.expires} />,
cell: ({ row }) => {
let expires = dayjs(row.original.expires);
let isNeverExpiring = expires?.year() == 1 || false;
return !isNeverExpiring ? (
<ExpirationDateRow date={row.original.expires} />
) : (
<EmptyRow className={"px-3"} />
);
},
},

{
Expand All @@ -116,7 +126,7 @@ export default function SetupKeysTable({
setupKeys,
isLoading,
headingTarget,
}: Props) {
}: Readonly<Props>) {
const { mutate } = useSWRConfig();
const path = usePathname();

Expand Down Expand Up @@ -216,6 +226,20 @@ export default function SetupKeysTable({
{(table) => (
<>
<ButtonGroup disabled={setupKeys?.length == 0}>
<ButtonGroup.Button
onClick={() => {
table.setPageIndex(0);
table.getColumn("valid")?.setFilterValue(undefined);
}}
disabled={setupKeys?.length == 0}
variant={
table.getColumn("valid")?.getFilterValue() == undefined
? "tertiary"
: "secondary"
}
>
All
</ButtonGroup.Button>
<ButtonGroup.Button
onClick={() => {
table.setPageIndex(0);
Expand All @@ -233,16 +257,16 @@ export default function SetupKeysTable({
<ButtonGroup.Button
onClick={() => {
table.setPageIndex(0);
table.getColumn("valid")?.setFilterValue("");
table.getColumn("valid")?.setFilterValue(false);
}}
disabled={setupKeys?.length == 0}
variant={
table.getColumn("valid")?.getFilterValue() != true
table.getColumn("valid")?.getFilterValue() == false
? "tertiary"
: "secondary"
}
>
All
Expired
</ButtonGroup.Button>
</ButtonGroup>
<DataTableRowsPerPage
Expand Down

0 comments on commit cd3e75b

Please sign in to comment.