summaryrefslogtreecommitdiff
path: root/lib/dolce/dialogs/add-detail-drawing-dialog.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dolce/dialogs/add-detail-drawing-dialog.tsx')
-rw-r--r--lib/dolce/dialogs/add-detail-drawing-dialog.tsx376
1 files changed, 376 insertions, 0 deletions
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>
+ );
+}
+