feat: Enhance V2Repeater and configuration panel with source detail auto-fetching
- Added support for automatic fetching of detail rows from the master data in the V2Repeater component, improving data management. - Introduced a new configuration option in the V2RepeaterConfigPanel to enable source detail auto-fetching, allowing users to specify detail table and foreign key settings. - Enhanced the V2Repeater component to handle entity joins for loading data, optimizing data retrieval processes. - Updated the V2RepeaterProps and V2RepeaterConfig interfaces to include new properties for grouped data and source detail configuration, ensuring type safety and clarity in component usage. - Improved logging for data loading processes to provide better insights during development and debugging.
This commit is contained in:
@@ -224,23 +224,38 @@ export function UniversalFormModalComponent({
|
||||
// 설정 병합
|
||||
const config: UniversalFormModalConfig = useMemo(() => {
|
||||
const componentConfig = component?.config || {};
|
||||
|
||||
// V2 레이아웃에서 overrides 전체가 config로 전달되는 경우
|
||||
// 실제 설정이 propConfig.componentConfig에 이중 중첩되어 있을 수 있음
|
||||
const nestedPropConfig = propConfig?.componentConfig;
|
||||
const hasFlatPropConfig = propConfig?.modal !== undefined || propConfig?.sections !== undefined;
|
||||
const effectivePropConfig = hasFlatPropConfig
|
||||
? propConfig
|
||||
: (nestedPropConfig?.modal ? nestedPropConfig : propConfig);
|
||||
|
||||
const nestedCompConfig = componentConfig?.componentConfig;
|
||||
const hasFlatCompConfig = componentConfig?.modal !== undefined || componentConfig?.sections !== undefined;
|
||||
const effectiveCompConfig = hasFlatCompConfig
|
||||
? componentConfig
|
||||
: (nestedCompConfig?.modal ? nestedCompConfig : componentConfig);
|
||||
|
||||
return {
|
||||
...defaultConfig,
|
||||
...propConfig,
|
||||
...componentConfig,
|
||||
...effectivePropConfig,
|
||||
...effectiveCompConfig,
|
||||
modal: {
|
||||
...defaultConfig.modal,
|
||||
...propConfig?.modal,
|
||||
...componentConfig.modal,
|
||||
...effectivePropConfig?.modal,
|
||||
...effectiveCompConfig?.modal,
|
||||
},
|
||||
saveConfig: {
|
||||
...defaultConfig.saveConfig,
|
||||
...propConfig?.saveConfig,
|
||||
...componentConfig.saveConfig,
|
||||
...effectivePropConfig?.saveConfig,
|
||||
...effectiveCompConfig?.saveConfig,
|
||||
afterSave: {
|
||||
...defaultConfig.saveConfig.afterSave,
|
||||
...propConfig?.saveConfig?.afterSave,
|
||||
...componentConfig.saveConfig?.afterSave,
|
||||
...effectivePropConfig?.saveConfig?.afterSave,
|
||||
...effectiveCompConfig?.saveConfig?.afterSave,
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -295,6 +310,7 @@ export function UniversalFormModalComponent({
|
||||
const hasInitialized = useRef(false);
|
||||
// 마지막으로 초기화된 데이터의 ID를 추적 (수정 모달에서 다른 항목 선택 시 재초기화 필요)
|
||||
const lastInitializedId = useRef<string | undefined>(undefined);
|
||||
const tableSectionLoadedRef = useRef(false);
|
||||
|
||||
// 초기화 - 최초 마운트 시 또는 initialData가 변경되었을 때 실행
|
||||
useEffect(() => {
|
||||
@@ -316,7 +332,7 @@ export function UniversalFormModalComponent({
|
||||
if (hasInitialized.current && lastInitializedId.current === currentIdString) {
|
||||
// 생성 모드에서 데이터가 새로 전달된 경우는 재초기화 필요
|
||||
if (!createModeDataHash || capturedInitialData.current) {
|
||||
// console.log("[UniversalFormModal] 초기화 스킵 - 이미 초기화됨");
|
||||
// console.log("[UniversalFormModal] 초기화 스킵 - 이미 초기화됨", { currentIdString });
|
||||
// 🆕 채번 플래그가 true인데 formData에 값이 없으면 재생성 필요
|
||||
// (컴포넌트 remount로 인해 state가 초기화된 경우)
|
||||
return;
|
||||
@@ -350,21 +366,13 @@ export function UniversalFormModalComponent({
|
||||
// console.log("[UniversalFormModal] 초기 데이터 캡처:", capturedInitialData.current);
|
||||
}
|
||||
|
||||
// console.log("[UniversalFormModal] initializeForm 호출 예정");
|
||||
// console.log("[UniversalFormModal] initializeForm 호출 예정", { currentIdString });
|
||||
hasInitialized.current = true;
|
||||
tableSectionLoadedRef.current = false;
|
||||
initializeForm();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [initialData]); // initialData 전체 변경 시 재초기화
|
||||
|
||||
// config 변경 시에만 재초기화 (initialData 변경은 무시) - 채번규칙 제외
|
||||
useEffect(() => {
|
||||
if (!hasInitialized.current) return; // 최초 초기화 전이면 스킵
|
||||
|
||||
// console.log("[useEffect config 변경] 재초기화 스킵 (채번 중복 방지)");
|
||||
// initializeForm(); // 주석 처리 - config 변경 시 재초기화 안 함 (채번 중복 방지)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [config]);
|
||||
|
||||
// 컴포넌트 unmount 시 채번 플래그 초기화
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
@@ -728,9 +736,13 @@ export function UniversalFormModalComponent({
|
||||
// 🆕 테이블 섹션(type: "table") 디테일 데이터 로드 (마스터-디테일 구조)
|
||||
// 수정 모드일 때 디테일 테이블에서 데이터 가져오기
|
||||
if (effectiveInitialData) {
|
||||
console.log("[initializeForm] 테이블 섹션 디테일 로드 시작", {
|
||||
sectionsCount: config.sections.length,
|
||||
effectiveInitialDataKeys: Object.keys(effectiveInitialData),
|
||||
// console.log("[initializeForm] 테이블 섹션 디테일 로드 시작", { sectionsCount: config.sections.length });
|
||||
|
||||
console.warn("[initializeForm] 테이블 섹션 순회 시작:", {
|
||||
sectionCount: config.sections.length,
|
||||
tableSections: config.sections.filter(s => s.type === "table").map(s => s.id),
|
||||
hasInitialData: !!effectiveInitialData,
|
||||
initialDataKeys: effectiveInitialData ? Object.keys(effectiveInitialData).slice(0, 10) : [],
|
||||
});
|
||||
|
||||
for (const section of config.sections) {
|
||||
@@ -739,16 +751,14 @@ export function UniversalFormModalComponent({
|
||||
}
|
||||
|
||||
const tableConfig = section.tableConfig;
|
||||
// editConfig는 타입에 정의되지 않았지만 런타임에 존재할 수 있음
|
||||
const editConfig = (tableConfig as any).editConfig;
|
||||
const saveConfig = tableConfig.saveConfig;
|
||||
|
||||
console.log(`[initializeForm] 테이블 섹션 ${section.id} 검사:`, {
|
||||
hasEditConfig: !!editConfig,
|
||||
loadOnEdit: editConfig?.loadOnEdit,
|
||||
hasSaveConfig: !!saveConfig,
|
||||
console.warn(`[initializeForm] 테이블 섹션 ${section.id}:`, {
|
||||
editConfig,
|
||||
targetTable: saveConfig?.targetTable,
|
||||
linkColumn: editConfig?.linkColumn,
|
||||
masterField: editConfig?.linkColumn?.masterField,
|
||||
masterValue: effectiveInitialData?.[editConfig?.linkColumn?.masterField],
|
||||
});
|
||||
|
||||
// 수정 모드 로드 설정 확인 (기본값: true)
|
||||
@@ -1073,6 +1083,25 @@ export function UniversalFormModalComponent({
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [config]); // initialData는 의존성에서 제거 (capturedInitialData.current 사용)
|
||||
|
||||
// config 변경 시 테이블 섹션 데이터 로드 보완
|
||||
// initializeForm은 initialData useEffect에서 호출되지만, config(화면 설정)이
|
||||
// 비동기 로드로 늦게 도착하면 테이블 섹션 로드를 놓칠 수 있음
|
||||
useEffect(() => {
|
||||
if (!hasInitialized.current) return;
|
||||
|
||||
const hasTableSection = config.sections.some(s => s.type === "table" && s.tableConfig?.saveConfig?.targetTable);
|
||||
if (!hasTableSection) return;
|
||||
|
||||
const editData = capturedInitialData.current || initialData;
|
||||
if (!editData || Object.keys(editData).length === 0) return;
|
||||
|
||||
if (tableSectionLoadedRef.current) return;
|
||||
|
||||
tableSectionLoadedRef.current = true;
|
||||
initializeForm();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [config.sections, initializeForm]);
|
||||
|
||||
// 반복 섹션 아이템 생성
|
||||
const createRepeatItem = (section: FormSectionConfig, index: number): RepeatSectionItem => {
|
||||
const item: RepeatSectionItem = {
|
||||
|
||||
Reference in New Issue
Block a user