From 47fb72704161b4b58a27c7f5c679fc44618de9a1 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Tue, 4 Nov 2025 10:03:32 +0000 Subject: (최겸) 구매 견적 내 RFQ Cancel/Delete, 연동제 적용, MRC Type 개발 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/rfq-last/table/delete-rfq-dialog.tsx | 254 +++++++++++++++++++++++ lib/rfq-last/table/rfq-table-columns.tsx | 1 + lib/rfq-last/table/rfq-table-toolbar-actions.tsx | 56 ++++- 3 files changed, 310 insertions(+), 1 deletion(-) create mode 100644 lib/rfq-last/table/delete-rfq-dialog.tsx (limited to 'lib/rfq-last/table') diff --git a/lib/rfq-last/table/delete-rfq-dialog.tsx b/lib/rfq-last/table/delete-rfq-dialog.tsx new file mode 100644 index 00000000..01af5453 --- /dev/null +++ b/lib/rfq-last/table/delete-rfq-dialog.tsx @@ -0,0 +1,254 @@ +"use client"; + +import * as React from "react"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/components/ui/alert-dialog"; +import { RfqsLastView } from "@/db/schema"; +import { deleteRfq } from "@/lib/rfq-last/delete-action"; +import { Loader2, AlertTriangle } from "lucide-react"; +import { Alert, AlertDescription } from "@/components/ui/alert"; +import { toast } from "sonner"; +import { Textarea } from "@/components/ui/textarea"; +import { Label } from "@/components/ui/label"; + +interface DeleteRfqDialogProps { + open: boolean; + onOpenChange: (open: boolean) => void; + selectedRfqs: RfqsLastView[]; + onSuccess?: () => void; +} + +export function DeleteRfqDialog({ + open, + onOpenChange, + selectedRfqs, + onSuccess, +}: DeleteRfqDialogProps) { + const [isDeleting, setIsDeleting] = React.useState(false); + const [deleteReason, setDeleteReason] = React.useState(""); + + // ANFNR이 있는 RFQ만 필터링 + const rfqsWithAnfnr = React.useMemo(() => { + return selectedRfqs.filter(rfq => rfq.ANFNR && rfq.ANFNR.trim() !== ""); + }, [selectedRfqs]); + + const handleDelete = async () => { + if (rfqsWithAnfnr.length === 0) { + toast.error("ANFNR이 있는 RFQ가 선택되지 않았습니다."); + return; + } + + if (!deleteReason || deleteReason.trim() === "") { + toast.error("삭제 사유를 입력해주세요."); + return; + } + + setIsDeleting(true); + + try { + const rfqIds = rfqsWithAnfnr.map(rfq => rfq.id); + const result = await deleteRfq(rfqIds, deleteReason.trim()); + + if (result.results) { + const successCount = result.results.filter(r => r.success).length; + const failCount = result.results.length - successCount; + + if (result.success) { + // 성공한 RFQ 목록 + const successRfqs = result.results + .filter(r => r.success) + .map(r => { + const rfq = rfqsWithAnfnr.find(rf => rf.id === r.rfqId); + return rfq?.rfqCode || `RFQ ID: ${r.rfqId}`; + }); + + if (successCount > 0) { + toast.success( + `RFQ 삭제가 완료되었습니다. (${successCount}건)`, + { + description: successRfqs.length <= 3 + ? successRfqs.join(", ") + : `${successRfqs.slice(0, 3).join(", ")} 외 ${successRfqs.length - 3}건`, + duration: 5000, + } + ); + } + + // 실패한 RFQ가 있는 경우 + if (failCount > 0) { + const failRfqs = result.results + .filter(r => !r.success) + .map(r => { + const rfq = rfqsWithAnfnr.find(rf => rf.id === r.rfqId); + return `${rfq?.rfqCode || r.rfqId}: ${r.error || "알 수 없는 오류"}`; + }); + + toast.error( + `${failCount}건의 RFQ 삭제가 실패했습니다.`, + { + description: failRfqs.length <= 3 + ? failRfqs.join(", ") + : `${failRfqs.slice(0, 3).join(", ")} 외 ${failRfqs.length - 3}건`, + duration: 7000, + } + ); + } + } else { + // 전체 실패 + toast.error(result.message || "RFQ 삭제에 실패했습니다."); + } + } else { + if (result.success) { + toast.success(result.message); + } else { + toast.error(result.message); + } + } + + // 성공 여부와 관계없이 다이얼로그 닫기 및 콜백 호출 + if (result.success) { + setDeleteReason(""); // 성공 시 입력 필드 초기화 + onOpenChange(false); + onSuccess?.(); + } + } catch (err) { + toast.error(err instanceof Error ? err.message : "RFQ 삭제 중 오류가 발생했습니다."); + } finally { + setIsDeleting(false); + } + }; + + const handleClose = () => { + if (!isDeleting) { + setDeleteReason(""); // 다이얼로그 닫을 때 입력 필드 초기화 + onOpenChange(false); + } + }; + + // ANFNR이 없는 RFQ가 포함된 경우 경고 표시 + const rfqsWithoutAnfnr = selectedRfqs.filter(rfq => !rfq.ANFNR || rfq.ANFNR.trim() === ""); + const hasWarning = rfqsWithoutAnfnr.length > 0; + + return ( + + + + RFQ 삭제 + + {isDeleting ? ( + /* 로딩 중 상태 - 다른 내용 숨김 */ +
+ +
+ RFQ 삭제 처리 중... + + ECC로 취소 요청을 전송하고 있습니다. + +
+
+ ) : ( + <> +
+ 선택된 RFQ 중 ANFNR이 있는 RFQ만 삭제됩니다. +
+ + {/* 삭제 대상 RFQ 목록 */} + {rfqsWithAnfnr.length > 0 && ( +
+

삭제 대상 RFQ ({rfqsWithAnfnr.length}건):

+
+ {rfqsWithAnfnr.map((rfq) => ( +
+ {rfq.rfqCode} + {rfq.rfqTitle && ( + + - {rfq.rfqTitle} + + )} +
+ ))} +
+
+ )} + + {/* ANFNR이 없는 RFQ 경고 */} + {hasWarning && ( + + + +
+

ANFNR이 없는 RFQ는 삭제할 수 없습니다 ({rfqsWithoutAnfnr.length}건):

+
+ {rfqsWithoutAnfnr.map((rfq) => ( +
+ {rfq.rfqCode} + {rfq.rfqTitle && ( + + - {rfq.rfqTitle} + + )} +
+ ))} +
+
+
+
+ )} + + {/* 삭제 사유 입력 */} +
+ +