diff options
Diffstat (limited to 'lib/swp')
| -rw-r--r-- | lib/swp/table/swp-table-toolbar.tsx | 81 | ||||
| -rw-r--r-- | lib/swp/table/swp-upload-validation-dialog.tsx | 55 |
2 files changed, 119 insertions, 17 deletions
diff --git a/lib/swp/table/swp-table-toolbar.tsx b/lib/swp/table/swp-table-toolbar.tsx index add69666..013b4a13 100644 --- a/lib/swp/table/swp-table-toolbar.tsx +++ b/lib/swp/table/swp-table-toolbar.tsx @@ -19,6 +19,8 @@ import { validateFileName } from "./swp-upload-validation-dialog"; import { SwpUploadedFilesDialog } from "./swp-uploaded-files-dialog"; +import { getProjectDocumentClassStages } from "@/lib/docu-list-rule/document-class/service"; +import type { DocumentListItem } from "@/lib/swp/document-service"; interface SwpTableFilters { docNo?: string; @@ -38,7 +40,7 @@ interface SwpTableToolbarProps { vendorCode?: string; droppedFiles?: File[]; onFilesProcessed?: () => void; - documents?: Array<{ OWN_DOC_NO: string | null }>; // 업로드 권한 검증용 문서 목록 (OWN_DOC_NO 기준) + documents?: DocumentListItem[]; // 업로드 권한 검증 + DOC_TYPE 확인용 문서 목록 userId?: string; // 파일 취소 시 필요 } @@ -80,6 +82,10 @@ export function SwpTableToolbar({ }>>([]); const [showValidationDialog, setShowValidationDialog] = useState(false); + // Document Class-Stage 매핑 (프로젝트별) + const [documentClassStages, setDocumentClassStages] = useState<Record<string, string[]>>({}); + const [isLoadingDocClassStages, setIsLoadingDocClassStages] = useState(false); + /** * 업로드 가능한 문서번호 목록 추출 (OWN_DOC_NO 기준) */ @@ -90,11 +96,62 @@ export function SwpTableToolbar({ }, [documents]); /** + * 문서번호 → DOC_TYPE 매핑 (Stage 검증용) + */ + const docNoToDocTypeMap = useMemo(() => { + const map: Record<string, string> = {}; + for (const doc of documents) { + if (doc.OWN_DOC_NO && doc.DOC_TYPE) { + map[doc.OWN_DOC_NO] = doc.DOC_TYPE; + } + } + return map; + }, [documents]); + + /** * 벤더 모드 여부 (벤더 코드가 있으면 벤더 모드) */ const isVendorMode = !!vendorCode; /** + * 프로젝트 변경 시 Document Class-Stage 매핑 로드 + */ + useEffect(() => { + if (!projNo) { + setDocumentClassStages({}); + return; + } + + let isCancelled = false; + + const loadDocumentClassStages = async () => { + try { + setIsLoadingDocClassStages(true); + const stages = await getProjectDocumentClassStages(projNo); + if (!isCancelled) { + setDocumentClassStages(stages); + console.log(`[SwpTableToolbar] Document Class-Stage 매핑 로드 완료:`, stages); + } + } catch (error) { + if (!isCancelled) { + console.error('[SwpTableToolbar] Document Class-Stage 매핑 로드 실패:', error); + setDocumentClassStages({}); + } + } finally { + if (!isCancelled) { + setIsLoadingDocClassStages(false); + } + } + }; + + loadDocumentClassStages(); + + return () => { + isCancelled = true; + }; + }, [projNo]); + + /** * 드롭된 파일 처리 - useEffect로 감지하여 자동 검증 */ useEffect(() => { @@ -120,9 +177,15 @@ export function SwpTableToolbar({ return; } - // 파일명 검증 (문서번호 권한 포함) + // 파일명 검증 (문서번호 권한 + Stage 검증 포함) const results = droppedFiles.map((file) => { - const validation = validateFileName(file.name, availableDocNos, isVendorMode); + const validation = validateFileName( + file.name, + availableDocNos, + isVendorMode, + docNoToDocTypeMap, + documentClassStages + ); return { file, valid: validation.valid, @@ -135,7 +198,7 @@ export function SwpTableToolbar({ setShowValidationDialog(true); onFilesProcessed?.(); } - }, [droppedFiles, projNo, vendorCode, toast, onFilesProcessed, availableDocNos, isVendorMode]); + }, [droppedFiles, projNo, vendorCode, toast, onFilesProcessed, availableDocNos, isVendorMode, docNoToDocTypeMap, documentClassStages]); /** * 파일 업로드 핸들러 @@ -171,9 +234,15 @@ export function SwpTableToolbar({ return; } - // 각 파일의 파일명 검증 (문서번호 권한 포함) + // 각 파일의 파일명 검증 (문서번호 권한 + Stage 검증 포함) const results = Array.from(selectedFiles).map((file) => { - const validation = validateFileName(file.name, availableDocNos, isVendorMode); + const validation = validateFileName( + file.name, + availableDocNos, + isVendorMode, + docNoToDocTypeMap, + documentClassStages + ); return { file, valid: validation.valid, diff --git a/lib/swp/table/swp-upload-validation-dialog.tsx b/lib/swp/table/swp-upload-validation-dialog.tsx index 8e786c8b..ef48f0c6 100644 --- a/lib/swp/table/swp-upload-validation-dialog.tsx +++ b/lib/swp/table/swp-upload-validation-dialog.tsx @@ -43,11 +43,15 @@ interface SwpUploadValidationDialogProps { * @param fileName 검증할 파일명 * @param availableDocNos 업로드 가능한 문서번호 목록 (선택) * @param isVendorMode 벤더 모드인지 여부 (true인 경우 문서번호 검증 필수) + * @param docNoToDocTypeMap 문서번호 → DOC_TYPE 매핑 (Stage 검증용) + * @param documentClassStages Document Class → 허용 Stage 목록 매핑 */ export function validateFileName( fileName: string, availableDocNos?: string[], - isVendorMode?: boolean + isVendorMode?: boolean, + docNoToDocTypeMap?: Record<string, string>, + documentClassStages?: Record<string, string[]> ): { valid: boolean; parsed?: { @@ -134,12 +138,36 @@ export function validateFileName( } } + // Stage 검증 (DOC_TYPE별 허용 Stage 확인) + const trimmedDocNo = ownDocNo.trim(); + const trimmedStage = stage.trim(); + + if (docNoToDocTypeMap && documentClassStages) { + const docType = docNoToDocTypeMap[trimmedDocNo]; + + if (docType) { + const allowedStages = documentClassStages[docType]; + + if (allowedStages && allowedStages.length > 0) { + // 허용된 Stage 목록이 있는 경우에만 검증 + if (!allowedStages.includes(trimmedStage)) { + return { + valid: false, + error: `문서 '${trimmedDocNo}'의 Document Class '${docType}'에서 Stage '${trimmedStage}'는 허용되지 않습니다. 허용된 Stage: ${allowedStages.join(", ")}`, + }; + } + } + // allowedStages가 비어있으면 Stage 검증을 스킵 (설정되지 않은 경우) + } + // docType이 없으면 Stage 검증을 스킵 (문서 정보가 없는 경우) + } + return { valid: true, parsed: { - ownDocNo: ownDocNo.trim(), + ownDocNo: trimmedDocNo, revNo: revNo.trim(), - stage: stage.trim(), + stage: trimmedStage, fileName: customFileName.trim(), extension, }, @@ -314,7 +342,7 @@ export function SwpUploadValidationDialog({ {/* 형식 안내 */} <div className="rounded-lg bg-blue-50 dark:bg-blue-950/30 border border-blue-200 dark:border-blue-800 p-3"> <div className="text-sm font-medium text-blue-900 dark:text-blue-100 mb-1"> - 올바른 파일명 형식 + 📋 올바른 파일명 형식 </div> <code className="text-xs text-blue-700 dark:text-blue-300"> [OWN_DOC_NO]_[REV_NO]_[STAGE].[확장자] @@ -329,13 +357,18 @@ export function SwpUploadValidationDialog({ ※ 파일명에는 언더스코어(_)가 포함될 수 있습니다. </div> {isVendorMode && ( - <div className="text-xs text-blue-600 dark:text-blue-400 mt-2 pt-2 border-t border-blue-200 dark:border-blue-800"> - {availableDocNos.length > 0 ? ( - <>ℹ️ 업로드 가능한 문서: {availableDocNos.length}개</> - ) : ( - <>⚠️ 할당된 문서가 없습니다</> - )} - </div> + <> + <div className="text-xs text-blue-600 dark:text-blue-400 mt-2 pt-2 border-t border-blue-200 dark:border-blue-800"> + {availableDocNos.length > 0 ? ( + <>ℹ️ 업로드 가능한 문서: {availableDocNos.length}개</> + ) : ( + <>⚠️ 할당된 문서가 없습니다</> + )} + </div> + <div className="text-xs text-blue-600 dark:text-blue-400 mt-1"> + ⚠️ 각 문서의 Document Class에 정의된 Stage만 사용할 수 있습니다. + </div> + </> )} </div> </div> |
