"use client"; import { LoaderCircleIcon, Trash2Icon } from "lucide-react"; import { isValidElement, ReactNode, useState } from "react"; import { useIsInActionBar } from "../../contexts/ActionBarContext"; import { useI18nRouter, useI18nTranslations } from "../../i18n"; import { AlertDialog, AlertDialogContent, AlertDialogDescription, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, Button, } from "../../shadcnui"; import { errorToast } from "../errors"; type CommonDeleterProps = { type?: string; /** Override the type-derived dialog title. */ title?: string; /** Override the type-derived dialog subtitle. */ subtitle?: string; /** Override the type-derived dialog body; rendered as-is. */ description?: ReactNode; deleteFunction: () => Promise; redirectTo?: string; forceShow?: boolean; testId?: string; /** Custom trigger element replacing the default trash button. */ trigger?: ReactNode; /** * Force the default trigger to show / hide its text label. Left unset it * follows `useIsInActionBar()`: labelled in a page action bar, glyph-only in * a table row or a sheet header, where there is no room for the word. */ showLabel?: boolean; onSuccess?: () => void | Promise; /** Fired after a successful delete, in addition to redirect/onSuccess. */ propagateChanges?: () => void; }; export function CommonDeleter({ deleteFunction, redirectTo, type, title, subtitle, description, forceShow, testId, trigger, showLabel, onSuccess, propagateChanges, }: CommonDeleterProps) { const t = useI18nTranslations(); // Called unconditionally: `showLabel ?? useIsInActionBar()` would skip the // hook whenever the prop is set, breaking the rules of hooks. const inActionBar = useIsInActionBar(); const isLabelled = showLabel ?? inActionBar; const router = useI18nRouter(); const [open, setOpen] = useState(forceShow || false); const [isDeleting, setIsDeleting] = useState(false); const handleDelete = async () => { setIsDeleting(true); try { await deleteFunction(); setOpen(false); if (onSuccess) await onSuccess(); else if (redirectTo) router.push(redirectTo); if (propagateChanges) propagateChanges(); } catch (error) { errorToast({ title: t(`common.errors.delete`), error: error }); } setIsDeleting(false); }; return ( {forceShow ? null : isValidElement(trigger) ? ( // A caller-supplied element trigger must REPLACE the trigger's own // )} )} {title ?? t(`common.delete.title`, { type: t(`entities.${type}`, { count: 1 }) })} {subtitle ?? t(`common.delete.subtitle`, { type: t(`entities.${type}`, { count: 1 }) })} {description !== undefined ? ( description ) : (
{t(`common.delete.description`, { type: t(`entities.${type}`, { count: 1 }) })}
)}
); }