summaryrefslogtreecommitdiff
path: root/lib/compliance/table/delete-compliance-templates-dialog.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/compliance/table/delete-compliance-templates-dialog.tsx')
-rw-r--r--lib/compliance/table/delete-compliance-templates-dialog.tsx209
1 files changed, 209 insertions, 0 deletions
diff --git a/lib/compliance/table/delete-compliance-templates-dialog.tsx b/lib/compliance/table/delete-compliance-templates-dialog.tsx
new file mode 100644
index 00000000..4cc672c7
--- /dev/null
+++ b/lib/compliance/table/delete-compliance-templates-dialog.tsx
@@ -0,0 +1,209 @@
+"use client"
+
+import * as React from "react"
+import { type Row } from "@tanstack/react-table"
+import { Loader, Trash } from "lucide-react"
+import { toast } from "sonner"
+
+import { useMediaQuery } from "@/hooks/use-media-query"
+import { Button } from "@/components/ui/button"
+import {
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/components/ui/dialog"
+import {
+ Drawer,
+ DrawerClose,
+ DrawerContent,
+ DrawerDescription,
+ DrawerFooter,
+ DrawerHeader,
+ DrawerTitle,
+ DrawerTrigger,
+} from "@/components/ui/drawer"
+
+import { deleteComplianceSurveyTemplate, getTemplatesRelatedDataCount } from "../services"
+import { complianceSurveyTemplates } from "@/db/schema/compliance"
+
+interface DeleteComplianceTemplatesDialogProps
+ extends React.ComponentPropsWithoutRef<typeof Dialog> {
+ templates: Row<typeof complianceSurveyTemplates.$inferSelect>["original"][]
+ showTrigger?: boolean
+ open?: boolean
+ onOpenChange?: (open: boolean) => void
+}
+
+export function DeleteComplianceTemplatesDialog({
+ templates,
+ showTrigger = true,
+ open: controlledOpen,
+ onOpenChange: controlledOnOpenChange,
+}: DeleteComplianceTemplatesDialogProps) {
+ const [isDeletePending, startDeleteTransition] = React.useTransition()
+ const [internalOpen, setInternalOpen] = React.useState(false)
+ const [relatedDataCount, setRelatedDataCount] = React.useState<{
+ totalQuestions: number;
+ totalResponses: number;
+ }>({ totalQuestions: 0, totalResponses: 0 })
+ const isDesktop = useMediaQuery("(min-width: 640px)")
+
+ // controlled/uncontrolled 상태 관리
+ const isControlled = controlledOpen !== undefined
+ const open = isControlled ? controlledOpen : internalOpen
+ const setOpen = isControlled ? controlledOnOpenChange : setInternalOpen
+
+ // 다이얼로그가 열릴 때 연결된 데이터 개수 조회
+ React.useEffect(() => {
+
+ if (open) {
+ const fetchRelatedDataCount = async () => {
+ try {
+ const templateIds = templates.map(template => template.id)
+
+ const data = await getTemplatesRelatedDataCount(templateIds)
+
+ setRelatedDataCount({
+ totalQuestions: data.totalQuestions,
+ totalResponses: data.totalResponses,
+ })
+ } catch (error) {
+ console.error("Error fetching related data count:", error)
+ }
+ }
+ fetchRelatedDataCount()
+ }
+ }, [open, templates])
+
+ function onDelete() {
+ startDeleteTransition(async () => {
+ try {
+
+
+ // 각 템플릿을 순차적으로 삭제
+ for (const template of templates) {
+ try {
+ await deleteComplianceSurveyTemplate(template.id);
+ } catch (error) {
+ toast.error(`템플릿 ${template.name} 삭제 실패: ${error instanceof Error ? error.message : '알 수 없는 오류'}`);
+ return;
+ }
+ }
+
+ if (setOpen) {
+ setOpen(false);
+ }
+ toast.success("템플릿이 성공적으로 삭제되었습니다.");
+
+ // 페이지 새로고침으로 데이터 업데이트
+ window.location.reload();
+ } catch (error) {
+ console.error("Error during deletion:", error);
+ toast.error("템플릿 삭제 중 오류가 발생했습니다.");
+ }
+ });
+ }
+
+ if (isDesktop) {
+ return (
+ <Dialog open={open} onOpenChange={setOpen}>
+ {showTrigger ? (
+ <DialogTrigger asChild>
+ <Button variant="outline" size="sm">
+ <Trash className="mr-2 size-4" aria-hidden="true" />
+ Delete ({templates.length})
+ </Button>
+ </DialogTrigger>
+ ) : null}
+ <DialogContent>
+ <DialogHeader>
+ <DialogTitle>Are you sure you want to delete?</DialogTitle>
+ <DialogDescription>
+ <br />
+ This action cannot be undone.
+ <br />
+ This will permanently delete{" "}
+ <span className="font-medium">{templates.length}</span>
+ template(s) from the server.
+ <div className="mt-2 text-sm text-red-600">
+ <div><br />⚠️ Data that will be deleted together ⚠️</div>
+ <div>• Questions: {relatedDataCount.totalQuestions}</div>
+ <div>• Responses: {relatedDataCount.totalResponses}</div>
+ </div>
+ </DialogDescription>
+ </DialogHeader>
+ <DialogFooter className="gap-2 sm:space-x-0">
+ <DialogClose asChild>
+ <Button variant="outline">Cancel</Button>
+ </DialogClose>
+ <Button
+ aria-label="Delete selected rows"
+ variant="destructive"
+ onClick={onDelete}
+ disabled={isDeletePending}
+ >
+ {isDeletePending && (
+ <Loader
+ className="mr-2 size-4 animate-spin"
+ aria-hidden="true"
+ />
+ )}
+ Delete
+ </Button>
+ </DialogFooter>
+ </DialogContent>
+ </Dialog>
+ )
+ }
+
+ return (
+ <Drawer open={open} onOpenChange={setOpen}>
+ {showTrigger ? (
+ <DrawerTrigger asChild>
+ <Button variant="outline" size="sm">
+ <Trash className="mr-2 size-4" aria-hidden="true" />
+ Delete ({templates.length})
+ </Button>
+ </DrawerTrigger>
+ ) : null}
+ <DrawerContent>
+ <DrawerHeader>
+ <DrawerTitle>Are you sure you want to delete?</DrawerTitle>
+ <DrawerDescription>
+ This action cannot be undone.
+ <br />
+ This will permanently delete{" "}
+ <span className="font-medium">{templates.length}</span>
+ template(s) from the server.
+ <div className="mt-2 text-sm text-red-600">
+ <div>⚠️ Data that will be deleted together ⚠️</div>
+ <div>• Questions: {relatedDataCount.totalQuestions}</div>
+ <div>• Responses: {relatedDataCount.totalResponses}</div>
+ </div>
+ </DrawerDescription>
+ </DrawerHeader>
+ <DrawerFooter className="gap-2 sm:space-x-0">
+ <DrawerClose asChild>
+ <Button variant="outline">Cancel</Button>
+ </DrawerClose>
+ <Button
+ aria-label="Delete selected rows"
+ variant="destructive"
+ onClick={onDelete}
+ disabled={isDeletePending}
+ >
+ {isDeletePending && (
+ <Loader className="mr-2 size-4 animate-spin" aria-hidden="true" />
+ )}
+ Delete
+ </Button>
+ </DrawerFooter>
+ </DrawerContent>
+ </Drawer>
+ )
+}