1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
|
"use client"
import * as React from "react"
import { Loader, Trash2 } from "lucide-react"
import { toast } from "sonner"
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog"
import { Button } from "@/components/ui/button"
interface DeleteAttachmentsDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
attachments: Array<{
id: number;
originalFileName?: string | null;
serialNo?: string | null;
}>;
onSuccess?: () => void;
}
export function DeleteAttachmentsDialog({
open,
onOpenChange,
attachments,
onSuccess,
}: DeleteAttachmentsDialogProps) {
const [isDeleting, setIsDeleting] = React.useState(false)
async function onDelete() {
setIsDeleting(true);
try {
// 여러 개 삭제 시 병렬 처리
const deletePromises = attachments.map(async (attachment) => {
const response = await fetch(`/api/rfq-attachments/${attachment.id}`, {
method: "DELETE",
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message || "삭제 실패");
}
return response.json();
});
const results = await Promise.allSettled(deletePromises);
const failures = results.filter(r => r.status === 'rejected');
if (failures.length > 0) {
const firstError = failures[0];
if (firstError.status === 'rejected') {
toast.error(`일부 파일 삭제 실패: ${firstError.reason}`);
}
return;
}
onOpenChange(false);
toast.success(`${attachments.length}개 파일이 삭제되었습니다`);
onSuccess?.();
} catch (error) {
toast.error(error instanceof Error ? error.message : "파일 삭제 중 오류가 발생했습니다");
} finally {
setIsDeleting(false);
}
}
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent>
<DialogHeader>
<DialogTitle>파일을 삭제하시겠습니까?</DialogTitle>
<DialogDescription>
이 작업은 되돌릴 수 없습니다. 선택한{" "}
<span className="font-medium">{attachments.length}개</span>의 파일이
영구적으로 삭제됩니다.
{attachments[0]?.originalFileName && (
<div className="mt-2 p-2 bg-muted rounded text-sm">
{attachments[0].originalFileName}
{attachments[0].serialNo && (
<span className="text-muted-foreground"> ({attachments[0].serialNo})</span>
)}
</div>
)}
</DialogDescription>
</DialogHeader>
<DialogFooter className="gap-2 sm:space-x-0">
<DialogClose asChild>
<Button variant="outline" disabled={isDeleting}>
취소
</Button>
</DialogClose>
<Button
variant="destructive"
onClick={onDelete}
disabled={isDeleting}
>
{isDeleting && (
<Loader
className="mr-2 size-4 animate-spin"
aria-hidden="true"
/>
)}
삭제
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}
|