From 50adedf48ee4674ebe00f1ee72d93485183cdc51 Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Fri, 5 Sep 2025 11:44:32 +0000
Subject: (대표님, 최겸, 임수민) EDP 입력 진행률, 견적목록관리, EDP excel
 import 오류수정, GTC-Contract
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 lib/rfq-last/vendor/add-vendor-dialog.tsx | 307 ++++++++++++++++++++++++++++++
 1 file changed, 307 insertions(+)
 create mode 100644 lib/rfq-last/vendor/add-vendor-dialog.tsx

(limited to 'lib/rfq-last/vendor/add-vendor-dialog.tsx')

diff --git a/lib/rfq-last/vendor/add-vendor-dialog.tsx b/lib/rfq-last/vendor/add-vendor-dialog.tsx
new file mode 100644
index 00000000..d8745298
--- /dev/null
+++ b/lib/rfq-last/vendor/add-vendor-dialog.tsx
@@ -0,0 +1,307 @@
+"use client";
+
+import * as React from "react";
+import {
+  Dialog,
+  DialogContent,
+  DialogDescription,
+  DialogFooter,
+  DialogHeader,
+  DialogTitle,
+} from "@/components/ui/dialog";
+import { Button } from "@/components/ui/button";
+import { Label } from "@/components/ui/label";
+import {
+  Command,
+  CommandEmpty,
+  CommandGroup,
+  CommandInput,
+  CommandItem,
+  CommandList,
+} from "@/components/ui/command";
+import {
+  Popover,
+  PopoverContent,
+  PopoverTrigger
+} from "@/components/ui/popover";
+import { Check, ChevronsUpDown, Loader2, X, Plus } from "lucide-react";
+import { cn } from "@/lib/utils";
+import { toast } from "sonner";
+import { addVendorsToRfq } from "../service";
+import { getVendorsForSelection } from "@/lib/b-rfq/service";
+import { Badge } from "@/components/ui/badge";
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
+import { ScrollArea } from "@/components/ui/scroll-area";
+import { Alert, AlertDescription } from "@/components/ui/alert";
+import { Info } from "lucide-react";
+
+interface AddVendorDialogProps {
+  open: boolean;
+  onOpenChange: (open: boolean) => void;
+  rfqId: number;
+  onSuccess: () => void;
+}
+
+export function AddVendorDialog({
+  open,
+  onOpenChange,
+  rfqId,
+  onSuccess,
+}: AddVendorDialogProps) {
+  const [isLoading, setIsLoading] = React.useState(false);
+  const [vendorOpen, setVendorOpen] = React.useState(false);
+  const [vendorList, setVendorList] = React.useState<any[]>([]);
+  const [selectedVendors, setSelectedVendors] = React.useState<any[]>([]);
+
+  // 벤더 로드
+  const loadVendors = React.useCallback(async () => {
+    try {
+      const result = await getVendorsForSelection();
+      if (result) {
+        setVendorList(result);
+      }
+    } catch (error) {
+      console.error("Failed to load vendors:", error);
+      toast.error("벤더 목록을 불러오는데 실패했습니다.");
+    }
+  }, []);
+
+  React.useEffect(() => {
+    if (open) {
+      loadVendors();
+    }
+  }, [open, loadVendors]);
+
+  // 초기화
+  React.useEffect(() => {
+    if (!open) {
+      setSelectedVendors([]);
+    }
+  }, [open]);
+
+  // 벤더 추가
+  const handleAddVendor = (vendor: any) => {
+    if (!selectedVendors.find(v => v.id === vendor.id)) {
+      setSelectedVendors([...selectedVendors, vendor]);
+    }
+    setVendorOpen(false);
+  };
+
+  // 벤더 제거
+  const handleRemoveVendor = (vendorId: number) => {
+    setSelectedVendors(selectedVendors.filter(v => v.id !== vendorId));
+  };
+
+  // 제출 처리 - 벤더만 추가
+  const handleSubmit = async () => {
+    if (selectedVendors.length === 0) {
+      toast.error("최소 1개 이상의 벤더를 선택해주세요.");
+      return;
+    }
+
+    setIsLoading(true);
+
+    try {
+      const vendorIds = selectedVendors.map(v => v.id);
+      const result = await addVendorsToRfq({
+        rfqId,
+        vendorIds,
+        // 기본값으로 벤더만 추가 (상세 조건은 나중에 일괄 입력)
+        conditions: null, 
+      });
+
+      if (result.success) {
+        toast.success(
+          <div>
+            <p>{selectedVendors.length}개 벤더가 추가되었습니다.</p>
+            <p className="text-sm text-muted-foreground mt-1">
+              벤더 목록에서 '정보 일괄 입력' 버튼으로 조건을 설정하세요.
+            </p>
+          </div>
+        );
+        onSuccess();
+        onOpenChange(false);
+      } else {
+        toast.error(result.error || "벤더 추가에 실패했습니다.");
+      }
+    } catch (error) {
+      console.error("Submit error:", error);
+      toast.error("오류가 발생했습니다.");
+    } finally {
+      setIsLoading(false);
+    }
+  };
+
+  // 이미 선택된 벤더인지 확인
+  const isVendorSelected = (vendorId: number) => {
+    return selectedVendors.some(v => v.id === vendorId);
+  };
+
+  return (
+    <Dialog open={open} onOpenChange={onOpenChange}>
+      <DialogContent className="max-w-2xl max-h-[80vh] p-0 flex flex-col">
+        {/* 헤더 */}
+        <DialogHeader className="p-6 pb-0">
+          <DialogTitle>벤더 추가</DialogTitle>
+          <DialogDescription>
+            견적 요청을 보낼 벤더를 선택하세요. 조건 설정은 추가 후 일괄로 진행할 수 있습니다.
+          </DialogDescription>
+        </DialogHeader>
+
+        {/* 컨텐츠 영역 */}
+        <div className="flex-1 px-6 py-4 overflow-y-auto">
+          <div className="space-y-4">
+            {/* 안내 메시지 */}
+            <Alert>
+              <Info className="h-4 w-4" />
+              <AlertDescription>
+                여기서는 벤더만 선택합니다. 납기일, 결제조건 등의 상세 정보는 벤더 추가 후 
+                '정보 일괄 입력' 기능으로 한 번에 설정할 수 있습니다.
+              </AlertDescription>
+            </Alert>
+
+            {/* 벤더 선택 카드 */}
+            <Card>
+              <CardHeader>
+                <div className="flex items-center justify-between">
+                  <CardTitle className="text-lg">벤더 선택</CardTitle>
+                  <Badge variant="outline" className="ml-2">
+                    {selectedVendors.length}개 선택됨
+                  </Badge>
+                </div>
+                <CardDescription>
+                  RFQ를 발송할 벤더를 선택하세요. 여러 개 선택 가능합니다.
+                </CardDescription>
+              </CardHeader>
+              <CardContent>
+                <div className="space-y-4">
+                  {/* 벤더 추가 버튼 */}
+                  <Popover open={vendorOpen} onOpenChange={setVendorOpen}>
+                    <PopoverTrigger asChild>
+                      <Button
+                        variant="outline"
+                        role="combobox"
+                        aria-expanded={vendorOpen}
+                        className="w-full justify-between"
+                        disabled={vendorList.length === 0}
+                      >
+                        <span className="flex items-center gap-2">
+                          <Plus className="h-4 w-4" />
+                          벤더 선택하기
+                        </span>
+                        <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
+                      </Button>
+                    </PopoverTrigger>
+                    <PopoverContent className="w-[500px] p-0" align="start">
+                      <Command>
+                        <CommandInput placeholder="벤더명 또는 코드로 검색..." />
+                        <CommandList
+                                      onWheel={(e) => {
+                                        e.stopPropagation(); // 이벤트 전파 차단
+                                        const target = e.currentTarget;
+                                        target.scrollTop += e.deltaY; // 직접 스크롤 처리
+                                      }}
+                        >
+                          <CommandEmpty>검색 결과가 없습니다.</CommandEmpty>
+                          <CommandGroup>
+                            {vendorList
+                              .filter(vendor => !isVendorSelected(vendor.id))
+                              .map((vendor) => (
+                                <CommandItem
+                                  key={vendor.id}
+                                  value={`${vendor.vendorCode} ${vendor.vendorName}`}
+                                  onSelect={() => handleAddVendor(vendor)}
+                                >
+                                  <div className="flex items-center gap-2 w-full">
+                                    <Badge variant="outline" className="shrink-0">
+                                      {vendor.vendorCode}
+                                    </Badge>
+                                    <span className="truncate">{vendor.vendorName}</span>
+                                    {vendor.country && (
+                                      <span className="text-xs text-muted-foreground ml-auto">
+                                        {vendor.country}
+                                      </span>
+                                    )}
+                                  </div>
+                                </CommandItem>
+                              ))}
+                          </CommandGroup>
+                        </CommandList>
+                      </Command>
+                    </PopoverContent>
+                  </Popover>
+
+                  {/* 선택된 벤더 목록 */}
+                  {selectedVendors.length > 0 && (
+                    <div className="space-y-2">
+                      <Label className="text-sm text-muted-foreground">선택된 벤더 목록</Label>
+                      <ScrollArea className="h-[200px] w-full rounded-md border p-4">
+                        <div className="space-y-2">
+                          {selectedVendors.map((vendor, index) => (
+                            <div
+                              key={vendor.id}
+                              className="flex items-center justify-between p-2 rounded-lg bg-secondary/50"
+                            >
+                              <div className="flex items-center gap-2">
+                                <span className="text-sm text-muted-foreground">
+                                  {index + 1}.
+                                </span>
+                                <Badge variant="outline">
+                                  {vendor.vendorCode}
+                                </Badge>
+                                <span className="text-sm font-medium">
+                                  {vendor.vendorName}
+                                </span>
+                              </div>
+                              <Button
+                                variant="ghost"
+                                size="sm"
+                                onClick={() => handleRemoveVendor(vendor.id)}
+                                className="h-8 w-8 p-0"
+                              >
+                                <X className="h-4 w-4" />
+                              </Button>
+                            </div>
+                          ))}
+                        </div>
+                      </ScrollArea>
+                    </div>
+                  )}
+
+                  {/* 벤더가 없는 경우 메시지 */}
+                  {selectedVendors.length === 0 && (
+                    <div className="text-center py-8 text-muted-foreground">
+                      <p className="text-sm">아직 선택된 벤더가 없습니다.</p>
+                      <p className="text-xs mt-1">위 버튼을 클릭하여 벤더를 추가하세요.</p>
+                    </div>
+                  )}
+                </div>
+              </CardContent>
+            </Card>
+          </div>
+        </div>
+
+        {/* 푸터 */}
+        <DialogFooter className="p-6 pt-0 border-t">
+          <Button
+            variant="outline"
+            onClick={() => onOpenChange(false)}
+            disabled={isLoading}
+          >
+            취소
+          </Button>
+          <Button 
+            onClick={handleSubmit} 
+            disabled={isLoading || selectedVendors.length === 0}
+          >
+            {isLoading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
+            {selectedVendors.length > 0 
+              ? `${selectedVendors.length}개 벤더 추가`
+              : '벤더 추가'
+            }
+          </Button>
+        </DialogFooter>
+      </DialogContent>
+    </Dialog>
+  );
+}
\ No newline at end of file
-- 
cgit v1.2.3

