summaryrefslogtreecommitdiff
path: root/lib/swp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/swp')
-rw-r--r--lib/swp/table/swp-table-toolbar.tsx81
-rw-r--r--lib/swp/table/swp-upload-validation-dialog.tsx55
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>