From a7a75b45b684c8367f85f7675e1f2aadf62644de Mon Sep 17 00:00:00 2001 From: Jyrki Keisala Date: Thu, 24 Oct 2024 09:24:39 +0300 Subject: [PATCH 1/3] feat(ui): Add optional text confirmation to item deletion Deleting items (organizations, products, repositories, secrets etc.) from the server can be considered dangerous, as deletion is irreversible and might have unwanted side effects. Add an optional boolean prop `textConfirmation` to the delete modal. When it is set to `true`, the user is required to write the name of the item which is about to be deleted to an input box, before the Delete button is enabled. Signed-off-by: Jyrki Keisala --- ui/src/components/delete-dialog.tsx | 50 ++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/ui/src/components/delete-dialog.tsx b/ui/src/components/delete-dialog.tsx index 7ef00e311..0e51093ab 100644 --- a/ui/src/components/delete-dialog.tsx +++ b/ui/src/components/delete-dialog.tsx @@ -18,6 +18,7 @@ */ import { Loader2, OctagonAlert, TrashIcon } from 'lucide-react'; +import { useEffect, useState } from 'react'; import { AlertDialog, @@ -30,6 +31,7 @@ import { AlertDialogTrigger, } from '@/components/ui/alert-dialog'; import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; import { Tooltip, TooltipContent, @@ -47,6 +49,7 @@ interface DeleteDialogProps { onDelete: () => void; isPending: boolean; className?: string; + textConfirmation?: boolean; } export const DeleteDialog = ({ @@ -56,7 +59,18 @@ export const DeleteDialog = ({ onDelete, isPending, className, + textConfirmation = false, }: DeleteDialogProps) => { + const [input, setInput] = useState(''); + const isDeleteDisabled = textConfirmation && input !== item.name; + + // Reset the input field whenever the dialog is opened/closed + useEffect(() => { + if (open) { + setInput(''); + } + }, [open]); + return ( @@ -82,13 +96,39 @@ export const DeleteDialog = ({ Delete {item.descriptor} - - Are you sure you want to delete this {item.descriptor}:{' '} - {item.name}? - + {textConfirmation ? ( + +
+
+ Are you sure you want to delete this {item.descriptor}:{' '} + {item.name}? +
+
+ Deleting might have unwanted results and side effects, and the + deletion is irreversible. Please type{' '} + {item.name} below to confirm + deletion. +
+ setInput(e.target.value)} + /> +
+
+ ) : ( + + Are you sure you want to delete this {item.descriptor}:{' '} + {item.name}? + + )} Cancel - + + + Add a new user to the server. Note that the username has to be + unique. + +
+ + + + + { + return { + to: Route.to, + search: { ...search, page: currentPage }, + }; + }} + setPageSizeOptions={(size) => { + return { + to: Route.to, + search: { ...search, page: 1, pageSize: size }, + }; + }} + /> + + + + ); +}; + +export const Route = createFileRoute('/_layout/admin/users/')({ + validateSearch: paginationSchema, + loader: async ({ context }) => { + prefetchUseAdminServiceGetUsers(context.queryClient); + }, + component: Users, + pendingComponent: LoadingIndicator, +});