fix(UniversalFormModal): 반복 섹션 linkedFieldGroup 매핑 및 서브 테이블 저장 로직 개선

- renderFieldWithColumns()에 repeatContext 파라미터 추가

- linkedFieldGroup 선택 시 repeatContext 유무에 따라 formData/repeatSections 분기 저장

- multiTableSave: UPSERT 대신 SELECT-UPDATE/INSERT 명시적 분기로 변경

- ON CONFLICT 조건 불일치 에러 방지

- 서브 테이블 저장 상세 로그 추가
This commit is contained in:
SeongHyun Kim
2025-12-08 18:23:28 +09:00
parent a278ceca3f
commit b15b6e21ea
2 changed files with 138 additions and 31 deletions

View File

@@ -734,11 +734,54 @@ export function UniversalFormModalComponent({
}
}
// saveMainAsFirst가 활성화된 경우, 메인 데이터를 서브 테이블에 저장하기 위한 매핑 생성
let mainFieldMappings: Array<{ formField: string; targetColumn: string }> | undefined;
if (subTableConfig.options?.saveMainAsFirst) {
mainFieldMappings = [];
// 메인 섹션(비반복)의 필드들을 서브 테이블에 매핑
// 서브 테이블의 fieldMappings에서 targetColumn을 찾아서 매핑
for (const mapping of subTableConfig.fieldMappings || []) {
if (mapping.targetColumn) {
// 메인 데이터에서 동일한 컬럼명이 있으면 매핑
if (mainData[mapping.targetColumn] !== undefined) {
mainFieldMappings.push({
formField: mapping.targetColumn,
targetColumn: mapping.targetColumn,
});
}
// 또는 메인 섹션의 필드 중 같은 이름이 있으면 매핑
else {
config.sections.forEach((section) => {
if (section.repeatable) return;
const matchingField = section.fields.find(f => f.columnName === mapping.targetColumn);
if (matchingField && mainData[matchingField.columnName] !== undefined) {
mainFieldMappings!.push({
formField: matchingField.columnName,
targetColumn: mapping.targetColumn,
});
}
});
}
}
}
// 중복 제거
mainFieldMappings = mainFieldMappings.filter((m, idx, arr) =>
arr.findIndex(x => x.targetColumn === m.targetColumn) === idx
);
console.log("[UniversalFormModal] 메인 필드 매핑 생성:", mainFieldMappings);
}
subTablesData.push({
tableName: subTableConfig.tableName,
linkColumn: subTableConfig.linkColumn,
items: subItems,
options: subTableConfig.options,
options: {
...subTableConfig.options,
mainFieldMappings, // 메인 데이터 매핑 추가
},
});
}
@@ -885,12 +928,14 @@ export function UniversalFormModalComponent({
}, [initializeForm]);
// 필드 요소 렌더링 (입력 컴포넌트만)
// repeatContext: 반복 섹션인 경우 { sectionId, itemId }를 전달
const renderFieldElement = (
field: FormFieldConfig,
value: any,
onChangeHandler: (value: any) => void,
fieldKey: string,
isDisabled: boolean,
repeatContext?: { sectionId: string; itemId: string },
) => {
return (() => {
switch (field.fieldType) {
@@ -969,11 +1014,24 @@ export function UniversalFormModalComponent({
lfg.mappings.forEach((mapping) => {
if (mapping.sourceColumn && mapping.targetColumn) {
const mappedValue = selectedRow[mapping.sourceColumn];
// formData에 직접 저장
setFormData((prev) => ({
...prev,
[mapping.targetColumn]: mappedValue,
}));
// 반복 섹션인 경우 repeatSections에 저장, 아니면 formData에 저장
if (repeatContext) {
setRepeatSections((prev) => {
const items = prev[repeatContext.sectionId] || [];
const newItems = items.map((item) =>
item._id === repeatContext.itemId
? { ...item, [mapping.targetColumn]: mappedValue }
: item
);
return { ...prev, [repeatContext.sectionId]: newItems };
});
} else {
setFormData((prev) => ({
...prev,
[mapping.targetColumn]: mappedValue,
}));
}
}
});
}
@@ -1116,12 +1174,14 @@ export function UniversalFormModalComponent({
};
// 필드 렌더링 (섹션 열 수 적용)
// repeatContext: 반복 섹션인 경우 { sectionId, itemId }를 전달
const renderFieldWithColumns = (
field: FormFieldConfig,
value: any,
onChangeHandler: (value: any) => void,
fieldKey: string,
sectionColumns: number = 2,
repeatContext?: { sectionId: string; itemId: string },
) => {
// 섹션 열 수에 따른 기본 gridSpan 계산 (섹션 열 수가 우선)
const defaultSpan = getDefaultGridSpan(sectionColumns);
@@ -1135,7 +1195,7 @@ export function UniversalFormModalComponent({
return null;
}
const fieldElement = renderFieldElement(field, value, onChangeHandler, fieldKey, isDisabled);
const fieldElement = renderFieldElement(field, value, onChangeHandler, fieldKey, isDisabled, repeatContext);
if (field.fieldType === "checkbox") {
return (
@@ -1275,6 +1335,7 @@ export function UniversalFormModalComponent({
(value) => handleRepeatFieldChange(section.id, item._id, field.columnName, value),
`${section.id}-${item._id}-${field.id}`,
sectionColumns,
{ sectionId: section.id, itemId: item._id }, // 반복 섹션 컨텍스트 전달
),
)}
</div>