Merge pull request 'jskim-node' (#422) from jskim-node into main
Reviewed-on: http://39.117.244.52:3000/kjs/ERP-node/pulls/422
This commit is contained in:
commit
e42932d76c
|
|
@ -194,4 +194,6 @@ mcp-task-queue/
|
||||||
|
|
||||||
# 파이프라인 회고록 (자동 생성)
|
# 파이프라인 회고록 (자동 생성)
|
||||||
docs/retrospectives/
|
docs/retrospectives/
|
||||||
mes-architecture-guide.md
|
mes-architecture-guide.md
|
||||||
|
# MES Reference Documents
|
||||||
|
docs/mes-reference/
|
||||||
|
|
|
||||||
|
|
@ -367,6 +367,20 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// 🆕 필수값 검증
|
||||||
|
const requiredColumns = repeaterColumnsRef.current.filter(col => col.required);
|
||||||
|
for (let i = 0; i < currentData.length; i++) {
|
||||||
|
const row = currentData[i];
|
||||||
|
for (const col of requiredColumns) {
|
||||||
|
const val = row[col.field];
|
||||||
|
if (val === undefined || val === null || val === "") {
|
||||||
|
toast.error(`${i + 1}번째 행의 '${col.label}'은(는) 필수 입력 항목입니다.`);
|
||||||
|
window.dispatchEvent(new CustomEvent("repeaterSaveComplete"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let validColumns: Set<string> = new Set();
|
let validColumns: Set<string> = new Set();
|
||||||
try {
|
try {
|
||||||
const columnsResponse = await apiClient.get(`/table-management/tables/${tableName}/columns`);
|
const columnsResponse = await apiClient.get(`/table-management/tables/${tableName}/columns`);
|
||||||
|
|
@ -706,6 +720,7 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||||
displayName: col.displayName || col.display_name || col.label || name,
|
displayName: col.displayName || col.display_name || col.label || name,
|
||||||
detailSettings: col.detailSettings || col.detail_settings,
|
detailSettings: col.detailSettings || col.detail_settings,
|
||||||
categoryRef: typeInfo?.categoryRef || null,
|
categoryRef: typeInfo?.categoryRef || null,
|
||||||
|
isRequired: col.isNullable === 'NO' || col.is_nullable === 'NO' || col.isRequired || col.is_required || col.notNull || col.not_null === true || col.not_null === 'Y' || col.not_null === 'y',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
setCurrentTableColumnInfo(columnMap);
|
setCurrentTableColumnInfo(columnMap);
|
||||||
|
|
@ -808,10 +823,12 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||||
loadSourceColumnLabels();
|
loadSourceColumnLabels();
|
||||||
}, [resolvedSourceTable, isModalMode]);
|
}, [resolvedSourceTable, isModalMode]);
|
||||||
|
|
||||||
|
const repeaterColumnsRef = useRef<RepeaterColumnConfig[]>([]);
|
||||||
|
|
||||||
// V2ColumnConfig → RepeaterColumnConfig 변환
|
// V2ColumnConfig → RepeaterColumnConfig 변환
|
||||||
// 🆕 모든 컬럼을 columns 배열의 순서대로 처리 (isSourceDisplay 플래그로 구분)
|
// 🆕 모든 컬럼을 columns 배열의 순서대로 처리 (isSourceDisplay 플래그로 구분)
|
||||||
const repeaterColumns: RepeaterColumnConfig[] = useMemo(() => {
|
const repeaterColumns: RepeaterColumnConfig[] = useMemo(() => {
|
||||||
return config.columns
|
const cols = config.columns
|
||||||
.filter((col: V2ColumnConfig) => col.visible !== false)
|
.filter((col: V2ColumnConfig) => col.visible !== false)
|
||||||
.map((col: V2ColumnConfig): RepeaterColumnConfig => {
|
.map((col: V2ColumnConfig): RepeaterColumnConfig => {
|
||||||
const colInfo = currentTableColumnInfo[col.key];
|
const colInfo = currentTableColumnInfo[col.key];
|
||||||
|
|
@ -858,12 +875,15 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||||
type,
|
type,
|
||||||
editable: col.editable !== false,
|
editable: col.editable !== false,
|
||||||
width: col.width === "auto" ? undefined : col.width,
|
width: col.width === "auto" ? undefined : col.width,
|
||||||
required: false,
|
required: colInfo?.isRequired || false,
|
||||||
categoryRef, // 🆕 카테고리 참조 ID 전달
|
categoryRef, // 🆕 카테고리 참조 ID 전달
|
||||||
hidden: col.hidden, // 🆕 히든 처리
|
hidden: col.hidden, // 🆕 히든 처리
|
||||||
autoFill: col.autoFill, // 🆕 자동 입력 설정
|
autoFill: col.autoFill, // 🆕 자동 입력 설정
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
repeaterColumnsRef.current = cols;
|
||||||
|
return cols;
|
||||||
}, [config.columns, sourceColumnLabels, currentTableColumnInfo, resolvedSourceTable, config.dataSource?.tableName]);
|
}, [config.columns, sourceColumnLabels, currentTableColumnInfo, resolvedSourceTable, config.dataSource?.tableName]);
|
||||||
|
|
||||||
// 리피터 컬럼 설정에서 카테고리 타입 컬럼 자동 감지
|
// 리피터 컬럼 설정에서 카테고리 타입 컬럼 자동 감지
|
||||||
|
|
|
||||||
|
|
@ -792,6 +792,7 @@ export function RepeaterTable({
|
||||||
{/* 컬럼명 - 선택된 옵션라벨 형식으로 표시 */}
|
{/* 컬럼명 - 선택된 옵션라벨 형식으로 표시 */}
|
||||||
<span>
|
<span>
|
||||||
{activeOption?.headerLabel || `${col.label} - ${activeOption?.label || ""}`}
|
{activeOption?.headerLabel || `${col.label} - ${activeOption?.label || ""}`}
|
||||||
|
{col.required && <span className="text-destructive ml-1">*</span>}
|
||||||
</span>
|
</span>
|
||||||
<ChevronDown className="h-3 w-3 opacity-60" />
|
<ChevronDown className="h-3 w-3 opacity-60" />
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -207,9 +207,6 @@ const FileUploadComponent: React.FC<FileUploadComponentProps> = ({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 등록 모드(새 레코드)일 때는 이전 파일을 로드하지 않음
|
|
||||||
if (!isRecordMode) return;
|
|
||||||
|
|
||||||
const rawValue = String(imageObjidFromFormData);
|
const rawValue = String(imageObjidFromFormData);
|
||||||
// 콤마 구분 다중 objid 또는 단일 objid 모두 처리
|
// 콤마 구분 다중 objid 또는 단일 objid 모두 처리
|
||||||
const objids = rawValue.split(',').map(s => s.trim()).filter(s => /^\d+$/.test(s));
|
const objids = rawValue.split(',').map(s => s.trim()).filter(s => /^\d+$/.test(s));
|
||||||
|
|
@ -265,7 +262,7 @@ const FileUploadComponent: React.FC<FileUploadComponentProps> = ({
|
||||||
console.error("🖼️ [FileUploadComponent] 파일 정보 조회 오류:", error);
|
console.error("🖼️ [FileUploadComponent] 파일 정보 조회 오류:", error);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
}, [imageObjidFromFormData, columnName, component.id, isRecordMode]);
|
}, [imageObjidFromFormData, columnName, component.id]);
|
||||||
|
|
||||||
// 🎯 화면설계 모드에서 실제 화면으로의 실시간 동기화 이벤트 리스너
|
// 🎯 화면설계 모드에서 실제 화면으로의 실시간 동기화 이벤트 리스너
|
||||||
// 🆕 columnName도 체크하여 같은 화면의 다른 파일 업로드 컴포넌트와 구분
|
// 🆕 columnName도 체크하여 같은 화면의 다른 파일 업로드 컴포넌트와 구분
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue