"use client"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Badge } from "@/components/ui/badge"; import { CheckCircle2, XCircle, AlertCircle, Upload } from "lucide-react"; import { ScrollArea } from "@/components/ui/scroll-area"; interface FileValidationResult { file: File; valid: boolean; parsed?: { ownDocNo: string; revNo: string; stage: string; fileName: string; extension: string; }; error?: string; } interface SwpUploadValidationDialogProps { open: boolean; onOpenChange: (open: boolean) => void; validationResults: FileValidationResult[]; onConfirmUpload: (validFiles: File[]) => void; isUploading: boolean; availableDocNos?: string[]; // 업로드 가능한 문서번호 목록 isVendorMode?: boolean; // 벤더 모드인지 여부 (문서번호 검증 필수) } /** * 파일명 검증 함수 (클라이언트 사이드) * 형식: [DOC_NO]_[REV_NO]_[STAGE].[확장자] 또는 [DOC_NO]_[REV_NO]_[STAGE]_[자유-파일명].[확장자] * 자유 파일명은 선택사항이며, 포함될 경우 언더스코어를 포함할 수 있음 * @param fileName 검증할 파일명 * @param availableDocNos 업로드 가능한 문서번호 목록 (선택) * @param isVendorMode 벤더 모드인지 여부 (true인 경우 문서번호 검증 필수) */ export function validateFileName( fileName: string, availableDocNos?: string[], isVendorMode?: boolean ): { valid: boolean; parsed?: { ownDocNo: string; revNo: string; stage: string; fileName: string; extension: string; }; error?: string; } { try { // 확장자 분리 const lastDotIndex = fileName.lastIndexOf("."); if (lastDotIndex === -1) { return { valid: false, error: "파일 확장자가 없습니다", }; } const extension = fileName.substring(lastDotIndex + 1); const nameWithoutExt = fileName.substring(0, lastDotIndex); // 언더스코어로 분리 const parts = nameWithoutExt.split("_"); // 최소 3개 파트 필요: docNo, revNo, stage (fileName은 선택사항) if (parts.length < 3) { return { valid: false, error: `언더스코어(_)가 최소 2개 있어야 합니다 (현재: ${parts.length - 1}개). 형식: [DOC_NO]_[REV_NO]_[STAGE].[확장자]`, }; } // 앞에서부터 3개는 고정: docNo, revNo, stage const ownDocNo = parts[0]; const revNo = parts[1]; const stage = parts[2]; // 나머지는 자유 파일명 (선택사항, 언더스코어 포함 가능) const customFileName = parts.length > 3 ? parts.slice(3).join("_") : ""; // 필수 항목이 비어있지 않은지 확인 if (!ownDocNo || ownDocNo.trim() === "") { return { valid: false, error: "문서번호(DOC_NO)가 비어있습니다", }; } if (!revNo || revNo.trim() === "") { return { valid: false, error: "리비전 번호(REV_NO)가 비어있습니다", }; } if (!stage || stage.trim() === "") { return { valid: false, error: "스테이지(STAGE)가 비어있습니다", }; } // 문서번호 검증 (벤더 모드에서는 필수) if (isVendorMode) { const trimmedDocNo = ownDocNo.trim(); // 벤더 모드에서 문서 목록이 비어있으면 에러 if (!availableDocNos || availableDocNos.length === 0) { return { valid: false, error: "할당된 문서가 없거나 문서 목록 로드에 실패했습니다. 페이지를 새로고침하거나 관리자에게 문의하세요.", }; } // 문서번호가 목록에 없으면 에러 if (!availableDocNos.includes(trimmedDocNo)) { return { valid: false, error: `문서번호 '${trimmedDocNo}'는 업로드 권한이 없습니다. 할당된 문서번호를 확인해주세요.`, }; } } return { valid: true, parsed: { ownDocNo: ownDocNo.trim(), revNo: revNo.trim(), stage: stage.trim(), fileName: customFileName.trim(), extension, }, }; } catch (error) { return { valid: false, error: error instanceof Error ? error.message : "알 수 없는 오류", }; } } /** * 업로드 전 파일 검증 다이얼로그 */ export function SwpUploadValidationDialog({ open, onOpenChange, validationResults, onConfirmUpload, isUploading, availableDocNos = [], isVendorMode = false, }: SwpUploadValidationDialogProps) { const validFiles = validationResults.filter((r) => r.valid); const invalidFiles = validationResults.filter((r) => !r.valid); const handleUpload = () => { if (validFiles.length > 0) { onConfirmUpload(validFiles.map((r) => r.file)); } }; const handleCancel = () => { if (!isUploading) { onOpenChange(false); } }; return ( 파일 업로드 검증 선택한 파일의 파일명 형식을 검증합니다
{/* 요약 통계 */}
전체 파일
{validationResults.length}
검증 성공
{validFiles.length}
검증 실패
{invalidFiles.length}
{/* 경고 메시지 */} {invalidFiles.length > 0 && ( {invalidFiles.length}개 파일의 파일명 형식이 올바르지 않습니다. 검증에 성공한 {validFiles.length}개 파일만 업로드됩니다. )} {validFiles.length === 0 && ( 업로드 가능한 파일이 없습니다. 파일명 형식을 확인해주세요. )} {/* 파일 목록 */}
{/* 검증 성공 파일 */} {validFiles.length > 0 && (

검증 성공 ({validFiles.length}개)

{validFiles.map((result, index) => (
{result.file.name}
{result.parsed && (
문서: {result.parsed.ownDocNo} Rev: {result.parsed.revNo} Stage: {result.parsed.stage} {result.parsed.fileName && ( 파일명: {result.parsed.fileName} )} 확장자: .{result.parsed.extension}
)}
))}
)} {/* 검증 실패 파일 */} {invalidFiles.length > 0 && (

검증 실패 ({invalidFiles.length}개)

{invalidFiles.map((result, index) => (
{result.file.name}
{result.error && (
✗ {result.error}
)}
))}
)}
{/* 형식 안내 */}
올바른 파일명 형식
[DOC_NO]_[REV_NO]_[STAGE].[확장자]
예: VD-DOC-001_01_IFA.pdf
※ 선택사항: [DOC_NO]_[REV_NO]_[STAGE]_[파일명].[확장자] (파일명 추가 가능)
※ 파일명에는 언더스코어(_)가 포함될 수 있습니다.
{isVendorMode && (
{availableDocNos.length > 0 ? ( <>ℹ️ 업로드 가능한 문서: {availableDocNos.length}개 ) : ( <>⚠️ 할당된 문서가 없습니다 )}
)}
); }