fix: Improve TableListComponent and UniversalFormModalComponent for better data handling

- Updated TableListComponent to use flex-nowrap and overflow-hidden for better badge rendering.
- Enhanced UniversalFormModalComponent to maintain the latest formData using a ref, preventing stale closures during form save events.
- Improved data merging logic in UniversalFormModalComponent to ensure accurate updates and maintain original data integrity.
- Refactored buttonActions to streamline table section data collection and merging, ensuring proper handling of modified and original data during save operations.
This commit is contained in:
kjs
2026-02-25 13:53:20 +09:00
parent 60b1ac1442
commit 38dda2f807
4 changed files with 125 additions and 73 deletions

View File

@@ -247,6 +247,10 @@ export function UniversalFormModalComponent({
// 폼 데이터 상태
const [formData, setFormData] = useState<FormDataState>({});
// formDataRef: 항상 최신 formData를 유지하는 ref
// React 상태 업데이트는 비동기적이므로, handleBeforeFormSave 등에서
// 클로저의 formData가 오래된 값을 참조하는 문제를 방지
const formDataRef = useRef<FormDataState>({});
const [, setOriginalData] = useState<Record<string, any>>({});
// 반복 섹션 데이터
@@ -398,18 +402,19 @@ export function UniversalFormModalComponent({
console.log("[UniversalFormModal] beforeFormSave 이벤트 수신");
console.log("[UniversalFormModal] 설정된 필드 목록:", Array.from(configuredFields));
// formDataRef.current 사용: blur+click 동시 처리 시 클로저의 formData가 오래된 문제 방지
const latestFormData = formDataRef.current;
// 🆕 시스템 필드 병합: id는 설정 여부와 관계없이 항상 전달 (UPDATE/INSERT 판단용)
// - 신규 등록: formData.id가 없으므로 영향 없음
// - 편집 모드: formData.id가 있으면 메인 테이블 UPDATE에 사용
if (formData.id !== undefined && formData.id !== null && formData.id !== "") {
event.detail.formData.id = formData.id;
console.log(`[UniversalFormModal] 시스템 필드 병합: id =`, formData.id);
if (latestFormData.id !== undefined && latestFormData.id !== null && latestFormData.id !== "") {
event.detail.formData.id = latestFormData.id;
console.log(`[UniversalFormModal] 시스템 필드 병합: id =`, latestFormData.id);
}
// UniversalFormModal에 설정된 필드만 병합 (채번 규칙 포함)
// 외부 formData에 이미 값이 있어도 UniversalFormModal 값으로 덮어씀
// (UniversalFormModal이 해당 필드의 주인이므로)
for (const [key, value] of Object.entries(formData)) {
for (const [key, value] of Object.entries(latestFormData)) {
// 설정에 정의된 필드 또는 채번 규칙 ID 필드만 병합
const isConfiguredField = configuredFields.has(key);
const isNumberingRuleId = key.endsWith("_numberingRuleId");
@@ -432,17 +437,13 @@ export function UniversalFormModalComponent({
}
// 🆕 테이블 섹션 데이터 병합 (품목 리스트 등)
// 참고: initializeForm에서 DB 로드 시 __tableSection_ (더블),
// handleTableDataChange에서 수정 시 _tableSection_ (싱글) 사용
for (const [key, value] of Object.entries(formData)) {
// 싱글/더블 언더스코어 모두 처리
// formDataRef.current(= latestFormData) 사용: React 상태 커밋 전에도 최신 데이터 보장
for (const [key, value] of Object.entries(latestFormData)) {
// _tableSection_ 과 __tableSection_ 모두 원본 키 그대로 전달
// buttonActions.ts에서 DB데이터(__tableSection_)와 수정데이터(_tableSection_)를 구분하여 병합
if ((key.startsWith("_tableSection_") || key.startsWith("__tableSection_")) && Array.isArray(value)) {
// 저장 시에는 _tableSection_ 키로 통일 (buttonActions.ts에서 이 키를 기대)
const normalizedKey = key.startsWith("__tableSection_")
? key.replace("__tableSection_", "_tableSection_")
: key;
event.detail.formData[normalizedKey] = value;
console.log(`[UniversalFormModal] 테이블 섹션 병합: ${key}${normalizedKey}, ${value.length}개 항목`);
event.detail.formData[key] = value;
console.log(`[UniversalFormModal] 테이블 섹션 병합: ${key}, ${value.length}개 항목`);
}
// 🆕 원본 테이블 섹션 데이터도 병합 (삭제 추적용)
@@ -457,6 +458,22 @@ export function UniversalFormModalComponent({
event.detail.formData._originalGroupedData = originalGroupedData;
console.log(`[UniversalFormModal] 원본 그룹 데이터 병합: ${originalGroupedData.length}`);
}
// 🆕 부모 formData의 중첩 객체(modalKey)도 최신 데이터로 업데이트
// onChange(setTimeout)가 아직 부모에 전파되지 않았을 수 있으므로 직접 업데이트
for (const parentKey of Object.keys(event.detail.formData)) {
const parentValue = event.detail.formData[parentKey];
if (parentValue && typeof parentValue === "object" && !Array.isArray(parentValue)) {
const hasTableSection = Object.keys(parentValue).some(
(k) => k.startsWith("_tableSection_") || k.startsWith("__tableSection_"),
);
if (hasTableSection) {
event.detail.formData[parentKey] = { ...latestFormData };
console.log(`[UniversalFormModal] 부모 중첩 객체 업데이트: ${parentKey}`);
break;
}
}
}
};
window.addEventListener("beforeFormSave", handleBeforeFormSave as EventListener);
@@ -482,10 +499,11 @@ export function UniversalFormModalComponent({
// 테이블 섹션 데이터 설정
const tableSectionKey = `_tableSection_${tableSection.id}`;
setFormData((prev) => ({
...prev,
[tableSectionKey]: _groupedData,
}));
setFormData((prev) => {
const newData = { ...prev, [tableSectionKey]: _groupedData };
formDataRef.current = newData;
return newData;
});
groupedDataInitializedRef.current = true;
}, [_groupedData, config.sections]);
@@ -965,6 +983,7 @@ export function UniversalFormModalComponent({
}
setFormData(newFormData);
formDataRef.current = newFormData;
setRepeatSections(newRepeatSections);
setCollapsedSections(newCollapsed);
setActivatedOptionalFieldGroups(newActivatedGroups);
@@ -1132,6 +1151,9 @@ export function UniversalFormModalComponent({
console.log(`[연쇄 드롭다운] 부모 ${columnName} 변경 → 자식 ${childField} 초기화`);
}
// ref 즉시 업데이트 (React 상태 커밋 전에도 최신 데이터 접근 가능)
formDataRef.current = newData;
// onChange는 렌더링 외부에서 호출해야 함 (setTimeout 사용)
if (onChange) {
setTimeout(() => onChange(newData), 0);