jskim-node #422

Merged
kjs merged 5 commits from jskim-node into main 2026-03-19 09:47:46 +09:00
4 changed files with 27 additions and 7 deletions

4
.gitignore vendored
View File

@ -194,4 +194,6 @@ mcp-task-queue/
# 파이프라인 회고록 (자동 생성)
docs/retrospectives/
mes-architecture-guide.md
mes-architecture-guide.md
# MES Reference Documents
docs/mes-reference/

View File

@ -367,6 +367,20 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
});
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();
try {
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,
detailSettings: col.detailSettings || col.detail_settings,
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);
@ -808,10 +823,12 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
loadSourceColumnLabels();
}, [resolvedSourceTable, isModalMode]);
const repeaterColumnsRef = useRef<RepeaterColumnConfig[]>([]);
// V2ColumnConfig → RepeaterColumnConfig 변환
// 🆕 모든 컬럼을 columns 배열의 순서대로 처리 (isSourceDisplay 플래그로 구분)
const repeaterColumns: RepeaterColumnConfig[] = useMemo(() => {
return config.columns
const cols = config.columns
.filter((col: V2ColumnConfig) => col.visible !== false)
.map((col: V2ColumnConfig): RepeaterColumnConfig => {
const colInfo = currentTableColumnInfo[col.key];
@ -858,12 +875,15 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
type,
editable: col.editable !== false,
width: col.width === "auto" ? undefined : col.width,
required: false,
required: colInfo?.isRequired || false,
categoryRef, // 🆕 카테고리 참조 ID 전달
hidden: col.hidden, // 🆕 히든 처리
autoFill: col.autoFill, // 🆕 자동 입력 설정
};
});
repeaterColumnsRef.current = cols;
return cols;
}, [config.columns, sourceColumnLabels, currentTableColumnInfo, resolvedSourceTable, config.dataSource?.tableName]);
// 리피터 컬럼 설정에서 카테고리 타입 컬럼 자동 감지

View File

@ -792,6 +792,7 @@ export function RepeaterTable({
{/* 컬럼명 - 선택된 옵션라벨 형식으로 표시 */}
<span>
{activeOption?.headerLabel || `${col.label} - ${activeOption?.label || ""}`}
{col.required && <span className="text-destructive ml-1">*</span>}
</span>
<ChevronDown className="h-3 w-3 opacity-60" />
</button>

View File

@ -207,9 +207,6 @@ const FileUploadComponent: React.FC<FileUploadComponentProps> = ({
return;
}
// 등록 모드(새 레코드)일 때는 이전 파일을 로드하지 않음
if (!isRecordMode) return;
const rawValue = String(imageObjidFromFormData);
// 콤마 구분 다중 objid 또는 단일 objid 모두 처리
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);
}
})();
}, [imageObjidFromFormData, columnName, component.id, isRecordMode]);
}, [imageObjidFromFormData, columnName, component.id]);
// 🎯 화면설계 모드에서 실제 화면으로의 실시간 동기화 이벤트 리스너
// 🆕 columnName도 체크하여 같은 화면의 다른 파일 업로드 컴포넌트와 구분