summaryrefslogtreecommitdiff
path: root/lib/rfq-last/table/delete-rfq-dialog.tsx
blob: 01af5453e28e796818a1dd179ec86a72f26922ba (plain)
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
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>
  );
}