From fd4909bba7be8abc1eeab9ae1b4621c66a61604a Mon Sep 17 00:00:00 2001
From: joonhoekim <26rote@gmail.com>
Date: Sun, 23 Nov 2025 16:40:37 +0900
Subject: (김준회) 돌체 재개발 - 1차 (다운로드 오류 수정 필요)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 lib/dolce/dialogs/add-detail-drawing-dialog.tsx | 376 ++++++++++++++++++++++++
 1 file changed, 376 insertions(+)
 create mode 100644 lib/dolce/dialogs/add-detail-drawing-dialog.tsx

(limited to 'lib/dolce/dialogs/add-detail-drawing-dialog.tsx')

diff --git a/lib/dolce/dialogs/add-detail-drawing-dialog.tsx b/lib/dolce/dialogs/add-detail-drawing-dialog.tsx
new file mode 100644
index 00000000..290a226b
--- /dev/null
+++ b/lib/dolce/dialogs/add-detail-drawing-dialog.tsx
@@ -0,0 +1,376 @@
+"use client";
+
+import { useState, useCallback } from "react";
+import { useDropzone } from "react-dropzone";
+import {
+  Dialog,
+  DialogContent,
+  DialogHeader,
+  DialogTitle,
+  DialogFooter,
+} from "@/components/ui/dialog";
+import { Button } from "@/components/ui/button";
+import { Label } from "@/components/ui/label";
+import { Input } from "@/components/ui/input";
+import {
+  Select,
+  SelectContent,
+  SelectItem,
+  SelectTrigger,
+  SelectValue,
+} from "@/components/ui/select";
+import { Alert, AlertDescription } from "@/components/ui/alert";
+import { Upload, X, FileIcon, Info } from "lucide-react";
+import { toast } from "sonner";
+import { UnifiedDwgReceiptItem, editDetailDwgReceipt, uploadFilesToDetailDrawing } from "../actions";
+import { v4 as uuidv4 } from "uuid";
+
+interface AddDetailDrawingDialogProps {
+  open: boolean;
+  onOpenChange: (open: boolean) => void;
+  drawing: UnifiedDwgReceiptItem | null;
+  vendorCode: string;
+  userId: string;
+  userName: string;
+  userEmail: string;
+  onComplete: () => void;
+  drawingKind: "B3" | "B4"; // 추가
+}
+
+// B3 벤더의 선택 옵션
+const B3_DRAWING_USAGE_OPTIONS = [
+  { value: "APP", label: "APPROVAL (승인용)" },
+  { value: "WOR", label: "WORKING (작업용)" },
+];
+
+const B3_REGISTER_KIND_OPTIONS: Record<string, Array<{ value: string; label: string; revisionRule: string }>> = {
+  APP: [
+    { value: "APPR", label: "승인용 도면 (Full)", revisionRule: "예: A, B, C 또는 R00, R01, R02" },
+    { value: "APPR-P", label: "승인용 도면 (Partial)", revisionRule: "예: A, B, C 또는 R00, R01, R02" },
+  ],
+  WOR: [
+    { value: "WORK", label: "작업용 입수도면 (Full)", revisionRule: "예: A, B, C 또는 R00, R01, R02" },
+    { value: "WORK-P", label: "작업용 입수도면 (Partial)", revisionRule: "예: A, B, C 또는 R00, R01, R02" },
+  ],
+};
+
+// B4 벤더(GTT)의 선택 옵션
+const B4_DRAWING_USAGE_OPTIONS = [
+  { value: "REC", label: "RECEIVE (입수용)" },
+];
+
+const B4_REGISTER_KIND_OPTIONS: Record<string, Array<{ value: string; label: string; revisionRule: string }>> = {
+  REC: [
+    { value: "RECP", label: "Pre. 도면입수", revisionRule: "예: R00, R01, R02, R03" },
+    { value: "RECW", label: "Working 도면입수", revisionRule: "예: R00, R01, R02, R03" },
+  ],
+};
+
+export function AddDetailDrawingDialog({
+  open,
+  onOpenChange,
+  drawing,
+  vendorCode,
+  userId,
+  userName,
+  userEmail,
+  onComplete,
+  drawingKind,
+}: AddDetailDrawingDialogProps) {
+  const [drawingUsage, setDrawingUsage] = useState<string>("");
+  const [registerKind, setRegisterKind] = useState<string>("");
+  const [revision, setRevision] = useState<string>("");
+  const [files, setFiles] = useState<File[]>([]);
+  const [isSubmitting, setIsSubmitting] = useState(false);
+
+  // 파일 드롭 핸들러
+  const onDrop = useCallback((acceptedFiles: File[]) => {
+    setFiles((prev) => [...prev, ...acceptedFiles]);
+  }, []);
+
+  const { getRootProps, getInputProps, isDragActive } = useDropzone({
+    onDrop,
+    multiple: true,
+  });
+
+  // 파일 제거
+  const removeFile = (index: number) => {
+    setFiles((prev) => prev.filter((_, i) => i !== index));
+  };
+
+  // 폼 초기화
+  const resetForm = () => {
+    setDrawingUsage("");
+    setRegisterKind("");
+    setRevision("");
+    setFiles([]);
+  };
+
+  // 제출
+  const handleSubmit = async () => {
+    if (!drawing) return;
+
+    // 유효성 검사
+    if (!drawingUsage) {
+      toast.error("도면용도를 선택하세요");
+      return;
+    }
+    if (!registerKind) {
+      toast.error("등록종류를 선택하세요");
+      return;
+    }
+    if (!revision.trim()) {
+      toast.error("Revision을 입력하세요");
+      return;
+    }
+    if (files.length === 0) {
+      toast.error("최소 1개 이상의 파일을 첨부해야 합니다");
+      return;
+    }
+
+    try {
+      setIsSubmitting(true);
+
+      // 파일 업로드 ID 생성
+      const uploadId = uuidv4();
+
+      // 상세도면 추가
+      const result = await editDetailDwgReceipt({
+        dwgList: [
+          {
+            Mode: "ADD",
+            Status: "Draft",
+            RegisterId: 0,
+            ProjectNo: drawing.ProjectNo,
+            Discipline: drawing.Discipline,
+            DrawingKind: drawing.DrawingKind,
+            DrawingNo: drawing.DrawingNo,
+            DrawingName: drawing.DrawingName,
+            RegisterGroupId: drawing.RegisterGroupId,
+            RegisterSerialNo: 0, // 자동 증가
+            RegisterKind: registerKind,
+            DrawingRevNo: revision,
+            Category: "TS", // To SHI (벤더가 SHI에게 제출)
+            Receiver: null,
+            Manager: "",
+            RegisterDesc: "",
+            UploadId: uploadId,
+            RegCompanyCode: vendorCode,
+          },
+        ],
+        userId,
+        userNm: userName,
+        vendorCode,
+        email: userEmail,
+      });
+
+      if (result > 0) {
+        // 파일 업로드 처리 (상세도면 추가 후)
+        if (files.length > 0) {
+          toast.info(`${files.length}개 파일 업로드를 진행합니다...`);
+          
+          const formData = new FormData();
+          formData.append("uploadId", uploadId);
+          formData.append("userId", userId);
+          formData.append("fileCount", String(files.length));
+
+          files.forEach((file, index) => {
+            formData.append(`file_${index}`, file);
+          });
+
+          const uploadResult = await uploadFilesToDetailDrawing(formData);
+          
+          if (uploadResult.success) {
+            toast.success(`상세도면 추가 및 ${uploadResult.uploadedCount}개 파일 업로드 완료`);
+          } else {
+            toast.warning(`상세도면은 추가되었으나 파일 업로드 실패: ${uploadResult.error}`);
+          }
+        } else {
+          toast.success("상세도면이 추가되었습니다");
+        }
+        
+        resetForm();
+        onComplete();
+      } else {
+        toast.error("상세도면 추가에 실패했습니다");
+      }
+    } catch (error) {
+      console.error("상세도면 추가 실패:", error);
+      toast.error("상세도면 추가 중 오류가 발생했습니다");
+    } finally {
+      setIsSubmitting(false);
+    }
+  };
+
+  const handleCancel = () => {
+    resetForm();
+    onOpenChange(false);
+  };
+
+  // DrawingUsage가 변경되면 RegisterKind 초기화
+  const handleDrawingUsageChange = (value: string) => {
+    setDrawingUsage(value);
+    setRegisterKind("");
+  };
+
+  // 현재 선택 가능한 DrawingUsage 및 RegisterKind 옵션
+  const drawingUsageOptions = drawingKind === "B4" ? B4_DRAWING_USAGE_OPTIONS : B3_DRAWING_USAGE_OPTIONS;
+  const registerKindOptionsMap = drawingKind === "B4" ? B4_REGISTER_KIND_OPTIONS : B3_REGISTER_KIND_OPTIONS;
+  
+  const registerKindOptions = drawingUsage
+    ? registerKindOptionsMap[drawingUsage] || []
+    : [];
+
+  // 선택된 RegisterKind의 Revision Rule
+  const revisionRule = registerKindOptions.find((opt) => opt.value === registerKind)?.revisionRule || "";
+
+  return (
+    <Dialog open={open} onOpenChange={onOpenChange}>
+      <DialogContent className="max-w-2xl">
+        <DialogHeader>
+          <DialogTitle>상세도면 추가</DialogTitle>
+        </DialogHeader>
+
+        <div className="space-y-6">
+          {/* 도면 정보 표시 */}
+          {drawing && (
+            <Alert>
+              <Info className="h-4 w-4" />
+              <AlertDescription>
+                <div className="font-medium">{drawing.DrawingNo}</div>
+                <div className="text-sm text-muted-foreground">{drawing.DrawingName}</div>
+              </AlertDescription>
+            </Alert>
+          )}
+
+          {/* 도면용도 선택 */}
+          <div className="space-y-2">
+            <Label>도면용도 (Drawing Usage)</Label>
+            <Select value={drawingUsage} onValueChange={handleDrawingUsageChange}>
+              <SelectTrigger>
+                <SelectValue placeholder="도면용도를 선택하세요" />
+              </SelectTrigger>
+              <SelectContent>
+                {drawingUsageOptions.map((option) => (
+                  <SelectItem key={option.value} value={option.value}>
+                    {option.label}
+                  </SelectItem>
+                ))}
+              </SelectContent>
+            </Select>
+          </div>
+
+          {/* 등록종류 선택 */}
+          <div className="space-y-2">
+            <Label>등록종류 (Register Kind)</Label>
+            <Select
+              value={registerKind}
+              onValueChange={setRegisterKind}
+              disabled={!drawingUsage}
+            >
+              <SelectTrigger>
+                <SelectValue placeholder="등록종류를 선택하세요" />
+              </SelectTrigger>
+              <SelectContent>
+                {registerKindOptions.map((option) => (
+                  <SelectItem key={option.value} value={option.value}>
+                    {option.label}
+                  </SelectItem>
+                ))}
+              </SelectContent>
+            </Select>
+            {revisionRule && (
+              <p className="text-sm text-muted-foreground">
+                Revision 입력 형식: {revisionRule}
+              </p>
+            )}
+          </div>
+
+          {/* Revision 입력 */}
+          <div className="space-y-2">
+            <Label>Revision</Label>
+            <Input
+              value={revision}
+              onChange={(e) => setRevision(e.target.value)}
+              placeholder="예: A, B, R00, R01"
+              disabled={!registerKind}
+            />
+          </div>
+
+          {/* 파일 업로드 */}
+          <div className="space-y-2">
+            <Label>첨부파일 (필수) *</Label>
+            <div
+              {...getRootProps()}
+              className={`
+                border-2 border-dashed rounded-lg p-8 text-center cursor-pointer
+                transition-colors
+                ${isDragActive ? "border-primary bg-primary/5" : "border-muted-foreground/25"}
+                ${files.length > 0 ? "py-4" : ""}
+              `}
+            >
+              <input {...getInputProps()} />
+              {files.length === 0 ? (
+                <div className="space-y-2">
+                  <Upload className="h-8 w-8 mx-auto text-muted-foreground" />
+                  <div>
+                    <p className="text-sm font-medium">
+                      파일을 드래그하거나 클릭하여 선택
+                    </p>
+                    <p className="text-xs text-muted-foreground">
+                      여러 파일을 한 번에 업로드할 수 있습니다
+                    </p>
+                  </div>
+                </div>
+              ) : (
+                <div className="space-y-2">
+                  <p className="text-sm font-medium">
+                    {files.length}개 파일 선택됨
+                  </p>
+                  <p className="text-xs text-muted-foreground">
+                    추가로 파일을 드래그하거나 클릭하여 더 추가할 수 있습니다
+                  </p>
+                </div>
+              )}
+            </div>
+
+            {/* 선택된 파일 목록 */}
+            {files.length > 0 && (
+              <div className="space-y-2 mt-4">
+                {files.map((file, index) => (
+                  <div
+                    key={index}
+                    className="flex items-center gap-2 p-2 border rounded-lg"
+                  >
+                    <FileIcon className="h-4 w-4 text-muted-foreground" />
+                    <span className="flex-1 text-sm truncate">{file.name}</span>
+                    <span className="text-xs text-muted-foreground">
+                      {(file.size / 1024).toFixed(2)} KB
+                    </span>
+                    <Button
+                      variant="ghost"
+                      size="icon"
+                      onClick={() => removeFile(index)}
+                    >
+                      <X className="h-4 w-4" />
+                    </Button>
+                  </div>
+                ))}
+              </div>
+            )}
+          </div>
+        </div>
+
+        <DialogFooter>
+          <Button variant="outline" onClick={handleCancel} disabled={isSubmitting}>
+            취소
+          </Button>
+          <Button onClick={handleSubmit} disabled={isSubmitting}>
+            {isSubmitting ? "처리 중..." : "추가"}
+          </Button>
+        </DialogFooter>
+      </DialogContent>
+    </Dialog>
+  );
+}
+
-- 
cgit v1.2.3

