diff options
Diffstat (limited to 'lib/dolce/dialogs')
| -rw-r--r-- | lib/dolce/dialogs/add-detail-drawing-dialog.tsx | 69 | ||||
| -rw-r--r-- | lib/dolce/dialogs/b4-bulk-upload-dialog.tsx | 6 | ||||
| -rw-r--r-- | lib/dolce/dialogs/b4-upload-validation-dialog.tsx | 30 |
3 files changed, 93 insertions, 12 deletions
diff --git a/lib/dolce/dialogs/add-detail-drawing-dialog.tsx b/lib/dolce/dialogs/add-detail-drawing-dialog.tsx index 2f7f15a7..2454b1ba 100644 --- a/lib/dolce/dialogs/add-detail-drawing-dialog.tsx +++ b/lib/dolce/dialogs/add-detail-drawing-dialog.tsx @@ -82,6 +82,7 @@ export function AddDetailDrawingDialog({ 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); // 파일 업로드 훅 사용 (진행도 추적) @@ -96,11 +97,54 @@ export function AddDetailDrawingDialog({ 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) => { + let processedValue = value.toUpperCase().trim(); + + // R1~R9 입력시 R01~R09로 자동 변환 + const rNumberMatch = processedValue.match(/^R(\d)$/); + if (rNumberMatch) { + processedValue = `R0${rNumberMatch[1]}`; + } + + setRevision(processedValue); + + // 값이 있을 때만 validation + if (processedValue) { + const error = validateRevision(processedValue); + setRevisionError(error); + } else { + setRevisionError(""); + } + }; + // 폼 초기화 const resetForm = () => { setDrawingUsage(""); setRegisterKind(""); setRevision(""); + setRevisionError(""); clearFiles(); }; @@ -119,8 +163,18 @@ export function AddDetailDrawingDialog({ } 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; @@ -222,6 +276,8 @@ export function AddDetailDrawingDialog({ const handleDrawingUsageChange = (value: string) => { setDrawingUsage(value); setRegisterKind(""); + setRevision(""); + setRevisionError(""); }; // 현재 선택 가능한 DrawingUsage 및 RegisterKind 옵션 @@ -302,10 +358,21 @@ export function AddDetailDrawingDialog({ <Label>Revision</Label> <Input value={revision} - onChange={(e) => setRevision(e.target.value)} + 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> {/* 파일 업로드 */} diff --git a/lib/dolce/dialogs/b4-bulk-upload-dialog.tsx b/lib/dolce/dialogs/b4-bulk-upload-dialog.tsx index cd336e92..75b221da 100644 --- a/lib/dolce/dialogs/b4-bulk-upload-dialog.tsx +++ b/lib/dolce/dialogs/b4-bulk-upload-dialog.tsx @@ -591,6 +591,12 @@ export function B4BulkUploadDialog({ <span>{uploadProgress}%</span> </div> <Progress value={uploadProgress} className="h-2" /> + {/* 90% 이상일 때 추가 안내 메시지 */} + {uploadProgress >= 90 && uploadProgress < 100 && ( + <p className="text-xs text-muted-foreground text-center pt-2"> + 서버에서 DOLCE API로 전송 중입니다... + </p> + )} </div> </div> )} diff --git a/lib/dolce/dialogs/b4-upload-validation-dialog.tsx b/lib/dolce/dialogs/b4-upload-validation-dialog.tsx index b274d604..f3a7c70a 100644 --- a/lib/dolce/dialogs/b4-upload-validation-dialog.tsx +++ b/lib/dolce/dialogs/b4-upload-validation-dialog.tsx @@ -39,8 +39,11 @@ interface B4UploadValidationDialogProps { /** * B4 파일명 검증 함수 - * 형식: [버림] [DrawingNo] [RevNo].[확장자] - * 예시: "testfile GTT-DE-007 R01.pdf" → DrawingNo: "GTT-DE-007", RevNo: "R01" + * 형식: [버림] [문서번호토큰1] [문서번호토큰2] ... [리비전번호].[확장자] + * 예시: "testfile GTT DE 007 R01.pdf" → DrawingNo: "GTT-DE-007", RevNo: "R01" + * - 첫 번째 토큰은 버림 + * - 마지막 토큰은 RevNo + * - 중간 토큰들을 "-"로 연결하여 DrawingNo 생성 */ export function validateB4FileName(fileName: string): { valid: boolean; @@ -57,23 +60,25 @@ export function validateB4FileName(fileName: string): { }; } - const extension = fileName.substring(lastDotIndex + 1); const nameWithoutExt = fileName.substring(0, lastDotIndex); // 공백으로 분리 const parts = nameWithoutExt.split(" ").filter(p => p.trim() !== ""); - // 최소 3개 파트 필요: [버림], DrawingNo, RevNo + // 최소 3개 파트 필요: [버림], [문서번호토큰], [RevNo] if (parts.length < 3) { return { valid: false, - error: `공백이 최소 2개 있어야 합니다 (현재: ${parts.length - 1}개). 형식: [버림] [DrawingNo] [RevNo].[확장자]`, + error: `공백이 최소 2개 있어야 합니다 (현재: ${parts.length - 1}개). 형식: [버림] [문서번호토큰들...] [RevNo].[확장자]`, }; } // 첫 번째 토큰은 버림 - const drawingNo = parts[1]; - const revNo = parts[2]; + // 마지막 토큰은 RevNo + // 중간 토큰들을 "-"로 연결하여 DrawingNo 생성 + const revNo = parts[parts.length - 1]; + const drawingTokens = parts.slice(1, parts.length - 1); + const drawingNo = drawingTokens.join("-"); // 필수 항목이 비어있지 않은지 확인 if (!drawingNo || drawingNo.trim() === "") { @@ -307,16 +312,19 @@ export function B4UploadValidationDialog({ 📋 올바른 파일명 형식 </div> <code className="text-xs text-blue-700 dark:text-blue-300"> - [버림] [DrawingNo] [RevNo].[확장자] + [버림] [문서번호토큰1] [문서번호토큰2] ... [RevNo].[확장자] </code> <div className="text-xs text-blue-600 dark:text-blue-400 mt-1"> - 예: testfile GTT-DE-007 R01.pdf + 예: testfile GTT DE 007 R01.pdf → DrawingNo: GTT-DE-007, Rev: R01 </div> <div className="text-xs text-blue-600 dark:text-blue-400 mt-1"> - ※ 첫 번째 단어는 무시되며, 공백으로 구분됩니다 + ※ 첫 번째 단어는 무시됩니다 </div> <div className="text-xs text-blue-600 dark:text-blue-400 mt-1"> - ※ 네 번째 이상의 단어가 있으면 무시됩니다 + ※ 마지막 단어는 리비전 번호(RevNo)입니다 + </div> + <div className="text-xs text-blue-600 dark:text-blue-400 mt-1"> + ※ 중간의 모든 단어는 "-"로 연결되어 문서번호(DrawingNo)가 됩니다 </div> </div> </div> |
