diff --git a/frontend/components/screen/config-panels/ButtonConfigPanel.tsx b/frontend/components/screen/config-panels/ButtonConfigPanel.tsx index 99986cf7..93b9297f 100644 --- a/frontend/components/screen/config-panels/ButtonConfigPanel.tsx +++ b/frontend/components/screen/config-panels/ButtonConfigPanel.tsx @@ -101,6 +101,17 @@ export const ButtonConfigPanel: React.FC = ({ const [modalSourceSearch, setModalSourceSearch] = useState>({}); const [modalTargetSearch, setModalTargetSearch] = useState>({}); + // ๐Ÿ†• modal ์•ก์…˜์šฉ ํ•„๋“œ ๋งคํ•‘ ์ƒํƒœ + const [modalActionSourceTable, setModalActionSourceTable] = useState(null); + const [modalActionTargetTable, setModalActionTargetTable] = useState(null); + const [modalActionSourceColumns, setModalActionSourceColumns] = useState>([]); + const [modalActionTargetColumns, setModalActionTargetColumns] = useState>([]); + const [modalActionFieldMappings, setModalActionFieldMappings] = useState>([]); + const [modalFieldMappingSourceOpen, setModalFieldMappingSourceOpen] = useState>({}); + const [modalFieldMappingTargetOpen, setModalFieldMappingTargetOpen] = useState>({}); + const [modalFieldMappingSourceSearch, setModalFieldMappingSourceSearch] = useState>({}); + const [modalFieldMappingTargetSearch, setModalFieldMappingTargetSearch] = useState>({}); + // ๐ŸŽฏ ํ”Œ๋กœ์šฐ ์œ„์ ฏ์ด ํ™”๋ฉด์— ์žˆ๋Š”์ง€ ํ™•์ธ const hasFlowWidget = useMemo(() => { const found = allComponents.some((comp: any) => { @@ -328,6 +339,123 @@ export const ButtonConfigPanel: React.FC = ({ loadColumns(); }, [config.action?.dataTransfer?.sourceTable, config.action?.dataTransfer?.targetTable]); + // ๐Ÿ†• modal ์•ก์…˜: ๋Œ€์ƒ ํ™”๋ฉด ํ…Œ์ด๋ธ” ์กฐํšŒ ๋ฐ ํ•„๋“œ ๋งคํ•‘ ๋กœ๋“œ + useEffect(() => { + const actionType = config.action?.type; + if (actionType !== "modal") return; + + const autoDetect = config.action?.autoDetectDataSource; + if (!autoDetect) { + // ๋ฐ์ดํ„ฐ ์ „๋‹ฌ์ด ๋น„ํ™œ์„ฑํ™”๋˜๋ฉด ์ƒํƒœ ์ดˆ๊ธฐํ™” + setModalActionSourceTable(null); + setModalActionTargetTable(null); + setModalActionSourceColumns([]); + setModalActionTargetColumns([]); + return; + } + + const targetScreenId = config.action?.targetScreenId; + if (!targetScreenId) return; + + const loadModalActionMappingData = async () => { + // 1. ์†Œ์Šค ํ…Œ์ด๋ธ” ๊ฐ์ง€ (ํ˜„์žฌ ํ™”๋ฉด) + let sourceTableName: string | null = currentTableName || null; + + // allComponents์—์„œ ๋ถ„ํ• ํŒจ๋„/ํ…Œ์ด๋ธ”๋ฆฌ์ŠคํŠธ/ํ†ตํ•ฉ๋ชฉ๋ก ๊ฐ์ง€ + for (const comp of allComponents) { + const compType = comp.componentType || (comp as any).componentConfig?.type; + const compConfig = (comp as any).componentConfig || {}; + + if (compType === "split-panel-layout" || compType === "screen-split-panel") { + sourceTableName = compConfig.leftPanel?.tableName || compConfig.tableName || null; + if (sourceTableName) break; + } + if (compType === "table-list") { + sourceTableName = compConfig.tableName || compConfig.selectedTable || null; + if (sourceTableName) break; + } + if (compType === "unified-list") { + sourceTableName = compConfig.dataSource?.table || compConfig.tableName || null; + if (sourceTableName) break; + } + } + + setModalActionSourceTable(sourceTableName); + + // 2. ๋Œ€์ƒ ํ™”๋ฉด์˜ ํ…Œ์ด๋ธ” ์กฐํšŒ + let targetTableName: string | null = null; + try { + const screenResponse = await apiClient.get(`/screen-management/screens/${targetScreenId}`); + if (screenResponse.data.success && screenResponse.data.data) { + targetTableName = screenResponse.data.data.tableName || null; + } else if (screenResponse.data?.tableName) { + // ์ง์ ‘ ๋ฐ์ดํ„ฐ ๋ฐ˜ํ™˜ ํ˜•์‹์ธ ๊ฒฝ์šฐ + targetTableName = screenResponse.data.tableName || null; + } + } catch (error) { + console.error("๋Œ€์ƒ ํ™”๋ฉด ์ •๋ณด ๋กœ๋“œ ์‹คํŒจ:", error); + } + + setModalActionTargetTable(targetTableName); + + // 3. ์†Œ์Šค ํ…Œ์ด๋ธ” ์ปฌ๋Ÿผ ๋กœ๋“œ + if (sourceTableName) { + try { + const response = await apiClient.get(`/table-management/tables/${sourceTableName}/columns`); + if (response.data.success) { + let columnData = response.data.data; + if (!Array.isArray(columnData) && columnData?.columns) columnData = columnData.columns; + if (!Array.isArray(columnData) && columnData?.data) columnData = columnData.data; + + if (Array.isArray(columnData)) { + const columns = columnData.map((col: any) => ({ + name: col.name || col.columnName, + label: col.displayName || col.label || col.columnLabel || col.name || col.columnName, + })); + setModalActionSourceColumns(columns); + } + } + } catch (error) { + console.error("์†Œ์Šค ํ…Œ์ด๋ธ” ์ปฌ๋Ÿผ ๋กœ๋“œ ์‹คํŒจ:", error); + } + } + + // 4. ๋Œ€์ƒ ํ…Œ์ด๋ธ” ์ปฌ๋Ÿผ ๋กœ๋“œ + if (targetTableName) { + try { + const response = await apiClient.get(`/table-management/tables/${targetTableName}/columns`); + if (response.data.success) { + let columnData = response.data.data; + if (!Array.isArray(columnData) && columnData?.columns) columnData = columnData.columns; + if (!Array.isArray(columnData) && columnData?.data) columnData = columnData.data; + + if (Array.isArray(columnData)) { + const columns = columnData.map((col: any) => ({ + name: col.name || col.columnName, + label: col.displayName || col.label || col.columnLabel || col.name || col.columnName, + })); + setModalActionTargetColumns(columns); + } + } + } catch (error) { + console.error("๋Œ€์ƒ ํ…Œ์ด๋ธ” ์ปฌ๋Ÿผ ๋กœ๋“œ ์‹คํŒจ:", error); + } + } + + // 5. ๊ธฐ์กด ํ•„๋“œ ๋งคํ•‘ ๋กœ๋“œ ๋˜๋Š” ์ž๋™ ๋งคํ•‘ ์ƒ์„ฑ + const existingMappings = config.action?.fieldMappings || []; + if (existingMappings.length > 0) { + setModalActionFieldMappings(existingMappings); + } else if (sourceTableName && targetTableName && sourceTableName === targetTableName) { + // ํ…Œ์ด๋ธ”์ด ๊ฐ™์œผ๋ฉด ์ž๋™ ๋งคํ•‘ (๋™์ผ ์ปฌ๋Ÿผ๋ช…) + setModalActionFieldMappings([]); // ๋นˆ ๋ฐฐ์—ด = ์ž๋™ ๋งคํ•‘ + } + }; + + loadModalActionMappingData(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [config.action?.type, config.action?.autoDetectDataSource, config.action?.targetScreenId, currentTableName, allComponents]); + // ๐Ÿ†• openModalWithData ์†Œ์Šค/ํƒ€๊ฒŸ ํ…Œ์ด๋ธ” ์ปฌ๋Ÿผ ๋กœ๋“œ useEffect(() => { const actionType = config.action?.type; @@ -791,6 +919,10 @@ export const ButtonConfigPanel: React.FC = ({ checked={component.componentConfig?.action?.autoDetectDataSource === true} onCheckedChange={(checked) => { onUpdateProperty("componentConfig.action.autoDetectDataSource", checked); + if (!checked) { + // ์ฒดํฌ ํ•ด์ œ ์‹œ ํ•„๋“œ ๋งคํ•‘๋„ ์ดˆ๊ธฐํ™” + onUpdateProperty("componentConfig.action.fieldMappings", []); + } }} />
@@ -802,6 +934,192 @@ export const ButtonConfigPanel: React.FC = ({

+ + {/* ๐Ÿ†• ํ•„๋“œ ๋งคํ•‘ UI (๋ฐ์ดํ„ฐ ์ „๋‹ฌ ํ™œ์„ฑํ™” + ํ…Œ์ด๋ธ”์ด ๋‹ค๋ฅธ ๊ฒฝ์šฐ) */} + {component.componentConfig?.action?.autoDetectDataSource === true && ( +
+ {/* ํ…Œ์ด๋ธ” ์ •๋ณด ํ‘œ์‹œ */} +
+
+ + ์†Œ์Šค: + {modalActionSourceTable || "๊ฐ์ง€ ์ค‘..."} +
+ โ†’ +
+ ๋Œ€์ƒ: + {modalActionTargetTable || "๊ฐ์ง€ ์ค‘..."} +
+
+ + {/* ํ…Œ์ด๋ธ”์ด ๊ฐ™์œผ๋ฉด ์ž๋™ ๋งคํ•‘ ์•ˆ๋‚ด */} + {modalActionSourceTable && modalActionTargetTable && modalActionSourceTable === modalActionTargetTable && ( +
+ ๋™์ผํ•œ ํ…Œ์ด๋ธ”์ž…๋‹ˆ๋‹ค. ์ปฌ๋Ÿผ๋ช…์ด ๊ฐ™์€ ํ•„๋“œ๋Š” ์ž๋™์œผ๋กœ ๋งคํ•‘๋ฉ๋‹ˆ๋‹ค. +
+ )} + + {/* ํ…Œ์ด๋ธ”์ด ๋‹ค๋ฅด๋ฉด ํ•„๋“œ ๋งคํ•‘ UI ํ‘œ์‹œ */} + {modalActionSourceTable && modalActionTargetTable && modalActionSourceTable !== modalActionTargetTable && ( +
+
+ + +
+ + {(component.componentConfig?.action?.fieldMappings || []).length === 0 && ( +

+ ์ปฌ๋Ÿผ๋ช…์ด ๋‹ค๋ฅธ ๊ฒฝ์šฐ ๋งคํ•‘์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”. ๋งคํ•‘์ด ์—†์œผ๋ฉด ๋™์ผ ์ปฌ๋Ÿผ๋ช…๋งŒ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค. +

+ )} + + {(component.componentConfig?.action?.fieldMappings || []).map((mapping: any, index: number) => ( +
+ {/* ์†Œ์Šค ํ•„๋“œ ์„ ํƒ */} + setModalFieldMappingSourceOpen((prev) => ({ ...prev, [index]: open }))} + > + + + + + + setModalFieldMappingSourceSearch((prev) => ({ ...prev, [index]: val }))} + /> + + ์ปฌ๋Ÿผ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. + + {modalActionSourceColumns + .filter((col) => + col.name.toLowerCase().includes((modalFieldMappingSourceSearch[index] || "").toLowerCase()) || + col.label.toLowerCase().includes((modalFieldMappingSourceSearch[index] || "").toLowerCase()) + ) + .map((col) => ( + { + const newMappings = [...(component.componentConfig?.action?.fieldMappings || [])]; + newMappings[index] = { ...newMappings[index], sourceField: col.name }; + setModalActionFieldMappings(newMappings); + onUpdateProperty("componentConfig.action.fieldMappings", newMappings); + setModalFieldMappingSourceOpen((prev) => ({ ...prev, [index]: false })); + }} + > + +
+ {col.label} + {col.name} +
+
+ ))} +
+
+
+
+
+ + โ†’ + + {/* ๋Œ€์ƒ ํ•„๋“œ ์„ ํƒ */} + setModalFieldMappingTargetOpen((prev) => ({ ...prev, [index]: open }))} + > + + + + + + setModalFieldMappingTargetSearch((prev) => ({ ...prev, [index]: val }))} + /> + + ์ปฌ๋Ÿผ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. + + {modalActionTargetColumns + .filter((col) => + col.name.toLowerCase().includes((modalFieldMappingTargetSearch[index] || "").toLowerCase()) || + col.label.toLowerCase().includes((modalFieldMappingTargetSearch[index] || "").toLowerCase()) + ) + .map((col) => ( + { + const newMappings = [...(component.componentConfig?.action?.fieldMappings || [])]; + newMappings[index] = { ...newMappings[index], targetField: col.name }; + setModalActionFieldMappings(newMappings); + onUpdateProperty("componentConfig.action.fieldMappings", newMappings); + setModalFieldMappingTargetOpen((prev) => ({ ...prev, [index]: false })); + }} + > + +
+ {col.label} + {col.name} +
+
+ ))} +
+
+
+
+
+ + {/* ์‚ญ์ œ ๋ฒ„ํŠผ */} + +
+ ))} +
+ )} +
+ )} )} diff --git a/frontend/lib/registry/components/table-list/TableListComponent.tsx b/frontend/lib/registry/components/table-list/TableListComponent.tsx index b00f3e86..e3bb34fd 100644 --- a/frontend/lib/registry/components/table-list/TableListComponent.tsx +++ b/frontend/lib/registry/components/table-list/TableListComponent.tsx @@ -2069,10 +2069,6 @@ export const TableListComponent: React.FC = ({ } // ๐Ÿ†• modalDataStore์— ์„ ํƒ๋œ ๋ฐ์ดํ„ฐ ์ž๋™ ์ €์žฅ (ํ…Œ์ด๋ธ”๋ช… ๊ธฐ๋ฐ˜ dataSourceId) - console.log("๐Ÿ” [TableList] modalDataStore ์ €์žฅ ์กฐ๊ฑด:", { - selectedTable: tableConfig.selectedTable, - selectedRowsCount: selectedRowsData.length, - }); if (tableConfig.selectedTable && selectedRowsData.length > 0) { import("@/stores/modalDataStore").then(({ useModalDataStore }) => { const modalItems = selectedRowsData.map((row, idx) => ({ @@ -2080,11 +2076,6 @@ export const TableListComponent: React.FC = ({ originalData: row, additionalData: {}, })); - - console.log("โœ… [TableList] modalDataStore์— ๋ฐ์ดํ„ฐ ์ €์žฅ:", { - sourceId: tableConfig.selectedTable, - itemCount: modalItems.length, - }); useModalDataStore.getState().setData(tableConfig.selectedTable!, modalItems); }); } else if (tableConfig.selectedTable && selectedRowsData.length === 0) { @@ -2092,8 +2083,6 @@ export const TableListComponent: React.FC = ({ import("@/stores/modalDataStore").then(({ useModalDataStore }) => { useModalDataStore.getState().clearData(tableConfig.selectedTable!); }); - } else if (!tableConfig.selectedTable) { - console.warn("โš ๏ธ [TableList] selectedTable์ด ์—†์–ด modalDataStore์— ์ €์žฅํ•˜์ง€ ์•Š์Œ"); } const allRowsSelected = filteredData.every((row, index) => newSelectedRows.has(getRowKey(row, index))); diff --git a/frontend/lib/utils/buttonActions.ts b/frontend/lib/utils/buttonActions.ts index 7a302320..023be684 100644 --- a/frontend/lib/utils/buttonActions.ts +++ b/frontend/lib/utils/buttonActions.ts @@ -2629,25 +2629,10 @@ export class ButtonActionExecutor { dataSourceId = dataSourceId || context.tableName || "default"; - console.log("๐Ÿ” [handleModal] ๋ฐ์ดํ„ฐ ์กฐํšŒ ์‹œ๋„:", { - dataSourceId, - autoDetectDataSource, - hasAllComponents: !!context.allComponents, - allComponentsCount: context.allComponents?.length, - }); - // modalDataStore์—์„œ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ try { const { useModalDataStore } = await import("@/stores/modalDataStore"); const dataRegistry = useModalDataStore.getState().dataRegistry; - - console.log("๐Ÿ“ฆ [handleModal] modalDataStore ์ƒํƒœ:", { - registryKeys: Object.keys(dataRegistry), - targetKey: dataSourceId, - hasData: !!dataRegistry[dataSourceId], - dataLength: dataRegistry[dataSourceId]?.length || 0, - }); - const modalData = dataRegistry[dataSourceId] || []; if (modalData.length === 0) { @@ -2658,15 +2643,19 @@ export class ButtonActionExecutor { selectedData = modalData.map((item: any) => item.originalData || item); const rawParentData = modalData[0]?.originalData || modalData[0] || {}; - parentData = { ...rawParentData }; // ํ•„๋“œ ๋งคํ•‘ ์ ์šฉ if (config.fieldMappings?.length) { + // ๋งคํ•‘์ด ์žˆ์œผ๋ฉด ๋งคํ•‘๋œ ํ•„๋“œ๋งŒ ์ „๋‹ฌ + parentData = {}; config.fieldMappings.forEach((mapping) => { if (mapping.sourceField && mapping.targetField && rawParentData[mapping.sourceField] !== undefined) { parentData[mapping.targetField] = rawParentData[mapping.sourceField]; } }); + } else { + // ๋งคํ•‘์ด ์—†์œผ๋ฉด ๋ชจ๋“  ํ•„๋“œ ์ „๋‹ฌ (๋™์ผ ์ปฌ๋Ÿผ๋ช… ์ž๋™ ๋งคํ•‘) + parentData = { ...rawParentData }; } } catch (error) { console.error("โŒ ๋ฐ์ดํ„ฐ ํ™•์ธ ์‹คํŒจ:", error); @@ -2676,16 +2665,20 @@ export class ButtonActionExecutor { } else { // 2-2. ๊ธฐ๋ณธ ๋ชจ๋“œ: context์—์„œ ์ง์ ‘ ๊ฐ€์ ธ์˜ค๊ธฐ selectedData = context.selectedRowsData || []; - parentData = context.splitPanelParentData || {}; + const rawParentData = context.splitPanelParentData || selectedData[0] || {}; // ํ•„๋“œ ๋งคํ•‘ ์ ์šฉ if (config.fieldMappings?.length && selectedData.length > 0) { - const rawParentData = selectedData[0] || {}; + // ๋งคํ•‘์ด ์žˆ์œผ๋ฉด ๋งคํ•‘๋œ ํ•„๋“œ๋งŒ ์ „๋‹ฌ + parentData = {}; config.fieldMappings.forEach((mapping) => { if (mapping.sourceField && mapping.targetField && rawParentData[mapping.sourceField] !== undefined) { parentData[mapping.targetField] = rawParentData[mapping.sourceField]; } }); + } else { + // ๋งคํ•‘์ด ์—†์œผ๋ฉด ๋ชจ๋“  ํ•„๋“œ ์ „๋‹ฌ (๋™์ผ ์ปฌ๋Ÿผ๋ช… ์ž๋™ ๋งคํ•‘) + parentData = { ...rawParentData }; } } } @@ -2749,14 +2742,6 @@ export class ButtonActionExecutor { } // 4. ๋ชจ๋‹ฌ ์—ด๊ธฐ ์ด๋ฒคํŠธ ๋ฐœ์ƒ - console.log("๐Ÿš€ [handleModal] ๋ชจ๋‹ฌ ์—ด๊ธฐ ์ด๋ฒคํŠธ ๋ฐœ์ƒ:", { - screenId: config.targetScreenId, - title: finalTitle, - selectedDataCount: selectedData.length, - parentDataKeys: Object.keys(parentData), - parentData: parentData, - }); - // passSelectedData๊ฐ€ true์ด๋ฉด editData๋กœ ์ „๋‹ฌ (์ˆ˜์ • ๋ชจ๋“œ์ฒ˜๋Ÿผ ๋ชจ๋“  ํ•„๋“œ ํ‘œ์‹œ) const isPassDataMode = passSelectedData && selectedData.length > 0;