summaryrefslogtreecommitdiff
path: root/lib/rfq-last/attachment/delete-attachments-dialog.tsx
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-09-04 08:31:31 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-09-04 08:31:31 +0000
commitb67e36df49f067cbd5ba899f9fbcc755f38d4b4f (patch)
tree5a71c5960f90d988cd509e3ef26bff497a277661 /lib/rfq-last/attachment/delete-attachments-dialog.tsx
parentb7f54b06c1ef9e619f5358fb0a5caad9703c8905 (diff)
(대표님, 최겸, 임수민) 작업사항 커밋
Diffstat (limited to 'lib/rfq-last/attachment/delete-attachments-dialog.tsx')
-rw-r--r--lib/rfq-last/attachment/delete-attachments-dialog.tsx117
1 files changed, 117 insertions, 0 deletions
diff --git a/lib/rfq-last/attachment/delete-attachments-dialog.tsx b/lib/rfq-last/attachment/delete-attachments-dialog.tsx
new file mode 100644
index 00000000..c9041639
--- /dev/null
+++ b/lib/rfq-last/attachment/delete-attachments-dialog.tsx
@@ -0,0 +1,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>
+ )
+} \ No newline at end of file