diff options
Diffstat (limited to 'lib/evaluation-target-list/table/delete-targets-dialog.tsx')
| -rw-r--r-- | lib/evaluation-target-list/table/delete-targets-dialog.tsx | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/lib/evaluation-target-list/table/delete-targets-dialog.tsx b/lib/evaluation-target-list/table/delete-targets-dialog.tsx new file mode 100644 index 00000000..5414d281 --- /dev/null +++ b/lib/evaluation-target-list/table/delete-targets-dialog.tsx @@ -0,0 +1,181 @@ +"use client" + +import * as React from "react" +import { Trash2, AlertTriangle } from "lucide-react" +import { toast } from "sonner" + +import { Button } from "@/components/ui/button" +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog" +import { Badge } from "@/components/ui/badge" +import { ScrollArea } from "@/components/ui/scroll-area" +import { EvaluationTargetWithDepartments } from "@/db/schema" +import { deleteEvaluationTargets } from "../service" +interface DeleteTargetsDialogProps { + open: boolean + onOpenChange: (open: boolean) => void + targets: EvaluationTargetWithDepartments[] + onSuccess?: () => void +} + +export function DeleteTargetsDialog({ + open, + onOpenChange, + targets, + onSuccess +}: DeleteTargetsDialogProps) { + const [isLoading, setIsLoading] = React.useState(false) + + // PENDING 상태인 타겟들만 필터링 (추가 안전장치) + const pendingTargets = React.useMemo(() => { + return targets.filter(target => target.status === "PENDING") + }, [targets]) + + console.log(pendingTargets,"pendingTargets") + + const handleDelete = async () => { + if (pendingTargets.length === 0) { + toast.error("삭제할 수 있는 평가 대상이 없습니다.") + return + } + + setIsLoading(true) + try { + const targetIds = pendingTargets.map(target => target.id) + const result = await deleteEvaluationTargets(targetIds) + + if (result.success) { + toast.success(result.message || "평가 대상이 성공적으로 삭제되었습니다.", { + description: `${result.deletedCount || pendingTargets.length}개의 항목이 삭제되었습니다.` + }) + onSuccess?.() + onOpenChange(false) + } else { + toast.error(result.error || "삭제 중 오류가 발생했습니다.") + } + } catch (error) { + console.error('Error deleting targets:', error) + toast.error("삭제 중 오류가 발생했습니다.") + } finally { + setIsLoading(false) + } + } + + const handleCancel = () => { + if (!isLoading) { + onOpenChange(false) + } + } + + return ( + <Dialog open={open} onOpenChange={onOpenChange}> + <DialogContent className="max-w-2xl"> + <DialogHeader> + <DialogTitle className="flex items-center gap-2"> + <Trash2 className="size-5 text-destructive" /> + 평가 대상 삭제 + </DialogTitle> + <DialogDescription> + 선택한 평가 대상을 영구적으로 삭제합니다. 이 작업은 되돌릴 수 없습니다. + </DialogDescription> + </DialogHeader> + + {pendingTargets.length > 0 ? ( + <div className="space-y-4"> + {/* 경고 메시지 */} + <div className="flex items-start gap-3 p-4 bg-destructive/10 rounded-lg border border-destructive/20"> + <AlertTriangle className="size-5 text-destructive mt-0.5 flex-shrink-0" /> + <div className="space-y-1"> + <p className="font-medium text-destructive"> + 주의: 삭제된 데이터는 복구할 수 없습니다 + </p> + <p className="text-sm text-muted-foreground"> + PENDING 상태의 평가 대상만 삭제할 수 있습니다. + 확정(CONFIRMED)되거나 제외(EXCLUDED)된 대상은 삭제할 수 없습니다. + </p> + </div> + </div> + + {/* 삭제 대상 목록 */} + <div className="space-y-2"> + <div className="flex items-center justify-between"> + <h4 className="font-medium">삭제될 평가 대상 ({pendingTargets.length}개)</h4> + <Badge variant="destructive" className="gap-1"> + <Trash2 className="size-3" /> + 삭제 예정 + </Badge> + </div> + + <ScrollArea className="h-40 w-full border rounded-md"> + <div className="p-4 space-y-2"> + {pendingTargets.map((target) => ( + <div + key={target.id} + className="flex items-center justify-between p-2 bg-muted/50 rounded text-sm" + > + <div className="space-y-1"> + <div className="font-medium"> + {target.vendorName || '알 수 없는 업체'} + </div> + <div className="text-xs text-muted-foreground"> + • {target.evaluationYear}년 + </div> + </div> + <div className="flex items-center gap-2"> + <Badge variant="secondary" className="text-xs"> + {target.status} + </Badge> + </div> + </div> + ))} + </div> + </ScrollArea> + </div> + </div> + ) : ( + <div className="flex items-center justify-center py-8 text-muted-foreground"> + <div className="text-center space-y-2"> + <Trash2 className="size-8 mx-auto opacity-50" /> + <p>삭제할 수 있는 평가 대상이 없습니다.</p> + <p className="text-xs">PENDING 상태의 대상만 삭제할 수 있습니다.</p> + </div> + </div> + )} + + <DialogFooter> + <Button + variant="outline" + onClick={handleCancel} + disabled={isLoading} + > + 취소 + </Button> + <Button + variant="destructive" + onClick={handleDelete} + disabled={isLoading || pendingTargets.length === 0} + className="gap-2" + > + {isLoading ? ( + <> + <div className="size-4 border-2 border-current border-r-transparent rounded-full animate-spin" /> + 삭제 중... + </> + ) : ( + <> + <Trash2 className="size-4" /> + {pendingTargets.length}개 삭제 + </> + )} + </Button> + </DialogFooter> + </DialogContent> + </Dialog> + ) +}
\ No newline at end of file |
