From 5003141bac33e4d661481853479d9a9089a5f326 Mon Sep 17 00:00:00 2001 From: Maximilian Kaske <56969857+mxkaske@users.noreply.github.com> Date: Fri, 12 Dec 2025 11:51:37 +0100 Subject: [PATCH] feat: delete action resend ux (#1636) --- .../(dashboard)/monitors/[id]/nav-actions.tsx | 3 +- .../status-pages/[id]/nav-actions.tsx | 7 +-- .../incidents/data-table-row-actions.tsx | 3 +- .../maintenances/data-table-row-actions.tsx | 3 +- .../monitors/data-table-action-bar.tsx | 28 +++++++---- .../monitors/data-table-row-actions.tsx | 3 +- .../notifications/data-table-row-actions.tsx | 3 +- .../data-table-row-actions.tsx | 3 +- .../settings/invitations/data-table.tsx | 2 +- .../settings/members/data-table.tsx | 3 +- .../status-pages/data-table-row-actions.tsx | 3 +- .../data-table-row-actions.tsx | 2 +- .../status-reports/data-table-row-actions.tsx | 2 +- .../subscribers/data-table-row-actions.tsx | 2 +- .../components/dropdowns/quick-actions.tsx | 46 +++++++++++++------ .../components/forms/form-alert-dialog.tsx | 26 +++++++---- .../forms/monitor/form-danger-zone.tsx | 6 +-- .../forms/settings/form-api-key.tsx | 3 +- .../forms/status-page/form-danger-zone.tsx | 6 +-- .../src/components/nav/nav-monitors.tsx | 3 +- .../src/components/nav/nav-status-pages.tsx | 3 +- 21 files changed, 91 insertions(+), 69 deletions(-) diff --git a/apps/dashboard/src/app/(dashboard)/monitors/[id]/nav-actions.tsx b/apps/dashboard/src/app/(dashboard)/monitors/[id]/nav-actions.tsx index 1d61aafb..20d2d2a4 100644 --- a/apps/dashboard/src/app/(dashboard)/monitors/[id]/nav-actions.tsx +++ b/apps/dashboard/src/app/(dashboard)/monitors/[id]/nav-actions.tsx @@ -195,8 +195,7 @@ export function NavActions() { { await deleteMonitorMutation.mutateAsync({ id: Number.parseInt(id), diff --git a/apps/dashboard/src/app/(dashboard)/status-pages/[id]/nav-actions.tsx b/apps/dashboard/src/app/(dashboard)/status-pages/[id]/nav-actions.tsx index f8695524..019f05e8 100644 --- a/apps/dashboard/src/app/(dashboard)/status-pages/[id]/nav-actions.tsx +++ b/apps/dashboard/src/app/(dashboard)/status-pages/[id]/nav-actions.tsx @@ -58,7 +58,9 @@ export function NavActions() { {" "} + to confirm

setValue(e.target.value)} /> diff --git a/apps/dashboard/src/components/data-table/monitors/data-table-row-actions.tsx b/apps/dashboard/src/components/data-table/monitors/data-table-row-actions.tsx index 92bb546b..60ee554f 100644 --- a/apps/dashboard/src/components/data-table/monitors/data-table-row-actions.tsx +++ b/apps/dashboard/src/components/data-table/monitors/data-table-row-actions.tsx @@ -42,8 +42,7 @@ export function DataTableRowActions({ row }: DataTableRowActionsProps) { { await deleteMonitorMutation.mutateAsync({ id: row.original.id, diff --git a/apps/dashboard/src/components/data-table/notifications/data-table-row-actions.tsx b/apps/dashboard/src/components/data-table/notifications/data-table-row-actions.tsx index e24d3481..96b69868 100644 --- a/apps/dashboard/src/components/data-table/notifications/data-table-row-actions.tsx +++ b/apps/dashboard/src/components/data-table/notifications/data-table-row-actions.tsx @@ -47,8 +47,7 @@ export function DataTableRowActions(props: DataTableRowActionsProps) { { await deleteNotifierMutation.mutateAsync({ id: props.row.original.id, diff --git a/apps/dashboard/src/components/data-table/private-locations/data-table-row-actions.tsx b/apps/dashboard/src/components/data-table/private-locations/data-table-row-actions.tsx index 865808d4..6dc0a39d 100644 --- a/apps/dashboard/src/components/data-table/private-locations/data-table-row-actions.tsx +++ b/apps/dashboard/src/components/data-table/private-locations/data-table-row-actions.tsx @@ -47,8 +47,7 @@ export function DataTableRowActions(props: DataTableRowActionsProps) { { await deletePrivateLocationMutation.mutateAsync({ id: props.row.original.id, diff --git a/apps/dashboard/src/components/data-table/settings/invitations/data-table.tsx b/apps/dashboard/src/components/data-table/settings/invitations/data-table.tsx index b58ea0bc..46b18363 100644 --- a/apps/dashboard/src/components/data-table/settings/invitations/data-table.tsx +++ b/apps/dashboard/src/components/data-table/settings/invitations/data-table.tsx @@ -70,7 +70,7 @@ export function DataTable() {
deleteInvitationMutation.mutateAsync({ id: item.id }), }} diff --git a/apps/dashboard/src/components/data-table/settings/members/data-table.tsx b/apps/dashboard/src/components/data-table/settings/members/data-table.tsx index 001c0d56..80819849 100644 --- a/apps/dashboard/src/components/data-table/settings/members/data-table.tsx +++ b/apps/dashboard/src/components/data-table/settings/members/data-table.tsx @@ -52,8 +52,7 @@ export function DataTable() {
await deleteMemberMutation.mutateAsync({ diff --git a/apps/dashboard/src/components/data-table/status-pages/data-table-row-actions.tsx b/apps/dashboard/src/components/data-table/status-pages/data-table-row-actions.tsx index 19bff8e0..59363bcb 100644 --- a/apps/dashboard/src/components/data-table/status-pages/data-table-row-actions.tsx +++ b/apps/dashboard/src/components/data-table/status-pages/data-table-row-actions.tsx @@ -40,8 +40,7 @@ export function DataTableRowActions({ row }: DataTableRowActionsProps) { { await deleteStatusPageMutation.mutateAsync({ id: row.original.id, diff --git a/apps/dashboard/src/components/data-table/status-report-updates/data-table-row-actions.tsx b/apps/dashboard/src/components/data-table/status-report-updates/data-table-row-actions.tsx index 462d3801..32a310b2 100644 --- a/apps/dashboard/src/components/data-table/status-report-updates/data-table-row-actions.tsx +++ b/apps/dashboard/src/components/data-table/status-report-updates/data-table-row-actions.tsx @@ -55,7 +55,7 @@ export function DataTableRowActions({ row }: DataTableRowActionsProps) { { await deleteStatusReportUpdateMutation.mutateAsync({ id: row.id, diff --git a/apps/dashboard/src/components/data-table/status-reports/data-table-row-actions.tsx b/apps/dashboard/src/components/data-table/status-reports/data-table-row-actions.tsx index f0e481ae..cd0ac3ff 100644 --- a/apps/dashboard/src/components/data-table/status-reports/data-table-row-actions.tsx +++ b/apps/dashboard/src/components/data-table/status-reports/data-table-row-actions.tsx @@ -99,7 +99,7 @@ export function DataTableRowActions({ row }: DataTableRowActionsProps) { { await deleteStatusReportMutation.mutateAsync({ id: row.original.id, diff --git a/apps/dashboard/src/components/data-table/subscribers/data-table-row-actions.tsx b/apps/dashboard/src/components/data-table/subscribers/data-table-row-actions.tsx index b7ce3ad4..43a17fc3 100644 --- a/apps/dashboard/src/components/data-table/subscribers/data-table-row-actions.tsx +++ b/apps/dashboard/src/components/data-table/subscribers/data-table-row-actions.tsx @@ -30,7 +30,7 @@ export function DataTableRowActions({ row }: DataTableRowActionsProps) { { await deleteAction.mutateAsync({ id: row.original.id, diff --git a/apps/dashboard/src/components/dropdowns/quick-actions.tsx b/apps/dashboard/src/components/dropdowns/quick-actions.tsx index 691e9433..c81da3ee 100644 --- a/apps/dashboard/src/components/dropdowns/quick-actions.tsx +++ b/apps/dashboard/src/components/dropdowns/quick-actions.tsx @@ -3,7 +3,13 @@ import type * as React from "react"; import { useState, useTransition } from "react"; -import { type LucideIcon, MoreHorizontal, Trash2 } from "lucide-react"; +import { + Check, + Copy, + type LucideIcon, + MoreHorizontal, + Trash2, +} from "lucide-react"; import { AlertDialog, @@ -27,6 +33,7 @@ import { DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Input } from "@/components/ui/input"; +import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard"; import type { DropdownMenuContentProps } from "@radix-ui/react-dropdown-menu"; import { isTRPCClientError } from "@trpc/client"; import { toast } from "sonner"; @@ -42,11 +49,10 @@ interface QuickActionsProps extends React.ComponentProps { onClick?: () => Promise | void; }[]; deleteAction?: { - title: string; /** - * If set, an input field will require the user input to validate deletion + * The value that must be typed to confirm deletion. Also used in the dialog title. */ - confirmationValue?: string; + confirmationValue: string; submitAction?: () => Promise; }; } @@ -63,6 +69,7 @@ export function QuickActions({ const [value, setValue] = useState(""); const [isPending, startTransition] = useTransition(); const [open, setOpen] = useState(false); + const { copy, isCopied } = useCopyToClipboard(); const handleDelete = async () => { startTransition(async () => { @@ -147,25 +154,36 @@ export function QuickActions({ > - Are you sure about deleting `{deleteAction?.title}`? + Are you sure about deleting `{deleteAction?.confirmationValue}`? This action cannot be undone. This will permanently remove the entry from the database. - {deleteAction?.confirmationValue ? ( -
-

- Please write ' - - {deleteAction?.confirmationValue} - - ' to confirm + {deleteAction?.confirmationValue && ( + +

+ Type{" "} + {" "} + to confirm

setValue(e.target.value)} />
- ) : null} + )} e.stopPropagation()}> Cancel diff --git a/apps/dashboard/src/components/forms/form-alert-dialog.tsx b/apps/dashboard/src/components/forms/form-alert-dialog.tsx index 055f5110..58eb4e7d 100644 --- a/apps/dashboard/src/components/forms/form-alert-dialog.tsx +++ b/apps/dashboard/src/components/forms/form-alert-dialog.tsx @@ -13,25 +13,26 @@ import { } from "@/components/ui/alert-dialog"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; +import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard"; import { isTRPCClientError } from "@trpc/client"; +import { Check, Copy } from "lucide-react"; import { useState, useTransition } from "react"; import { toast } from "sonner"; interface FormAlertDialogProps { - title: string; confirmationValue: string; submitAction: () => Promise; children?: React.ReactNode; } export function FormAlertDialog({ - title, confirmationValue, submitAction, children, }: FormAlertDialogProps) { const [value, setValue] = useState(""); const [isPending, startTransition] = useTransition(); + const { copy, isCopied } = useCopyToClipboard(); const [open, setOpen] = useState(false); const handleDelete = async () => { @@ -68,17 +69,26 @@ export function FormAlertDialog({ - Are you sure about delete `{title}`? + Are you sure about delete `{confirmationValue}`? This action cannot be undone. This will permanently delete the item. -
-

- Please write ' - {confirmationValue} - ' to confirm + +

+ Type{" "} + {" "} + to confirm

setValue(e.target.value)} />
diff --git a/apps/dashboard/src/components/forms/monitor/form-danger-zone.tsx b/apps/dashboard/src/components/forms/monitor/form-danger-zone.tsx index 24dd8a5f..7708c583 100644 --- a/apps/dashboard/src/components/forms/monitor/form-danger-zone.tsx +++ b/apps/dashboard/src/components/forms/monitor/form-danger-zone.tsx @@ -23,11 +23,7 @@ export function FormDangerZone({ This action cannot be undone. - + ); diff --git a/apps/dashboard/src/components/forms/settings/form-api-key.tsx b/apps/dashboard/src/components/forms/settings/form-api-key.tsx index 80c3d88c..4c251dda 100644 --- a/apps/dashboard/src/components/forms/settings/form-api-key.tsx +++ b/apps/dashboard/src/components/forms/settings/form-api-key.tsx @@ -128,8 +128,7 @@ export function FormApiKey() { ) : ( { await revokeApiKeyMutation.mutateAsync({ keyId: apiKey.id, diff --git a/apps/dashboard/src/components/forms/status-page/form-danger-zone.tsx b/apps/dashboard/src/components/forms/status-page/form-danger-zone.tsx index 44032ca6..7708c583 100644 --- a/apps/dashboard/src/components/forms/status-page/form-danger-zone.tsx +++ b/apps/dashboard/src/components/forms/status-page/form-danger-zone.tsx @@ -23,11 +23,7 @@ export function FormDangerZone({ This action cannot be undone. - + ); diff --git a/apps/dashboard/src/components/nav/nav-monitors.tsx b/apps/dashboard/src/components/nav/nav-monitors.tsx index 6fb7c976..6ddc4a99 100644 --- a/apps/dashboard/src/components/nav/nav-monitors.tsx +++ b/apps/dashboard/src/components/nav/nav-monitors.tsx @@ -188,8 +188,7 @@ export function NavMonitors() { { await deleteMonitorMutation.mutateAsync({ id: item.id, diff --git a/apps/dashboard/src/components/nav/nav-status-pages.tsx b/apps/dashboard/src/components/nav/nav-status-pages.tsx index 1303d264..efdff10f 100644 --- a/apps/dashboard/src/components/nav/nav-status-pages.tsx +++ b/apps/dashboard/src/components/nav/nav-status-pages.tsx @@ -160,8 +160,7 @@ export function NavStatusPages() { { await deleteStatusPage.mutateAsync({ id: item.id }); if (pathname.includes(`/status-pages/${item.id}`)) { -- 2.51.2