diff --git a/backend-node/src/services/dynamicFormService.ts b/backend-node/src/services/dynamicFormService.ts index 89d96859..9e0915ee 100644 --- a/backend-node/src/services/dynamicFormService.ts +++ b/backend-node/src/services/dynamicFormService.ts @@ -937,11 +937,17 @@ export class DynamicFormService { }) .join(", "); - // ๐Ÿ†• JSONB ํƒ€์ž… ๊ฐ’์€ JSON ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ + // ๐Ÿ†• JSONB ํƒ€์ž… ๊ฐ’์€ JSON ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜, ๋นˆ ๋ฌธ์ž์—ด์€ null๋กœ ๋ณ€ํ™˜ const values: any[] = Object.keys(changedFields).map((key) => { const value = changedFields[key]; const dataType = columnTypes[key]; + // ๐Ÿ”ง ๋นˆ ๋ฌธ์ž์—ด์€ null๋กœ ๋ณ€ํ™˜ (๋‚ ์งœ ํ•„๋“œ ๋“ฑ์—์„œ ๊ฐ’์„ ์ง€์šธ ๋•Œ ํ•„์š”) + if (value === "" || value === undefined) { + console.log(`๐Ÿ”„ ๋นˆ ๊ฐ’ โ†’ null ๋ณ€ํ™˜: ${key}`); + return null; + } + // JSONB/JSON ํƒ€์ž…์ด๊ณ  ๋ฐฐ์—ด/๊ฐ์ฒด์ธ ๊ฒฝ์šฐ JSON ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ if ( (dataType === "jsonb" || dataType === "json") && diff --git a/frontend/components/screen/EditModal.tsx b/frontend/components/screen/EditModal.tsx index 48bf898f..106787cf 100644 --- a/frontend/components/screen/EditModal.tsx +++ b/frontend/components/screen/EditModal.tsx @@ -1039,8 +1039,15 @@ export const EditModal: React.FC = ({ className }) => { } ); + // ๐Ÿ†• _tableSection_ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ (TableSectionRenderer ์‚ฌ์šฉ ์‹œ) + // _tableSection_ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์œผ๋ฉด buttonActions.ts์˜ handleUniversalFormModalTableSectionSave๊ฐ€ ์ฒ˜๋ฆฌ + const hasTableSectionData = Object.keys(formData).some(k => + k.startsWith("_tableSection_") || k.startsWith("__tableSection_") + ); + // ๐Ÿ†• ๊ทธ๋ฃน ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์œผ๋ฉด EditModal.handleSave ์‚ฌ์šฉ (์ผ๊ด„ ์ €์žฅ) - const shouldUseEditModalSave = groupData.length > 0 || !hasUniversalFormModal; + // ๋‹จ, _tableSection_ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์œผ๋ฉด EditModal.handleSave ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ (buttonActions.ts๊ฐ€ ์ฒ˜๋ฆฌ) + const shouldUseEditModalSave = !hasTableSectionData && (groupData.length > 0 || !hasUniversalFormModal); // ๐Ÿ”‘ ์ฒจ๋ถ€ํŒŒ์ผ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ–‰(๋ ˆ์ฝ”๋“œ) ๋‹จ์œ„๋กœ ํŒŒ์ผ์„ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋„๋ก tableName ์ถ”๊ฐ€ const enrichedFormData = { diff --git a/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx b/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx index 8560db4e..61999ec1 100644 --- a/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx +++ b/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx @@ -951,23 +951,43 @@ export const SplitPanelLayoutComponent: React.FC // ์ถ”๊ฐ€ dataFilter ์ ์šฉ let filteredData = result.data || []; const dataFilter = componentConfig.rightPanel?.dataFilter; - if (dataFilter?.enabled && dataFilter.conditions?.length > 0) { + // ๐Ÿ”ง filters ๋˜๋Š” conditions ๋ฐฐ์—ด ๋ชจ๋‘ ์ง€์› + const filterConditions = dataFilter?.filters || dataFilter?.conditions || []; + if (dataFilter?.enabled && filterConditions.length > 0) { + console.log(`๐Ÿ” [๊ธฐ๋ณธํƒญ] dataFilter ์„ค์ •:`, JSON.stringify(dataFilter, null, 2)); + console.log(`๐Ÿ” [๊ธฐ๋ณธํƒญ] ํ•„ํ„ฐ ์ „ ๋ฐ์ดํ„ฐ ์ˆ˜:`, filteredData.length); filteredData = filteredData.filter((item: any) => { - return dataFilter.conditions.every((cond: any) => { - const value = item[cond.column]; + return filterConditions.every((cond: any) => { + // ๐Ÿ”ง columnName ๋˜๋Š” column ํ•„๋“œ ๋ชจ๋‘ ์ง€์› + const columnName = cond.columnName || cond.column; + const value = item[columnName]; const condValue = cond.value; + let result = true; switch (cond.operator) { case "equals": - return value === condValue; + result = value === condValue; + break; case "notEquals": - return value !== condValue; + result = value !== condValue; + break; case "contains": - return String(value).includes(String(condValue)); + result = String(value).includes(String(condValue)); + break; + case "is_null": + case "NULL": + result = value === null || value === undefined || value === ""; + break; + case "is_not_null": + case "NOT NULL": + result = value !== null && value !== undefined && value !== ""; + break; default: - return true; + result = true; } + return result; }); }); + console.log(`๐Ÿ” [๊ธฐ๋ณธํƒญ] ํ•„ํ„ฐ ํ›„ ๋ฐ์ดํ„ฐ ์ˆ˜:`, filteredData.length); } setRightData(filteredData); @@ -1080,23 +1100,48 @@ export const SplitPanelLayoutComponent: React.FC // ๋ฐ์ดํ„ฐ ํ•„ํ„ฐ ์ ์šฉ const dataFilter = tabConfig.dataFilter; - if (dataFilter?.enabled && dataFilter.conditions?.length > 0) { + console.log(`๐Ÿ” [์ถ”๊ฐ€ํƒญ ${tabIndex}] dataFilter ์„ค์ •:`, JSON.stringify(dataFilter, null, 2)); + // ๐Ÿ”ง filters ๋˜๋Š” conditions ๋ฐฐ์—ด ๋ชจ๋‘ ์ง€์› (DataFilterConfigPanel์€ filters ์‚ฌ์šฉ) + const filterConditions = dataFilter?.filters || dataFilter?.conditions || []; + console.log(`๐Ÿ” [์ถ”๊ฐ€ํƒญ ${tabIndex}] filterConditions:`, filterConditions); + console.log(`๐Ÿ” [์ถ”๊ฐ€ํƒญ ${tabIndex}] ํ•„ํ„ฐ ์ „ ๋ฐ์ดํ„ฐ ์ˆ˜:`, resultData.length); + if (dataFilter?.enabled && filterConditions.length > 0) { + const beforeCount = resultData.length; resultData = resultData.filter((item: any) => { - return dataFilter.conditions.every((cond: any) => { - const value = item[cond.column]; + return filterConditions.every((cond: any) => { + // ๐Ÿ”ง columnName ๋˜๋Š” column ํ•„๋“œ ๋ชจ๋‘ ์ง€์› + const columnName = cond.columnName || cond.column; + const value = item[columnName]; const condValue = cond.value; + let result = true; switch (cond.operator) { case "equals": - return value === condValue; + result = value === condValue; + break; case "notEquals": - return value !== condValue; + result = value !== condValue; + break; case "contains": - return String(value).includes(String(condValue)); + result = String(value).includes(String(condValue)); + break; + case "is_null": + case "NULL": + result = value === null || value === undefined || value === ""; + break; + case "is_not_null": + case "NOT NULL": + result = value !== null && value !== undefined && value !== ""; + break; default: - return true; + result = true; } + console.log(`๐Ÿ” [ํ•„ํ„ฐ ์ฒดํฌ] ${columnName}=${JSON.stringify(value)}, operator=${cond.operator}, result=${result}`); + return result; }); }); + console.log(`๐Ÿ” [์ถ”๊ฐ€ํƒญ ${tabIndex}] ํ•„ํ„ฐ ํ›„ ๋ฐ์ดํ„ฐ ์ˆ˜: ${beforeCount} โ†’ ${resultData.length}`); + } else { + console.log(`๐Ÿ” [์ถ”๊ฐ€ํƒญ ${tabIndex}] ํ•„ํ„ฐ ๋น„ํ™œ์„ฑํ™” ๋˜๋Š” ์กฐ๊ฑด ์—†์Œ (enabled=${dataFilter?.enabled}, conditions=${filterConditions.length})`); } // ์ค‘๋ณต ์ œ๊ฑฐ ์ ์šฉ @@ -1557,6 +1602,7 @@ export const SplitPanelLayoutComponent: React.FC // ์ถ”๊ฐ€ ๋ฒ„ํŠผ ํ•ธ๋“ค๋Ÿฌ const handleAddClick = useCallback( (panel: "left" | "right") => { + console.log("๐Ÿ†• [์ถ”๊ฐ€๋ชจ๋‹ฌ] handleAddClick ํ˜ธ์ถœ:", { panel, activeTabIndex }); setAddModalPanel(panel); // ์šฐ์ธก ํŒจ๋„ ์ถ”๊ฐ€ ์‹œ, ์ขŒ์ธก์—์„œ ์„ ํƒ๋œ ํ•ญ๋ชฉ์˜ ์กฐ์ธ ์ปฌ๋Ÿผ ๊ฐ’์„ ์ž๋™์œผ๋กœ ์ฑ„์›€ @@ -1567,16 +1613,19 @@ export const SplitPanelLayoutComponent: React.FC componentConfig.rightPanel?.rightColumn ) { const leftColumnValue = selectedLeftItem[componentConfig.leftPanel.leftColumn]; - setAddModalFormData({ + const initialData = { [componentConfig.rightPanel.rightColumn]: leftColumnValue, - }); + }; + console.log("๐Ÿ†• [์ถ”๊ฐ€๋ชจ๋‹ฌ] ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ ์„ค์ •:", initialData); + setAddModalFormData(initialData); } else { + console.log("๐Ÿ†• [์ถ”๊ฐ€๋ชจ๋‹ฌ] ๋นˆ ๋ฐ์ดํ„ฐ๋กœ ์ดˆ๊ธฐํ™”"); setAddModalFormData({}); } setShowAddModal(true); }, - [selectedLeftItem, componentConfig], + [selectedLeftItem, componentConfig, activeTabIndex], ); // ์ˆ˜์ • ๋ฒ„ํŠผ ํ•ธ๋“ค๋Ÿฌ @@ -1703,10 +1752,44 @@ export const SplitPanelLayoutComponent: React.FC // ๊ธฐ์กด ์ž๋™ ํŽธ์ง‘ ๋ชจ๋“œ (์ธ๋ผ์ธ ํŽธ์ง‘ ๋ชจ๋‹ฌ) setEditModalPanel(panel); setEditModalItem(item); - setEditModalFormData({ ...item }); + + // ๐Ÿ”ง ์šฐ์ธก ํŒจ๋„(์ถ”๊ฐ€ํƒญ ํฌํ•จ) ์ˆ˜์ • ์‹œ selectedLeftItem์˜ FK ๊ฐ’ ๋ณ‘ํ•ฉ + let mergedItem = { ...item }; + if (panel === "right" && selectedLeftItem) { + // ํ˜„์žฌ ํ™œ์„ฑ ํƒญ์˜ relation ์„ค์ • ๊ฐ€์ ธ์˜ค๊ธฐ + const currentTabConfig = + activeTabIndex === 0 + ? componentConfig.rightPanel + : componentConfig.rightPanel?.additionalTabs?.[activeTabIndex - 1]; + + const relationKeys = currentTabConfig?.relation?.keys; + const leftColumn = currentTabConfig?.relation?.leftColumn; + const rightColumn = currentTabConfig?.relation?.foreignKey || currentTabConfig?.relation?.rightColumn; + + if (relationKeys && relationKeys.length > 0) { + // ๋ณตํ•ฉํ‚ค์ธ ๊ฒฝ์šฐ + relationKeys.forEach((key: any) => { + if (key.leftColumn && key.rightColumn && selectedLeftItem[key.leftColumn] !== undefined) { + // item์— ํ•ด๋‹น FK ๊ฐ’์ด ์—†๊ฑฐ๋‚˜ ๋นˆ ๊ฐ’์ด๋ฉด selectedLeftItem์—์„œ ๊ฐ€์ ธ์˜ด + if (mergedItem[key.rightColumn] === undefined || mergedItem[key.rightColumn] === null || mergedItem[key.rightColumn] === "") { + mergedItem[key.rightColumn] = selectedLeftItem[key.leftColumn]; + } + } + }); + } else if (leftColumn && rightColumn) { + // ๋‹จ์ผํ‚ค์ธ ๊ฒฝ์šฐ + if (selectedLeftItem[leftColumn] !== undefined) { + if (mergedItem[rightColumn] === undefined || mergedItem[rightColumn] === null || mergedItem[rightColumn] === "") { + mergedItem[rightColumn] = selectedLeftItem[leftColumn]; + } + } + } + } + + setEditModalFormData(mergedItem); setShowEditModal(true); }, - [componentConfig, activeTabIndex], + [componentConfig, selectedLeftItem, activeTabIndex], ); // ์ˆ˜์ • ๋ชจ๋‹ฌ ์ €์žฅ diff --git a/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx b/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx index ba206683..ca4d57d0 100644 --- a/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx +++ b/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx @@ -449,6 +449,12 @@ export function UniversalFormModalComponent({ event.detail.formData[normalizedKey] = value; console.log(`[UniversalFormModal] ํ…Œ์ด๋ธ” ์„น์…˜ ๋ณ‘ํ•ฉ: ${key} โ†’ ${normalizedKey}, ${value.length}๊ฐœ ํ•ญ๋ชฉ`); } + + // ๐Ÿ†• ์›๋ณธ ํ…Œ์ด๋ธ” ์„น์…˜ ๋ฐ์ดํ„ฐ๋„ ๋ณ‘ํ•ฉ (์‚ญ์ œ ์ถ”์ ์šฉ) + if (key.startsWith("_originalTableSectionData_") && Array.isArray(value)) { + event.detail.formData[key] = value; + console.log(`[UniversalFormModal] ์›๋ณธ ํ…Œ์ด๋ธ” ์„น์…˜ ๋ฐ์ดํ„ฐ ๋ณ‘ํ•ฉ: ${key}, ${value.length}๊ฐœ ํ•ญ๋ชฉ`); + } } // ๐Ÿ†• ์ˆ˜์ • ๋ชจ๋“œ: ์›๋ณธ ๊ทธ๋ฃน ๋ฐ์ดํ„ฐ ์ „๋‹ฌ (UPDATE/DELETE ์ถ”์ ์šฉ) @@ -938,17 +944,19 @@ export function UniversalFormModalComponent({ newFormData[tableSectionKey] = items; console.log(`[initializeForm] ํ…Œ์ด๋ธ” ์„น์…˜ ${section.id}: formData[${tableSectionKey}]์— ์ €์žฅ๋จ`); - // ๐Ÿ†• ์›๋ณธ ๊ทธ๋ฃน ๋ฐ์ดํ„ฐ ์ €์žฅ (์‚ญ์ œ ์ถ”์ ์šฉ) - // groupedDataInitializedRef๊ฐ€ false์ผ ๋•Œ๋งŒ ์„ค์ • (true๋ฉด _groupedData useEffect์—์„œ ์ด๋ฏธ ์ฒ˜๋ฆฌ๋จ) - // DB์—์„œ ๋กœ๋“œํ•œ ๋ฐ์ดํ„ฐ๋ฅผ originalGroupedData์— ์ €์žฅํ•ด์•ผ ์‚ญ์ œ ์‹œ ๋น„๊ต ๊ฐ€๋Šฅ + // ๐Ÿ†• ํ…Œ์ด๋ธ” ์„น์…˜ ์›๋ณธ ๋ฐ์ดํ„ฐ ์ €์žฅ (์‚ญ์ œ ์ถ”์ ์šฉ) + // ๊ฐ ํ…Œ์ด๋ธ” ์„น์…˜๋ณ„๋กœ ๋ณ„๋„์˜ ํ‚ค์— ์›๋ณธ ๋ฐ์ดํ„ฐ ์ €์žฅ (groupedDataInitializedRef์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ ํ•ญ์ƒ ์ €์žฅ) + const originalTableSectionKey = `_originalTableSectionData_${section.id}`; + newFormData[originalTableSectionKey] = JSON.parse(JSON.stringify(items)); + console.log(`[initializeForm] ํ…Œ์ด๋ธ” ์„น์…˜ ${section.id}: formData[${originalTableSectionKey}]์— ์›๋ณธ ${items.length}๊ฑด ์ €์žฅ`); + + // ๊ธฐ์กด originalGroupedData์—๋„ ์ถ”๊ฐ€ (ํ•˜์œ„ ํ˜ธํ™˜์„ฑ) if (!groupedDataInitializedRef.current) { setOriginalGroupedData((prev) => { const newOriginal = [...prev, ...JSON.parse(JSON.stringify(items))]; console.log(`[initializeForm] ํ…Œ์ด๋ธ” ์„น์…˜ ${section.id}: originalGroupedData์— ${items.length}๊ฑด ์ถ”๊ฐ€ (์ด ${newOriginal.length}๊ฑด)`); return newOriginal; }); - } else { - console.log(`[initializeForm] ํ…Œ์ด๋ธ” ์„น์…˜ ${section.id}: _groupedData๋กœ ์ด๋ฏธ ์ดˆ๊ธฐํ™”๋จ, originalGroupedData ์„ค์ • ์Šคํ‚ต`); } } } catch (error) { diff --git a/frontend/lib/utils/buttonActions.ts b/frontend/lib/utils/buttonActions.ts index edc6861a..32613be4 100644 --- a/frontend/lib/utils/buttonActions.ts +++ b/frontend/lib/utils/buttonActions.ts @@ -522,21 +522,7 @@ export class ButtonActionExecutor { } console.log("โœ… [handleSave] ํ•„์ˆ˜ ํ•ญ๋ชฉ ๊ฒ€์ฆ ํ†ต๊ณผ"); - // ๐Ÿ†• EditModal ๋“ฑ์—์„œ ์ „๋‹ฌ๋œ onSave ์ฝœ๋ฐฑ์ด ์žˆ์œผ๋ฉด ์šฐ์„  ์‚ฌ์šฉ - if (onSave) { - console.log("โœ… [handleSave] onSave ์ฝœ๋ฐฑ ๋ฐœ๊ฒฌ - ์ฝœ๋ฐฑ ์‹คํ–‰"); - try { - await onSave(); - return true; - } catch (error) { - console.error("โŒ [handleSave] onSave ์ฝœ๋ฐฑ ์‹คํ–‰ ์˜ค๋ฅ˜:", error); - throw error; - } - } - - console.log("โš ๏ธ [handleSave] onSave ์ฝœ๋ฐฑ ์—†์Œ - ๊ธฐ๋ณธ ์ €์žฅ ๋กœ์ง ์‹คํ–‰"); - - // ๐Ÿ†• ์ €์žฅ ์ „ ์ด๋ฒคํŠธ ๋ฐœ์ƒ (SelectedItemsDetailInput ๋“ฑ์—์„œ ์ตœ์‹  ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘) + // ๐Ÿ†• ์ €์žฅ ์ „ ์ด๋ฒคํŠธ ๋จผ์ € ๋ฐœ์ƒ (UniversalFormModal์˜ __tableSection_ ๋ฐ์ดํ„ฐ ๋ณ‘ํ•ฉ์„ ์œ„ํ•ด) // context.formData๋ฅผ ์ด๋ฒคํŠธ detail์— ํฌํ•จํ•˜์—ฌ ์ง์ ‘ ์ˆ˜์ • ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•จ // skipDefaultSave ํ”Œ๋ž˜๊ทธ๋ฅผ ํ†ตํ•ด ๊ธฐ๋ณธ ์ €์žฅ ๋กœ์ง์„ ๊ฑด๋„ˆ๋›ธ ์ˆ˜ ์žˆ์Œ const beforeSaveEventDetail = { @@ -554,6 +540,8 @@ export class ButtonActionExecutor { // ์•ฝ๊ฐ„์˜ ๋Œ€๊ธฐ ์‹œ๊ฐ„์„ ์ฃผ์–ด ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ formData๋ฅผ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•จ await new Promise((resolve) => setTimeout(resolve, 100)); + console.log("๐Ÿ“ฆ [handleSave] beforeFormSave ์ด๋ฒคํŠธ ํ›„ formData keys:", Object.keys(context.formData || {})); + // ๊ฒ€์ฆ ์‹คํŒจ ์‹œ ์ €์žฅ ์ค‘๋‹จ if (beforeSaveEventDetail.validationFailed) { console.log("โŒ [handleSave] ๊ฒ€์ฆ ์‹คํŒจ๋กœ ์ €์žฅ ์ค‘๋‹จ:", beforeSaveEventDetail.validationErrors); @@ -566,7 +554,30 @@ export class ButtonActionExecutor { return true; } - console.log("๐Ÿ“ฆ [handleSave] beforeFormSave ์ด๋ฒคํŠธ ํ›„ formData:", context.formData); + // ๐Ÿ†• _tableSection_ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ (TableSectionRenderer ์‚ฌ์šฉ ์‹œ) + // beforeFormSave ์ด๋ฒคํŠธ ํ›„์— ์ฒดํฌํ•ด์•ผ UniversalFormModal์—์„œ ๋ณ‘ํ•ฉ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Œ + const hasTableSectionData = Object.keys(context.formData || {}).some(k => + k.startsWith("_tableSection_") || k.startsWith("__tableSection_") + ); + + if (hasTableSectionData) { + console.log("๐Ÿ“‹ [handleSave] _tableSection_ ๋ฐ์ดํ„ฐ ๊ฐ์ง€ - onSave ์ฝœ๋ฐฑ ๊ฑด๋„ˆ๋›ฐ๊ณ  ํ…Œ์ด๋ธ” ์„น์…˜ ์ €์žฅ ๋กœ์ง ์‚ฌ์šฉ"); + } + + // ๐Ÿ†• EditModal ๋“ฑ์—์„œ ์ „๋‹ฌ๋œ onSave ์ฝœ๋ฐฑ์ด ์žˆ์œผ๋ฉด ์šฐ์„  ์‚ฌ์šฉ + // ๋‹จ, _tableSection_ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์œผ๋ฉด ๊ฑด๋„ˆ๋›ฐ๊ธฐ (handleUniversalFormModalTableSectionSave๊ฐ€ ์ฒ˜๋ฆฌ) + if (onSave && !hasTableSectionData) { + console.log("โœ… [handleSave] onSave ์ฝœ๋ฐฑ ๋ฐœ๊ฒฌ - ์ฝœ๋ฐฑ ์‹คํ–‰ (ํ…Œ์ด๋ธ” ์„น์…˜ ๋ฐ์ดํ„ฐ ์—†์Œ)"); + try { + await onSave(); + return true; + } catch (error) { + console.error("โŒ [handleSave] onSave ์ฝœ๋ฐฑ ์‹คํ–‰ ์˜ค๋ฅ˜:", error); + throw error; + } + } + + console.log("โš ๏ธ [handleSave] ๊ธฐ๋ณธ ์ €์žฅ ๋กœ์ง ์‹คํ–‰ (onSave ์ฝœ๋ฐฑ ์—†์Œ ๋˜๋Š” _tableSection_ ๋ฐ์ดํ„ฐ ์žˆ์Œ)"); // ๐Ÿ†• ๋ ‰ ๊ตฌ์กฐ ์ปดํฌ๋„ŒํŠธ ์ผ๊ด„ ์ €์žฅ ๊ฐ์ง€ let rackStructureLocations: any[] | undefined; @@ -2246,9 +2257,24 @@ export class ButtonActionExecutor { } // 3๏ธโƒฃ ์‚ญ์ œ๋œ ํ’ˆ๋ชฉ DELETE (์›๋ณธ์—๋Š” ์žˆ์ง€๋งŒ ํ˜„์žฌ์—๋Š” ์—†๋Š” ํ•ญ๋ชฉ) + // ๐Ÿ†• ํ…Œ์ด๋ธ” ์„น์…˜๋ณ„ ์›๋ณธ ๋ฐ์ดํ„ฐ ์‚ฌ์šฉ (์šฐ์„ ), ์—†์œผ๋ฉด ์ „์—ญ originalGroupedData ์‚ฌ์šฉ + const sectionOriginalKey = `_originalTableSectionData_${sectionId}`; + const sectionOriginalData: any[] = modalData[sectionOriginalKey] || formData[sectionOriginalKey] || []; + + // ์„น์…˜๋ณ„ ์›๋ณธ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์œผ๋ฉด ์‚ฌ์šฉ, ์—†์œผ๋ฉด ์ „์—ญ originalGroupedData ์‚ฌ์šฉ + const originalDataForDelete = sectionOriginalData.length > 0 ? sectionOriginalData : originalGroupedData; + + console.log(`๐Ÿ” [DELETE ๋น„๊ต] ์„น์…˜ ${sectionId}:`, { + sectionOriginalKey, + sectionOriginalCount: sectionOriginalData.length, + globalOriginalCount: originalGroupedData.length, + usingData: sectionOriginalData.length > 0 ? "์„น์…˜๋ณ„ ์›๋ณธ" : "์ „์—ญ ์›๋ณธ", + currentCount: currentItems.length + }); + // โš ๏ธ id ํƒ€์ž… ํ†ต์ผ: ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ๋น„๊ต (์ˆซ์ž vs ๋ฌธ์ž์—ด ๋ถˆ์ผ์น˜ ๋ฐฉ์ง€) const currentIds = new Set(currentItems.map((item) => String(item.id)).filter(Boolean)); - const deletedItems = originalGroupedData.filter((orig) => orig.id && !currentIds.has(String(orig.id))); + const deletedItems = originalDataForDelete.filter((orig) => orig.id && !currentIds.has(String(orig.id))); for (const deletedItem of deletedItems) { console.log(`๐Ÿ—‘๏ธ [DELETE] ํ’ˆ๋ชฉ ์‚ญ์ œ: id=${deletedItem.id}, tableName=${saveTableName}`);