diff options
Diffstat (limited to 'lib/rfq-last/table/delete-rfq-dialog.tsx')
| -rw-r--r-- | lib/rfq-last/table/delete-rfq-dialog.tsx | 254 |
1 files changed, 254 insertions, 0 deletions
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 (
+ <AlertDialog open={open} onOpenChange={handleClose}>
+ <AlertDialogContent className="max-w-2xl">
+ <AlertDialogHeader>
+ <AlertDialogTitle>RFQ 삭제</AlertDialogTitle>
+ <AlertDialogDescription className="space-y-4">
+ {isDeleting ? (
+ /* 로딩 중 상태 - 다른 내용 숨김 */
+ <div className="flex items-center justify-center gap-3 py-8">
+ <Loader2 className="h-6 w-6 animate-spin text-primary" />
+ <div className="flex flex-col">
+ <span className="text-base font-medium">RFQ 삭제 처리 중...</span>
+ <span className="text-sm text-muted-foreground mt-1">
+ ECC로 취소 요청을 전송하고 있습니다.
+ </span>
+ </div>
+ </div>
+ ) : (
+ <>
+ <div>
+ 선택된 RFQ 중 ANFNR이 있는 RFQ만 삭제됩니다.
+ </div>
+
+ {/* 삭제 대상 RFQ 목록 */}
+ {rfqsWithAnfnr.length > 0 && (
+ <div className="space-y-2">
+ <p className="font-medium text-sm">삭제 대상 RFQ ({rfqsWithAnfnr.length}건):</p>
+ <div className="max-h-40 overflow-y-auto border rounded-md p-3 space-y-1">
+ {rfqsWithAnfnr.map((rfq) => (
+ <div key={rfq.id} className="text-sm">
+ <span className="font-mono font-medium">{rfq.rfqCode}</span>
+ {rfq.rfqTitle && (
+ <span className="text-muted-foreground ml-2">
+ - {rfq.rfqTitle}
+ </span>
+ )}
+ </div>
+ ))}
+ </div>
+ </div>
+ )}
+
+ {/* ANFNR이 없는 RFQ 경고 */}
+ {hasWarning && (
+ <Alert variant="destructive">
+ <AlertTriangle className="h-4 w-4" />
+ <AlertDescription>
+ <div className="space-y-2">
+ <p className="font-medium">ANFNR이 없는 RFQ는 삭제할 수 없습니다 ({rfqsWithoutAnfnr.length}건):</p>
+ <div className="max-h-32 overflow-y-auto space-y-1">
+ {rfqsWithoutAnfnr.map((rfq) => (
+ <div key={rfq.id} className="text-sm">
+ <span className="font-mono">{rfq.rfqCode}</span>
+ {rfq.rfqTitle && (
+ <span className="text-muted-foreground ml-2">
+ - {rfq.rfqTitle}
+ </span>
+ )}
+ </div>
+ ))}
+ </div>
+ </div>
+ </AlertDescription>
+ </Alert>
+ )}
+
+ {/* 삭제 사유 입력 */}
+ <div className="space-y-2">
+ <Label htmlFor="delete-reason" className="text-sm font-medium">
+ 삭제 사유 <span className="text-destructive">*</span>
+ </Label>
+ <Textarea
+ id="delete-reason"
+ placeholder="RFQ 삭제 사유를 입력해주세요..."
+ value={deleteReason}
+ onChange={(e) => setDeleteReason(e.target.value)}
+ disabled={isDeleting}
+ className="min-h-[100px] resize-none"
+ required
+ />
+ </div>
+
+ {/* 안내 메시지 */}
+ <div className="text-sm text-muted-foreground space-y-1">
+ <p>• ANFNR이 있는 RFQ만 삭제됩니다.</p>
+ <p>• ECC로 SOAP 취소 요청이 전송됩니다.</p>
+ <p>• 성공 시 RFQ 상태가 RFQ 삭제로 변경됩니다.</p>
+ <p>• 연결된 TBE 세션도 취소 상태로 변경됩니다.</p>
+ </div>
+ </>
+ )}
+ </AlertDialogDescription>
+ </AlertDialogHeader>
+ <AlertDialogFooter>
+ <AlertDialogCancel disabled={isDeleting}>취소</AlertDialogCancel>
+ <AlertDialogAction
+ onClick={handleDelete}
+ disabled={isDeleting || rfqsWithAnfnr.length === 0 || !deleteReason || deleteReason.trim() === ""}
+ className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
+ >
+ {isDeleting ? (
+ <>
+ <Loader2 className="mr-2 h-4 w-4 animate-spin" />
+ 삭제 중...
+ </>
+ ) : (
+ "RFQ 삭제"
+ )}
+ </AlertDialogAction>
+ </AlertDialogFooter>
+ </AlertDialogContent>
+ </AlertDialog>
+ );
+}
+
|
