summaryrefslogtreecommitdiff
path: root/lib/qna/table/delete-qna-dialog.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/qna/table/delete-qna-dialog.tsx')
-rw-r--r--lib/qna/table/delete-qna-dialog.tsx250
1 files changed, 250 insertions, 0 deletions
diff --git a/lib/qna/table/delete-qna-dialog.tsx b/lib/qna/table/delete-qna-dialog.tsx
new file mode 100644
index 00000000..55dcd366
--- /dev/null
+++ b/lib/qna/table/delete-qna-dialog.tsx
@@ -0,0 +1,250 @@
+"use client"
+
+import * as React from "react"
+import { toast } from "sonner"
+import { Trash2 } from "lucide-react"
+
+import { Button } from "@/components/ui/button"
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/components/ui/dialog"
+import {
+ Drawer,
+ DrawerContent,
+ DrawerDescription,
+ DrawerFooter,
+ DrawerHeader,
+ DrawerTitle,
+ DrawerTrigger,
+} from "@/components/ui/drawer"
+import { Badge } from "@/components/ui/badge"
+import { ScrollArea } from "@/components/ui/scroll-area"
+
+import { QnaViewSelect } from "@/db/schema"
+import { useMediaQuery } from "@/hooks/use-media-query"
+import { deleteQna } from "../service"
+
+interface DeleteQnaDialogProps {
+ open: boolean
+ onOpenChange: (open: boolean) => void
+ qnas: QnaViewSelect[]
+ showTrigger?: boolean
+ onSuccess?: () => void
+}
+
+export function DeleteQnaDialog({
+ open,
+ onOpenChange,
+ qnas,
+ showTrigger = true,
+ onSuccess
+}: DeleteQnaDialogProps) {
+ const [isDeletePending, startDeleteTransition] = React.useTransition()
+ const isDesktop = useMediaQuery("(min-width: 640px)")
+
+ const qnaCount = qnas.length
+ const isMultiple = qnaCount > 1
+
+ async function handleDelete() {
+ startDeleteTransition(async () => {
+ try {
+ const promises = qnas.map(qna => deleteQna(qna.id))
+ const results = await Promise.all(promises)
+
+ const successCount = results.filter(result => result.success).length
+ const failCount = results.length - successCount
+
+ if (successCount > 0) {
+ toast.success(
+ isMultiple
+ ? `${successCount}개의 질문이 삭제되었습니다.`
+ : "질문이 삭제되었습니다."
+ )
+ onSuccess?.()
+ }
+
+ if (failCount > 0) {
+ toast.error(
+ isMultiple
+ ? `${failCount}개의 질문 삭제에 실패했습니다.`
+ : "질문 삭제에 실패했습니다."
+ )
+ }
+
+ if (successCount > 0) {
+ onOpenChange(false)
+ }
+ } catch (error) {
+ toast.error("삭제 중 오류가 발생했습니다.")
+ console.error("질문 삭제 오류:", error)
+ }
+ })
+ }
+
+ const title = isMultiple ? `${qnaCount}개 질문 삭제` : "질문 삭제"
+ const description = isMultiple
+ ? "선택한 질문들을 정말로 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다."
+ : "이 질문을 정말로 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다."
+
+ if (isDesktop) {
+ return (
+ <Dialog open={open} onOpenChange={onOpenChange}>
+ {showTrigger && (
+ <DialogTrigger asChild>
+ <Button variant="outline" size="sm">
+ <Trash2 className="mr-2 h-4 w-4" />
+ 삭제
+ </Button>
+ </DialogTrigger>
+ )}
+ <DialogContent className="max-w-2xl">
+ <DialogHeader>
+ <DialogTitle>{title}</DialogTitle>
+ <DialogDescription>{description}</DialogDescription>
+ </DialogHeader>
+
+ {/* 삭제할 질문 목록 */}
+ <div className="max-h-[300px]">
+ <div className="text-sm font-medium mb-2">삭제 대상:</div>
+ <ScrollArea className="max-h-[250px] border rounded-md p-3">
+ <div className="space-y-3">
+ {qnas.map((qna, index) => (
+ <div key={qna.id} className="flex items-start gap-3 p-3 border rounded-md bg-muted/50">
+ <div className="font-mono text-xs text-muted-foreground mt-1">
+ {index + 1}.
+ </div>
+ <div className="flex-1 min-w-0">
+ <div className="font-medium text-sm line-clamp-2 mb-1">
+ {qna.title}
+ </div>
+ <div className="flex items-center gap-2 text-xs text-muted-foreground">
+ <span>{qna.authorName}</span>
+ <span>•</span>
+ <span>{qna.companyName || "미지정"}</span>
+ <span>•</span>
+ <span>답변 {qna.totalAnswers}개</span>
+ </div>
+ <div className="flex items-center gap-1 mt-2">
+ {qna.hasAnswers && (
+ <Badge variant="secondary" className="text-xs">
+ 답변있음
+ </Badge>
+ )}
+ {qna.isPopular && (
+ <Badge variant="default" className="text-xs">
+ 인기질문
+ </Badge>
+ )}
+ </div>
+ </div>
+ </div>
+ ))}
+ </div>
+ </ScrollArea>
+ </div>
+
+ {/* 경고 메시지 */}
+ <div className="rounded-md border border-destructive/20 bg-destructive/5 p-3">
+ <div className="text-sm text-destructive">
+ <strong>주의:</strong> 질문을 삭제하면 해당 질문의 모든 답변과 댓글도 함께 삭제됩니다.
+ </div>
+ </div>
+
+ <DialogFooter>
+ <Button
+ type="button"
+ variant="outline"
+ onClick={() => onOpenChange(false)}
+ disabled={isDeletePending}
+ >
+ 취소
+ </Button>
+ <Button
+ type="button"
+ variant="destructive"
+ onClick={handleDelete}
+ disabled={isDeletePending}
+ >
+ {isDeletePending ? "삭제 중..." : "삭제"}
+ </Button>
+ </DialogFooter>
+ </DialogContent>
+ </Dialog>
+ )
+ }
+
+ return (
+ <Drawer open={open} onOpenChange={onOpenChange}>
+ {showTrigger && (
+ <DrawerTrigger asChild>
+ <Button variant="outline" size="sm">
+ <Trash2 className="mr-2 h-4 w-4" />
+ 삭제
+ </Button>
+ </DrawerTrigger>
+ )}
+ <DrawerContent>
+ <DrawerHeader>
+ <DrawerTitle>{title}</DrawerTitle>
+ <DrawerDescription>{description}</DrawerDescription>
+ </DrawerHeader>
+
+ <div className="px-4 pb-4">
+ {/* 삭제할 질문 목록 */}
+ <div className="mb-4">
+ <div className="text-sm font-medium mb-2">삭제 대상:</div>
+ <div className="max-h-[200px] space-y-2 overflow-y-auto">
+ {qnas.map((qna, index) => (
+ <div key={qna.id} className="flex items-start gap-2 p-2 border rounded-md bg-muted/50">
+ <div className="font-mono text-xs text-muted-foreground mt-1">
+ {index + 1}.
+ </div>
+ <div className="flex-1 min-w-0">
+ <div className="font-medium text-sm line-clamp-1 mb-1">
+ {qna.title}
+ </div>
+ <div className="text-xs text-muted-foreground">
+ {qna.authorName} • 답변 {qna.totalAnswers}개
+ </div>
+ </div>
+ </div>
+ ))}
+ </div>
+ </div>
+
+ {/* 경고 메시지 */}
+ <div className="rounded-md border border-destructive/20 bg-destructive/5 p-3 mb-4">
+ <div className="text-sm text-destructive">
+ <strong>주의:</strong> 삭제된 질문과 관련 데이터는 복구할 수 없습니다.
+ </div>
+ </div>
+ </div>
+
+ <DrawerFooter className="gap-2">
+ <Button
+ type="button"
+ variant="destructive"
+ onClick={handleDelete}
+ disabled={isDeletePending}
+ >
+ {isDeletePending ? "삭제 중..." : "삭제"}
+ </Button>
+ <Button
+ type="button"
+ variant="outline"
+ onClick={() => onOpenChange(false)}
+ disabled={isDeletePending}
+ >
+ 취소
+ </Button>
+ </DrawerFooter>
+ </DrawerContent>
+ </Drawer>
+ )
+} \ No newline at end of file