From 8994575437c6b965652b9def3065d5e01c688913 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Sat, 18 Apr 2026 17:00:43 +0100 Subject: [PATCH] feat(base): confirm before bulk deleting selected rows --- .../base/hooks/use-delete-selected-rows.ts | 35 ------------ .../base/hooks/use-delete-selected-rows.tsx | 55 +++++++++++++++++++ 2 files changed, 55 insertions(+), 35 deletions(-) delete mode 100644 apps/client/src/features/base/hooks/use-delete-selected-rows.ts create mode 100644 apps/client/src/features/base/hooks/use-delete-selected-rows.tsx diff --git a/apps/client/src/features/base/hooks/use-delete-selected-rows.ts b/apps/client/src/features/base/hooks/use-delete-selected-rows.ts deleted file mode 100644 index f52d716b..00000000 --- a/apps/client/src/features/base/hooks/use-delete-selected-rows.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { useCallback } from "react"; -import { notifications } from "@mantine/notifications"; -import { useTranslation } from "react-i18next"; -import { useRowSelection } from "@/features/base/hooks/use-row-selection"; -import { useDeleteRowsMutation } from "@/features/base/queries/base-row-query"; - -const BATCH_SIZE = 500; - -export function useDeleteSelectedRows(baseId: string) { - const { t } = useTranslation(); - const { selectedIds, clear } = useRowSelection(); - const mutation = useDeleteRowsMutation(); - - const deleteSelected = useCallback(async () => { - const ids = Array.from(selectedIds); - if (ids.length === 0) return; - const chunks: string[][] = []; - for (let i = 0; i < ids.length; i += BATCH_SIZE) { - chunks.push(ids.slice(i, i + BATCH_SIZE)); - } - try { - for (const chunk of chunks) { - await mutation.mutateAsync({ baseId, rowIds: chunk }); - } - notifications.show({ - message: t("{{count}} rows deleted", { count: ids.length }), - }); - clear(); - } catch { - // mutation onError already shows notification - } - }, [baseId, selectedIds, mutation, clear, t]); - - return { deleteSelected, isPending: mutation.isPending }; -} diff --git a/apps/client/src/features/base/hooks/use-delete-selected-rows.tsx b/apps/client/src/features/base/hooks/use-delete-selected-rows.tsx new file mode 100644 index 00000000..a4c451da --- /dev/null +++ b/apps/client/src/features/base/hooks/use-delete-selected-rows.tsx @@ -0,0 +1,55 @@ +import { useCallback } from "react"; +import { notifications } from "@mantine/notifications"; +import { modals } from "@mantine/modals"; +import { Text } from "@mantine/core"; +import { useTranslation } from "react-i18next"; +import { useRowSelection } from "@/features/base/hooks/use-row-selection"; +import { useDeleteRowsMutation } from "@/features/base/queries/base-row-query"; + +const BATCH_SIZE = 500; + +export function useDeleteSelectedRows(baseId: string) { + const { t } = useTranslation(); + const { selectedIds, clear } = useRowSelection(); + const mutation = useDeleteRowsMutation(); + + const runDelete = useCallback( + async (ids: string[]) => { + const chunks: string[][] = []; + for (let i = 0; i < ids.length; i += BATCH_SIZE) { + chunks.push(ids.slice(i, i + BATCH_SIZE)); + } + try { + for (const chunk of chunks) { + await mutation.mutateAsync({ baseId, rowIds: chunk }); + } + notifications.show({ + message: t("{{count}} rows deleted", { count: ids.length }), + }); + clear(); + } catch { + // mutation onError already shows notification + } + }, + [baseId, mutation, clear, t], + ); + + const deleteSelected = useCallback(() => { + const ids = Array.from(selectedIds); + if (ids.length === 0) return; + modals.openConfirmModal({ + title: t("Delete {{count}} rows?", { count: ids.length }), + centered: true, + children: ( + + {t("This action cannot be undone.")} + + ), + labels: { confirm: t("Delete"), cancel: t("Cancel") }, + confirmProps: { color: "red" }, + onConfirm: () => void runDelete(ids), + }); + }, [selectedIds, runDelete, t]); + + return { deleteSelected, isPending: mutation.isPending }; +}