summaryrefslogtreecommitdiff
path: root/lib/dolce/dialogs/add-detail-drawing-dialog.tsx
diff options
context:
space:
mode:
authorjoonhoekim <26rote@gmail.com>2025-11-25 15:42:00 +0900
committerjoonhoekim <26rote@gmail.com>2025-11-25 15:42:00 +0900
commit4586f2cd95f1cd7112cbec80399da8817df0d289 (patch)
tree48572a71cb01d3850defb6cab6c616ca545f5f25 /lib/dolce/dialogs/add-detail-drawing-dialog.tsx
parent25b2561bf17128b96f023c977efb5cb51da0b4aa (diff)
(김준회) dolce: i18n 적용, 상세도면 수정 적용
Diffstat (limited to 'lib/dolce/dialogs/add-detail-drawing-dialog.tsx')
-rw-r--r--lib/dolce/dialogs/add-detail-drawing-dialog.tsx465
1 files changed, 0 insertions, 465 deletions
diff --git a/lib/dolce/dialogs/add-detail-drawing-dialog.tsx b/lib/dolce/dialogs/add-detail-drawing-dialog.tsx
deleted file mode 100644
index 48614ecf..00000000
--- a/lib/dolce/dialogs/add-detail-drawing-dialog.tsx
+++ /dev/null
@@ -1,465 +0,0 @@
-"use client";
-
-import { useState } from "react";
-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 } from "../actions";
-import { v4 as uuidv4 } from "uuid";
-import { useFileUploadWithProgress } from "../hooks/use-file-upload-with-progress";
-import { uploadFilesWithProgress } from "../utils/upload-with-progress";
-import { FileUploadProgressList } from "../components/file-upload-progress-list";
-import {
- getB3DrawingUsageOptions,
- getB3RegisterKindOptions,
- getB4DrawingUsageOptions,
- getB4RegisterKindOptions
-} from "../utils/code-translator";
-
-interface AddDetailDrawingDialogProps {
- open: boolean;
- onOpenChange: (open: boolean) => void;
- drawing: UnifiedDwgReceiptItem | null;
- vendorCode: string;
- userId: string;
- userName: string;
- userEmail: string;
- onComplete: () => void;
- drawingKind: "B3" | "B4";
- lng?: string; // i18n support
-}
-
-export function AddDetailDrawingDialog({
- open,
- onOpenChange,
- drawing,
- vendorCode,
- userId,
- userName,
- userEmail,
- onComplete,
- drawingKind,
- lng = "ko",
-}: AddDetailDrawingDialogProps) {
- const [drawingUsage, setDrawingUsage] = useState<string>("");
- const [registerKind, setRegisterKind] = useState<string>("");
- const [revision, setRevision] = useState<string>("");
- const [revisionError, setRevisionError] = useState<string>("");
- const [isSubmitting, setIsSubmitting] = useState(false);
-
- // 옵션 생성 (다국어 지원)
- const drawingUsageOptions = drawingKind === "B3"
- ? getB3DrawingUsageOptions(lng)
- : getB4DrawingUsageOptions(lng);
-
- const registerKindOptions = drawingKind === "B3"
- ? getB3RegisterKindOptions(drawingUsage, lng).map(opt => ({
- ...opt,
- revisionRule: lng === "ko" ? "예: A, B, C 또는 R00, R01, R02" : "e.g. A, B, C or R00, R01, R02"
- }))
- : getB4RegisterKindOptions(drawingUsage, lng).map(opt => ({
- ...opt,
- revisionRule: lng === "ko" ? "예: R00, R01, R02, R03" : "e.g. R00, R01, R02, R03"
- }));
-
- // 파일 업로드 훅 사용 (진행도 추적)
- const {
- fileProgresses,
- files,
- removeFile,
- clearFiles,
- updateFileProgress,
- getRootProps,
- getInputProps,
- isDragActive,
- } = useFileUploadWithProgress();
-
- // Revision 유효성 검증 함수
- const validateRevision = (value: string): string => {
- if (!value.trim()) {
- return "Revision을 입력하세요";
- }
-
- const upperValue = value.toUpperCase().trim();
-
- // A-Z 패턴 (단일 알파벳)
- if (/^[A-Z]$/.test(upperValue)) {
- return "";
- }
-
- // R00-R99 패턴
- if (/^R\d{2}$/.test(upperValue)) {
- return "";
- }
-
- return "올바른 형식이 아닙니다 (A-Z 또는 R00-R99)";
- };
-
- // Revision 입력 핸들러
- const handleRevisionChange = (value: string) => {
- const processedValue = value.toUpperCase();
- setRevision(processedValue);
-
- // 값이 있을 때만 validation
- if (processedValue.trim()) {
- const error = validateRevision(processedValue);
- setRevisionError(error);
- } else {
- setRevisionError("");
- }
- };
-
- // 폼 초기화
- const resetForm = () => {
- setDrawingUsage("");
- setRegisterKind("");
- setRevision("");
- setRevisionError("");
- clearFiles();
- };
-
- // 제출
- const handleSubmit = async () => {
- if (!drawing) return;
-
- // 유효성 검사
- if (!drawingUsage) {
- toast.error("도면용도를 선택하세요");
- return;
- }
- if (!registerKind) {
- toast.error("등록종류를 선택하세요");
- return;
- }
- if (!revision.trim()) {
- toast.error("Revision을 입력하세요");
- setRevisionError("Revision을 입력하세요");
- return;
- }
-
- // Revision 형식 검증
- const revisionValidationError = validateRevision(revision);
- if (revisionValidationError) {
- toast.error(revisionValidationError);
- setRevisionError(revisionValidationError);
- 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}개 파일 업로드를 진행합니다...`);
-
- // 모든 파일 상태를 uploading으로 변경
- files.forEach((_, index) => {
- updateFileProgress(index, 0, "uploading");
- });
-
- const uploadResult = await uploadFilesWithProgress({
- uploadId,
- userId,
- files,
- callbacks: {
- onProgress: (fileIndex, progress) => {
- updateFileProgress(fileIndex, progress, "uploading");
- },
- onFileComplete: (fileIndex) => {
- updateFileProgress(fileIndex, 100, "completed");
- },
- onFileError: (fileIndex, error) => {
- updateFileProgress(fileIndex, 0, "error", error);
- },
- },
- });
-
- if (uploadResult.success) {
- toast.success(`상세도면 추가 및 ${uploadResult.uploadedCount}개 파일 업로드 완료`);
- } else {
- toast.warning(`상세도면은 추가되었으나 파일 업로드 실패: ${uploadResult.error}`);
- }
- } else {
- toast.success("상세도면이 추가되었습니다");
- }
-
- // API 호출 성공 시 무조건 다이얼로그 닫기 (파일 업로드 성공 여부와 무관)
- resetForm();
- onComplete();
- onOpenChange(false);
- } 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("");
- setRevision("");
- setRevisionError("");
- };
-
- // 선택된 RegisterKind의 Revision Rule
- const revisionRule = registerKindOptions.find((opt) => opt.value === registerKind)?.revisionRule || "";
-
- // 추가 버튼 활성화 조건
- const isFormValid =
- drawingUsage.trim() !== "" &&
- registerKind.trim() !== "" &&
- revision.trim() !== "" &&
- !revisionError &&
- files.length > 0;
-
- 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) => handleRevisionChange(e.target.value)}
- placeholder="예: A, B, R00, R01"
- disabled={!registerKind}
- className={revisionError ? "border-red-500 focus-visible:ring-red-500" : ""}
- />
- {revisionError && (
- <p className="text-sm text-red-500 flex items-center gap-1">
- {revisionError}
- </p>
- )}
- {!revisionError && revision && (
- <p className="text-sm text-green-600 flex items-center gap-1">
- ✓ 올바른 형식입니다
- </p>
- )}
- </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">
- 여러 파일을 한 번에 업로드할 수 있습니다 (최대 1GB/파일)
- </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">
- {isSubmitting ? (
- // 업로드 중: 진행도 표시
- <FileUploadProgressList fileProgresses={fileProgresses} />
- ) : (
- // 대기 중: 삭제 버튼 표시
- <>
- <div className="flex items-center justify-between mb-2">
- <h4 className="text-sm font-medium">
- 선택된 파일 ({files.length}개)
- </h4>
- <Button
- variant="ghost"
- size="sm"
- onClick={clearFiles}
- >
- 전체 제거
- </Button>
- </div>
- <div className="max-h-60 overflow-y-auto space-y-2">
- {files.map((file, index) => (
- <div
- key={index}
- className="flex items-center gap-2 p-2 border rounded-lg bg-muted/50"
- >
- <FileIcon className="h-4 w-4 text-muted-foreground shrink-0" />
- <div className="flex-1 min-w-0">
- <p className="text-sm truncate">{file.name}</p>
- <p className="text-xs text-muted-foreground">
- {(file.size / 1024 / 1024).toFixed(2)} MB
- </p>
- </div>
- <Button
- variant="ghost"
- size="sm"
- onClick={() => removeFile(index)}
- >
- <X className="h-4 w-4" />
- </Button>
- </div>
- ))}
- </div>
- </>
- )}
- </div>
- )}
- </div>
- </div>
-
- <DialogFooter>
- <Button variant="outline" onClick={handleCancel} disabled={isSubmitting}>
- 취소
- </Button>
- <Button onClick={handleSubmit} disabled={isSubmitting || !isFormValid}>
- {isSubmitting ? "처리 중..." : "추가"}
- </Button>
- </DialogFooter>
- </DialogContent>
- </Dialog>
- );
-}
-