diff --git a/frontend/components/screen/config-panels/ButtonConfigPanel.tsx b/frontend/components/screen/config-panels/ButtonConfigPanel.tsx index 3a126c29..417ea4ff 100644 --- a/frontend/components/screen/config-panels/ButtonConfigPanel.tsx +++ b/frontend/components/screen/config-panels/ButtonConfigPanel.tsx @@ -3081,6 +3081,79 @@ export const ButtonConfigPanel: React.FC = ({ /> )} + {/* ๐Ÿ†• ํ–‰ ์„ ํƒ ์‹œ์—๋งŒ ํ™œ์„ฑํ™” ์„ค์ • */} +
+

ํ–‰ ์„ ํƒ ํ™œ์„ฑํ™” ์กฐ๊ฑด

+

+ ํ…Œ์ด๋ธ” ๋ฆฌ์ŠคํŠธ๋‚˜ ๋ถ„ํ•  ํŒจ๋„์—์„œ ๋ฐ์ดํ„ฐ๊ฐ€ ์„ ํƒ๋˜์—ˆ์„ ๋•Œ๋งŒ ๋ฒ„ํŠผ์„ ํ™œ์„ฑํ™”ํ•ฉ๋‹ˆ๋‹ค. +

+ +
+
+ +

+ ์ฒดํฌํ•˜๋ฉด ํ…Œ์ด๋ธ”์—์„œ ํ–‰์„ ์„ ํƒํ•ด์•ผ๋งŒ ๋ฒ„ํŠผ์ด ํ™œ์„ฑํ™”๋ฉ๋‹ˆ๋‹ค. +

+
+ { + onUpdateProperty("componentConfig.action.requireRowSelection", checked); + }} + /> +
+ + {component.componentConfig?.action?.requireRowSelection && ( +
+
+ + +

+ ์ž๋™ ๊ฐ์ง€: ํ…Œ์ด๋ธ”, ๋ถ„ํ•  ํŒจ๋„, ํ”Œ๋กœ์šฐ ์œ„์ ฏ ์ค‘ ์„ ํƒ๋œ ํ•ญ๋ชฉ์ด ์žˆ์œผ๋ฉด ํ™œ์„ฑํ™” +

+
+ +
+
+ +

+ ์—ฌ๋Ÿฌ ํ–‰์ด ์„ ํƒ๋˜์–ด๋„ ํ™œ์„ฑํ™” (๊ธฐ๋ณธ: 1๊ฐœ ์ด์ƒ ์„ ํƒ ์‹œ ํ™œ์„ฑํ™”) +

+
+ { + onUpdateProperty("componentConfig.action.allowMultiRowSelection", checked); + }} + /> +
+ + {!(component.componentConfig?.action?.allowMultiRowSelection ?? true) && ( +
+

+ ์ •ํ™•ํžˆ 1๊ฐœ์˜ ํ–‰๋งŒ ์„ ํƒ๋˜์–ด์•ผ ๋ฒ„ํŠผ์ด ํ™œ์„ฑํ™”๋ฉ๋‹ˆ๋‹ค. +

+
+ )} +
+ )} +
+ {/* ์ œ์–ด ๊ธฐ๋Šฅ ์„น์…˜ */}
diff --git a/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx b/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx index 1f067865..f311c035 100644 --- a/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx +++ b/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx @@ -296,6 +296,145 @@ export const ButtonPrimaryComponent: React.FC = ({ return false; }, [component.componentConfig?.action, formData, vehicleStatus, statusLoading, component.label]); + // ๐Ÿ†• modalDataStore์—์„œ ์„ ํƒ๋œ ๋ฐ์ดํ„ฐ ํ™•์ธ (๋ถ„ํ•  ํŒจ๋„ ๋“ฑ์—์„œ ์ €์žฅ๋จ) + const [modalStoreData, setModalStoreData] = useState>({}); + + // modalDataStore ์ƒํƒœ ๊ตฌ๋… (์‹ค์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ) + useEffect(() => { + const actionConfig = component.componentConfig?.action; + if (!actionConfig?.requireRowSelection) return; + + // ๋™์  import๋กœ modalDataStore ๊ตฌ๋… + let unsubscribe: (() => void) | undefined; + + import("@/stores/modalDataStore").then(({ useModalDataStore }) => { + // ์ดˆ๊ธฐ๊ฐ’ ์„ค์ • + setModalStoreData(useModalDataStore.getState().dataRegistry); + + // ์ƒํƒœ ๋ณ€๊ฒฝ ๊ตฌ๋… + unsubscribe = useModalDataStore.subscribe((state) => { + setModalStoreData(state.dataRegistry); + }); + }); + + return () => { + unsubscribe?.(); + }; + }, [component.componentConfig?.action?.requireRowSelection]); + + // ๐Ÿ†• ํ–‰ ์„ ํƒ ๊ธฐ๋ฐ˜ ๋น„ํ™œ์„ฑํ™” ์กฐ๊ฑด ๊ณ„์‚ฐ + const isRowSelectionDisabled = useMemo(() => { + const actionConfig = component.componentConfig?.action; + + // requireRowSelection์ด ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์ง€ ์•Š์œผ๋ฉด ๋น„ํ™œ์„ฑํ™”ํ•˜์ง€ ์•Š์Œ + if (!actionConfig?.requireRowSelection) { + return false; + } + + const rowSelectionSource = actionConfig.rowSelectionSource || "auto"; + const allowMultiRowSelection = actionConfig.allowMultiRowSelection ?? true; + + // ์„ ํƒ๋œ ๋ฐ์ดํ„ฐ ํ™•์ธ + let hasSelection = false; + let selectionCount = 0; + let selectionSource = ""; + + // 1. ์ž๋™ ๊ฐ์ง€ ๋ชจ๋“œ ๋˜๋Š” ํ…Œ์ด๋ธ” ๋ฆฌ์ŠคํŠธ ๋ชจ๋“œ + if (rowSelectionSource === "auto" || rowSelectionSource === "tableList") { + // TableList์—์„œ ์„ ํƒ๋œ ํ–‰ ํ™•์ธ (props๋กœ ์ „๋‹ฌ๋จ) + if (selectedRowsData && selectedRowsData.length > 0) { + hasSelection = true; + selectionCount = selectedRowsData.length; + selectionSource = "tableList (selectedRowsData)"; + } + // ๋˜๋Š” selectedRows prop ํ™•์ธ + else if (selectedRows && selectedRows.length > 0) { + hasSelection = true; + selectionCount = selectedRows.length; + selectionSource = "tableList (selectedRows)"; + } + } + + // 2. ๋ถ„ํ•  ํŒจ๋„ ์ขŒ์ธก ์„ ํƒ ๋ฐ์ดํ„ฐ ํ™•์ธ + if (rowSelectionSource === "auto" || rowSelectionSource === "splitPanelLeft") { + // SplitPanelContext์—์„œ ํ™•์ธ + if (splitPanelContext?.selectedLeftData && Object.keys(splitPanelContext.selectedLeftData).length > 0) { + if (!hasSelection) { + hasSelection = true; + selectionCount = 1; + selectionSource = "splitPanelLeft (context)"; + } + } + + // ๐Ÿ†• modalDataStore์—์„œ๋„ ํ™•์ธ (SplitPanelLayoutComponent์—์„œ ์ €์žฅ) + if (!hasSelection && Object.keys(modalStoreData).length > 0) { + // modalDataStore์—์„œ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ + for (const [sourceId, items] of Object.entries(modalStoreData)) { + if (items && items.length > 0) { + hasSelection = true; + selectionCount = items.length; + selectionSource = `modalDataStore (${sourceId})`; + break; + } + } + } + } + + // 3. ํ”Œ๋กœ์šฐ ์œ„์ ฏ ์„ ํƒ ๋ฐ์ดํ„ฐ ํ™•์ธ + if (rowSelectionSource === "auto" || rowSelectionSource === "flowWidget") { + // ํ”Œ๋กœ์šฐ ์œ„์ ฏ ์„ ํƒ ๋ฐ์ดํ„ฐ ํ™•์ธ + if (!hasSelection && flowSelectedData && flowSelectedData.length > 0) { + hasSelection = true; + selectionCount = flowSelectedData.length; + selectionSource = "flowWidget"; + } + } + + // ๋””๋ฒ„๊น… ๋กœ๊ทธ + console.log("๐Ÿ” [ButtonPrimary] ํ–‰ ์„ ํƒ ์ฒดํฌ:", component.label, { + rowSelectionSource, + hasSelection, + selectionCount, + selectionSource, + hasSplitPanelContext: !!splitPanelContext, + selectedLeftData: splitPanelContext?.selectedLeftData, + selectedRowsData: selectedRowsData?.length, + selectedRows: selectedRows?.length, + flowSelectedData: flowSelectedData?.length, + modalStoreDataKeys: Object.keys(modalStoreData), + }); + + // ์„ ํƒ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์œผ๋ฉด ๋น„ํ™œ์„ฑํ™” + if (!hasSelection) { + console.log("๐Ÿšซ [ButtonPrimary] ํ–‰ ์„ ํƒ ํ•„์š” โ†’ ๋น„ํ™œ์„ฑํ™”:", component.label); + return true; + } + + // ๋‹ค์ค‘ ์„ ํƒ ํ—ˆ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ, ์ •ํ™•ํžˆ 1๊ฐœ๋งŒ ์„ ํƒ๋˜์–ด์•ผ ํ•จ + if (!allowMultiRowSelection && selectionCount !== 1) { + console.log("๐Ÿšซ [ButtonPrimary] ์ •ํ™•ํžˆ 1๊ฐœ ํ–‰ ์„ ํƒ ํ•„์š” โ†’ ๋น„ํ™œ์„ฑํ™”:", component.label, { + selectionCount, + allowMultiRowSelection, + }); + return true; + } + + console.log("โœ… [ButtonPrimary] ํ–‰ ์„ ํƒ ์กฐ๊ฑด ์ถฉ์กฑ:", component.label, { + selectionCount, + selectionSource, + }); + return false; + }, [ + component.componentConfig?.action, + component.label, + selectedRows, + selectedRowsData, + splitPanelContext?.selectedLeftData, + flowSelectedData, + splitPanelContext, + modalStoreData, + ]); + // ํ™•์ธ ๋‹ค์ด์–ผ๋กœ๊ทธ ์ƒํƒœ const [showConfirmDialog, setShowConfirmDialog] = useState(false); const [pendingAction, setPendingAction] = useState<{ @@ -832,7 +971,14 @@ export const ButtonPrimaryComponent: React.FC = ({ } // modalDataStore์—์„œ ์„ ํƒ๋œ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ (๋ถ„ํ•  ํŒจ๋„ ๋“ฑ์—์„œ ์„ ํƒํ•œ ๋ฐ์ดํ„ฐ) - if ((!effectiveSelectedRowsData || effectiveSelectedRowsData.length === 0) && effectiveTableName) { + // ๋‹จ, ๋ชจ๋‹ฌ(modal) ์•ก์…˜์€ ์‹ ๊ทœ ๋“ฑ๋ก์ด๋ฏ€๋กœ modalDataStore ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค์ง€ ์•Š์Œ + // (๋‹ค๋ฅธ ํ™”๋ฉด์—์„œ ์„ ํƒํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‚จ์•„์žˆ์„ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ) + const shouldFetchFromModalDataStore = + processedConfig.action.type !== "modal" && + (!effectiveSelectedRowsData || effectiveSelectedRowsData.length === 0) && + effectiveTableName; + + if (shouldFetchFromModalDataStore) { try { const { useModalDataStore } = await import("@/stores/modalDataStore"); const dataRegistry = useModalDataStore.getState().dataRegistry; @@ -860,12 +1006,10 @@ export const ButtonPrimaryComponent: React.FC = ({ return; } - // ๋ชจ๋‹ฌ ์•ก์…˜์ธ๋ฐ ์„ ํƒ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์œผ๋ฉด ๊ฒฝ๊ณ  ๋ฉ”์‹œ์ง€ ํ‘œ์‹œํ•˜๊ณ  ์ค‘๋‹จ - // (์‹ ๊ทœ ๋“ฑ๋ก ๋ชจ๋‹ฌ์—์„œ ์„ ํƒ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์ดˆ๊ธฐ๊ฐ’์œผ๋กœ ์ „๋‹ฌ๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€) - if (processedConfig.action.type === "modal" && effectiveSelectedRowsData && effectiveSelectedRowsData.length > 0) { - toast.warning("์‹ ๊ทœ ๋“ฑ๋ก ์‹œ์—๋Š” ํ…Œ์ด๋ธ”์—์„œ ์„ ํƒ๋œ ํ•ญ๋ชฉ์„ ํ•ด์ œํ•ด์ฃผ์„ธ์š”."); - return; - } + // ๐Ÿ”ง ๋ชจ๋‹ฌ ์•ก์…˜ ์‹œ ์„ ํƒ ๋ฐ์ดํ„ฐ ๊ฒฝ๊ณ  ์ œ๊ฑฐ + // ์ด์ „์—๋Š” "์‹ ๊ทœ ๋“ฑ๋ก ์‹œ์—๋Š” ํ…Œ์ด๋ธ”์—์„œ ์„ ํƒ๋œ ํ•ญ๋ชฉ์„ ํ•ด์ œํ•ด์ฃผ์„ธ์š”" ๊ฒฝ๊ณ ๋ฅผ ํ‘œ์‹œํ–ˆ์œผ๋‚˜, + // ๋‹ค๋ฅธ ํ™”๋ฉด์—์„œ ์„ ํƒํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‚จ์•„์žˆ๋Š” ๊ฒฝ์šฐ ์˜คํƒ์ด ๋ฐœ์ƒํ•˜์—ฌ ์ œ๊ฑฐํ•จ. + // ๋ชจ๋‹ฌ ํ™”๋ฉด ๋‚ด๋ถ€์—์„œ ํ•„์š” ์‹œ ์ž์ฒด์ ์œผ๋กœ ์„ ํƒ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฌด์‹œํ•˜๋„๋ก ์ฒ˜๋ฆฌํ•˜๋ฉด ๋จ. // ์ˆ˜์ •(edit) ์•ก์…˜ ๊ฒ€์ฆ if (processedConfig.action.type === "edit") { @@ -1088,17 +1232,26 @@ export const ButtonPrimaryComponent: React.FC = ({ } } - // ๐Ÿ†• ์ตœ์ข… ๋น„ํ™œ์„ฑํ™” ์ƒํƒœ (์„ค์ • + ์กฐ๊ฑด๋ถ€ ๋น„ํ™œ์„ฑํ™”) - const finalDisabled = componentConfig.disabled || isOperationButtonDisabled || statusLoading; + // ๐Ÿ†• ์ตœ์ข… ๋น„ํ™œ์„ฑํ™” ์ƒํƒœ (์„ค์ • + ์กฐ๊ฑด๋ถ€ ๋น„ํ™œ์„ฑํ™” + ํ–‰ ์„ ํƒ ํ•„์ˆ˜) + const finalDisabled = componentConfig.disabled || isOperationButtonDisabled || isRowSelectionDisabled || statusLoading; // ๊ณตํ†ต ๋ฒ„ํŠผ ์Šคํƒ€์ผ + // ๐Ÿ”ง component.style์—์„œ background/backgroundColor ์ถฉ๋Œ ๋ฐฉ์ง€ + const userStyle = component.style + ? Object.fromEntries( + Object.entries(component.style).filter( + ([key]) => !["width", "height", "background", "backgroundColor"].includes(key) + ) + ) + : {}; + const buttonElementStyle: React.CSSProperties = { width: "100%", height: "100%", minHeight: "40px", border: "none", borderRadius: "0.5rem", - background: finalDisabled ? "#e5e7eb" : buttonColor, + backgroundColor: finalDisabled ? "#e5e7eb" : buttonColor, // ๐Ÿ”ง background โ†’ backgroundColor๋กœ ๋ณ€๊ฒฝ color: finalDisabled ? "#9ca3af" : "white", // ๐Ÿ”ง ํฌ๊ธฐ ์„ค์ • ์ ์šฉ (sm/md/lg) fontSize: componentConfig.size === "sm" ? "0.75rem" : componentConfig.size === "lg" ? "1rem" : "0.875rem", @@ -1114,10 +1267,8 @@ export const ButtonPrimaryComponent: React.FC = ({ margin: "0", lineHeight: "1.25", boxShadow: finalDisabled ? "none" : "0 1px 2px 0 rgba(0, 0, 0, 0.05)", - // ๋””์ž์ธ ๋ชจ๋“œ์™€ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ๋ชจ๋“œ ๋ชจ๋‘์—์„œ ์‚ฌ์šฉ์ž ์Šคํƒ€์ผ ์ ์šฉ (width/height ์ œ์™ธ) - ...(component.style - ? Object.fromEntries(Object.entries(component.style).filter(([key]) => key !== "width" && key !== "height")) - : {}), + // ๋””์ž์ธ ๋ชจ๋“œ์™€ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ๋ชจ๋“œ ๋ชจ๋‘์—์„œ ์‚ฌ์šฉ์ž ์Šคํƒ€์ผ ์ ์šฉ (width/height/background ์ œ์™ธ) + ...userStyle, }; const buttonContent = processedConfig.text !== undefined ? processedConfig.text : component.label || "๋ฒ„ํŠผ"; diff --git a/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx b/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx index ef91a23d..bfb26c90 100644 --- a/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx +++ b/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx @@ -2030,14 +2030,14 @@ export const SplitPanelLayoutComponent: React.FC className="border-border flex flex-shrink-0 flex-col border-r" > -
@@ -2521,14 +2521,14 @@ export const SplitPanelLayoutComponent: React.FC className="flex flex-shrink-0 flex-col" > -
diff --git a/frontend/lib/registry/components/universal-form-modal/TableSectionRenderer.tsx b/frontend/lib/registry/components/universal-form-modal/TableSectionRenderer.tsx index ba03d2b9..a1c0bd76 100644 --- a/frontend/lib/registry/components/universal-form-modal/TableSectionRenderer.tsx +++ b/frontend/lib/registry/components/universal-form-modal/TableSectionRenderer.tsx @@ -16,12 +16,7 @@ import { ItemSelectionModal } from "../modal-repeater-table/ItemSelectionModal"; import { RepeaterColumnConfig, CalculationRule } from "../modal-repeater-table/types"; // ํƒ€์ž… ์ •์˜ -import { - TableSectionConfig, - TableColumnConfig, - TableJoinCondition, - FormDataState, -} from "./types"; +import { TableSectionConfig, TableColumnConfig, TableJoinCondition, FormDataState } from "./types"; interface TableSectionRendererProps { sectionId: string; @@ -56,7 +51,7 @@ function convertToRepeaterColumn(col: TableColumnConfig): RepeaterColumnConfig { selectOptions: col.selectOptions, // valueMapping์€ ๋ณ„๋„๋กœ ์ฒ˜๋ฆฌ }; - + // lookup ์„ค์ •์„ dynamicDataSource๋กœ ๋ณ€ํ™˜ (์ƒˆ๋กœ์šด ์กฐํšŒ ๊ธฐ๋Šฅ) if (col.lookup?.enabled && col.lookup.options && col.lookup.options.length > 0) { baseColumn.dynamicDataSource = { @@ -75,17 +70,19 @@ function convertToRepeaterColumn(col: TableColumnConfig): RepeaterColumnConfig { sourceField: cond.sourceField, targetField: cond.targetColumn, // sourceType์— ๋”ฐ๋ฅธ ๋ฐ์ดํ„ฐ ์ถœ์ฒ˜ ์„ค์ • - sourceType: cond.sourceType, // "currentRow" | "sectionField" | "externalTable" + sourceType: cond.sourceType, // "currentRow" | "sectionField" | "externalTable" fromFormData: cond.sourceType === "sectionField", sectionId: cond.sectionId, // ์™ธ๋ถ€ ํ…Œ์ด๋ธ” ์กฐํšŒ ์„ค์ • (sourceType์ด "externalTable"์ธ ๊ฒฝ์šฐ) externalLookup: cond.externalLookup, // ๊ฐ’ ๋ณ€ํ™˜ ์„ค์ • ์ „๋‹ฌ (๋ ˆ๊ฑฐ์‹œ ํ˜ธํ™˜) - transform: cond.transform?.enabled ? { - tableName: cond.transform.tableName, - matchColumn: cond.transform.matchColumn, - resultColumn: cond.transform.resultColumn, - } : undefined, + transform: cond.transform?.enabled + ? { + tableName: cond.transform.tableName, + matchColumn: cond.transform.matchColumn, + resultColumn: cond.transform.resultColumn, + } + : undefined, })), }, // ์กฐํšŒ ์œ ํ˜• ์ •๋ณด ์ถ”๊ฐ€ @@ -115,14 +112,18 @@ function convertToRepeaterColumn(col: TableColumnConfig): RepeaterColumnConfig { defaultOptionId: col.columnModes.find((m) => m.isDefault)?.id || col.columnModes[0]?.id, }; } - + return baseColumn; } /** * TableCalculationRule์„ CalculationRule๋กœ ๋ณ€ํ™˜ */ -function convertToCalculationRule(calc: { resultField: string; formula: string; dependencies: string[] }): CalculationRule { +function convertToCalculationRule(calc: { + resultField: string; + formula: string; + dependencies: string[]; +}): CalculationRule { return { result: calc.resultField, formula: calc.formula, @@ -136,7 +137,7 @@ function convertToCalculationRule(calc: { resultField: string; formula: string; */ async function transformValue( value: any, - transform: { tableName: string; matchColumn: string; resultColumn: string } + transform: { tableName: string; matchColumn: string; resultColumn: string }, ): Promise { if (!value || !transform.tableName || !transform.matchColumn || !transform.resultColumn) { return value; @@ -144,19 +145,16 @@ async function transformValue( try { // ์ •ํ™•ํžˆ ์ผ์น˜ํ•˜๋Š” ๊ฒ€์ƒ‰ - const response = await apiClient.post( - `/table-management/tables/${transform.tableName}/data`, - { - search: { - [transform.matchColumn]: { - value: value, - operator: "equals" - } - }, - size: 1, - page: 1 - } - ); + const response = await apiClient.post(`/table-management/tables/${transform.tableName}/data`, { + search: { + [transform.matchColumn]: { + value: value, + operator: "equals", + }, + }, + size: 1, + page: 1, + }); if (response.data.success && response.data.data?.data?.length > 0) { const transformedValue = response.data.data.data[0][transform.resultColumn]; @@ -186,7 +184,7 @@ async function fetchExternalLookupValue( }, rowData: any, sourceData: any, - formData: FormDataState + formData: FormDataState, ): Promise { // 1. ๋น„๊ต ๊ฐ’ ๊ฐ€์ ธ์˜ค๊ธฐ let matchValue: any; @@ -199,31 +197,32 @@ async function fetchExternalLookupValue( } if (matchValue === undefined || matchValue === null || matchValue === "") { - console.warn(`์™ธ๋ถ€ ํ…Œ์ด๋ธ” ์กฐํšŒ: ๋น„๊ต ๊ฐ’์ด ์—†์Šต๋‹ˆ๋‹ค. (${externalLookup.matchSourceType}.${externalLookup.matchSourceField})`); + console.warn( + `์™ธ๋ถ€ ํ…Œ์ด๋ธ” ์กฐํšŒ: ๋น„๊ต ๊ฐ’์ด ์—†์Šต๋‹ˆ๋‹ค. (${externalLookup.matchSourceType}.${externalLookup.matchSourceField})`, + ); return undefined; } // 2. ์™ธ๋ถ€ ํ…Œ์ด๋ธ”์—์„œ ๊ฐ’ ์กฐํšŒ (์ •ํ™•ํžˆ ์ผ์น˜ํ•˜๋Š” ๊ฒ€์ƒ‰) try { - const response = await apiClient.post( - `/table-management/tables/${externalLookup.tableName}/data`, - { - search: { - [externalLookup.matchColumn]: { - value: matchValue, - operator: "equals" - } - }, - size: 1, - page: 1 - } - ); + const response = await apiClient.post(`/table-management/tables/${externalLookup.tableName}/data`, { + search: { + [externalLookup.matchColumn]: { + value: matchValue, + operator: "equals", + }, + }, + size: 1, + page: 1, + }); if (response.data.success && response.data.data?.data?.length > 0) { return response.data.data.data[0][externalLookup.resultColumn]; } - console.warn(`์™ธ๋ถ€ ํ…Œ์ด๋ธ” ์กฐํšŒ: ${externalLookup.tableName}.${externalLookup.matchColumn} = "${matchValue}" ์ธ ํ–‰์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.`); + console.warn( + `์™ธ๋ถ€ ํ…Œ์ด๋ธ” ์กฐํšŒ: ${externalLookup.tableName}.${externalLookup.matchColumn} = "${matchValue}" ์ธ ํ–‰์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.`, + ); return undefined; } catch (error) { console.error("์™ธ๋ถ€ ํ…Œ์ด๋ธ” ์กฐํšŒ ์˜ค๋ฅ˜:", error); @@ -233,7 +232,7 @@ async function fetchExternalLookupValue( /** * ์™ธ๋ถ€ ํ…Œ์ด๋ธ”์—์„œ ๊ฐ’์„ ์กฐํšŒํ•˜๋Š” ํ•จ์ˆ˜ - * + * * @param tableName - ์กฐํšŒํ•  ํ…Œ์ด๋ธ”๋ช… * @param valueColumn - ๊ฐ€์ ธ์˜ฌ ์ปฌ๋Ÿผ๋ช… * @param joinConditions - ์กฐ์ธ ์กฐ๊ฑด ๋ชฉ๋ก @@ -247,7 +246,7 @@ async function fetchExternalValue( joinConditions: TableJoinCondition[], rowData: any, sourceData: any, - formData: FormDataState + formData: FormDataState, ): Promise { if (joinConditions.length === 0) { return undefined; @@ -298,15 +297,16 @@ async function fetchExternalValue( // ์ •ํ™•ํžˆ ์ผ์น˜ํ•˜๋Š” ๊ฒ€์ƒ‰์„ ์œ„ํ•ด operator: "equals" ์‚ฌ์šฉ whereConditions[condition.targetColumn] = { value: convertedValue, - operator: "equals" + operator: "equals", }; } // API ํ˜ธ์ถœ - const response = await apiClient.post( - `/table-management/tables/${tableName}/data`, - { search: whereConditions, size: 1, page: 1 } - ); + const response = await apiClient.post(`/table-management/tables/${tableName}/data`, { + search: whereConditions, + size: 1, + page: 1, + }); if (response.data.success && response.data.data?.data?.length > 0) { return response.data.data.data[0][valueColumn]; @@ -334,42 +334,42 @@ export function TableSectionRenderer({ }: TableSectionRendererProps) { // ํ…Œ์ด๋ธ” ๋ฐ์ดํ„ฐ ์ƒํƒœ (์ผ๋ฐ˜ ๋ชจ๋“œ) const [tableData, setTableData] = useState([]); - + // ์กฐ๊ฑด๋ถ€ ํ…Œ์ด๋ธ” ๋ฐ์ดํ„ฐ ์ƒํƒœ (์กฐ๊ฑด๋ณ„๋กœ ๋ถ„๋ฆฌ) const [conditionalTableData, setConditionalTableData] = useState({}); - + // ์กฐ๊ฑด๋ถ€ ํ…Œ์ด๋ธ”: ์„ ํƒ๋œ ์กฐ๊ฑด๋“ค (์ฒดํฌ๋ฐ•์Šค ๋ชจ๋“œ) const [selectedConditions, setSelectedConditions] = useState([]); - + // ์กฐ๊ฑด๋ถ€ ํ…Œ์ด๋ธ”: ํ˜„์žฌ ํ™œ์„ฑ ํƒญ const [activeConditionTab, setActiveConditionTab] = useState(""); - + // ์กฐ๊ฑด๋ถ€ ํ…Œ์ด๋ธ”: ํ˜„์žฌ ๋ชจ๋‹ฌ์ด ์—ด๋ฆฐ ์กฐ๊ฑด (์–ด๋–ค ์กฐ๊ฑด์˜ ํ…Œ์ด๋ธ”์— ์ถ”๊ฐ€ํ• ์ง€) const [modalCondition, setModalCondition] = useState(""); - + // ๋ชจ๋‹ฌ ์ƒํƒœ const [modalOpen, setModalOpen] = useState(false); - + // ์ฒดํฌ๋ฐ•์Šค ์„ ํƒ ์ƒํƒœ (์กฐ๊ฑด๋ณ„๋กœ ๋ถ„๋ฆฌ) const [selectedRows, setSelectedRows] = useState>(new Set()); const [conditionalSelectedRows, setConditionalSelectedRows] = useState>>({}); - + // ๋„ˆ๋น„ ์กฐ์ • ํŠธ๋ฆฌ๊ฑฐ (ํ™€์ˆ˜: ์ž๋™๋งž์ถค, ์ง์ˆ˜: ๊ท ๋“ฑ๋ถ„๋ฐฐ) const [widthTrigger, setWidthTrigger] = useState(0); - + // ๋™์  ๋ฐ์ดํ„ฐ ์†Œ์Šค ํ™œ์„ฑํ™” ์ƒํƒœ const [activeDataSources, setActiveDataSources] = useState>({}); - + // ๋‚ ์งœ ์ผ๊ด„ ์ ์šฉ ์™„๋ฃŒ ํ”Œ๋ž˜๊ทธ (์ปฌ๋Ÿผ๋ณ„๋กœ ํ•œ ๋ฒˆ๋งŒ ์ ์šฉ) const [batchAppliedFields, setBatchAppliedFields] = useState>(new Set()); // ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ ๋กœ๋“œ ์™„๋ฃŒ ํ”Œ๋ž˜๊ทธ (๋ฌดํ•œ ๋ฃจํ”„ ๋ฐฉ์ง€) const initialDataLoadedRef = React.useRef(false); - + // ์กฐ๊ฑด๋ถ€ ํ…Œ์ด๋ธ” ์„ค์ • const conditionalConfig = tableConfig.conditionalTable; const isConditionalMode = conditionalConfig?.enabled ?? false; - + // ์กฐ๊ฑด๋ถ€ ํ…Œ์ด๋ธ”: ๋™์  ์˜ต์…˜ ๋กœ๋“œ ์ƒํƒœ const [dynamicOptions, setDynamicOptions] = useState<{ id: string; value: string; label: string }[]>([]); const [dynamicOptionsLoading, setDynamicOptionsLoading] = useState(false); @@ -380,51 +380,48 @@ export function TableSectionRenderer({ if (!isConditionalMode) return; if (!conditionalConfig?.optionSource?.enabled) return; if (dynamicOptionsLoadedRef.current) return; - + const { tableName, valueColumn, labelColumn, filterCondition } = conditionalConfig.optionSource; - + if (!tableName || !valueColumn) return; - + const loadDynamicOptions = async () => { setDynamicOptionsLoading(true); try { // DISTINCT ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•œ API ํ˜ธ์ถœ - const response = await apiClient.post( - `/table-management/tables/${tableName}/data`, - { - search: filterCondition ? { _raw: filterCondition } : {}, - size: 1000, - page: 1, - } - ); - + const response = await apiClient.post(`/table-management/tables/${tableName}/data`, { + search: filterCondition ? { _raw: filterCondition } : {}, + size: 1000, + page: 1, + }); + if (response.data.success && response.data.data?.data) { const rows = response.data.data.data; - + // ์ค‘๋ณต ์ œ๊ฑฐํ•˜์—ฌ ๊ณ ์œ  ๊ฐ’ ์ถ”์ถœ const uniqueValues = new Map(); for (const row of rows) { const value = row[valueColumn]; if (value && !uniqueValues.has(value)) { - const label = labelColumn ? (row[labelColumn] || value) : value; + const label = labelColumn ? row[labelColumn] || value : value; uniqueValues.set(value, label); } } - + // ์˜ต์…˜ ๋ฐฐ์—ด๋กœ ๋ณ€ํ™˜ const options = Array.from(uniqueValues.entries()).map(([value, label], index) => ({ id: `dynamic_${index}`, value, label, })); - + console.log("[TableSectionRenderer] ๋™์  ์˜ต์…˜ ๋กœ๋“œ ์™„๋ฃŒ:", { tableName, valueColumn, optionCount: options.length, options, }); - + setDynamicOptions(options); dynamicOptionsLoadedRef.current = true; } @@ -434,48 +431,45 @@ export function TableSectionRenderer({ setDynamicOptionsLoading(false); } }; - + loadDynamicOptions(); }, [isConditionalMode, conditionalConfig?.optionSource]); // ============================================ // ๋™์  Select ์˜ต์…˜ (์†Œ์Šค ํ…Œ์ด๋ธ”์—์„œ ๋“œ๋กญ๋‹ค์šด ์˜ต์…˜ ๋กœ๋“œ) // ============================================ - + // ์†Œ์Šค ํ…Œ์ด๋ธ” ๋ฐ์ดํ„ฐ ์บ์‹œ (๋™์  Select ์˜ต์…˜์šฉ) const [sourceDataCache, setSourceDataCache] = useState([]); const sourceDataLoadedRef = React.useRef(false); - + // ๋™์  Select ์˜ต์…˜์ด ์žˆ๋Š” ์ปฌ๋Ÿผ ํ™•์ธ const hasDynamicSelectColumns = useMemo(() => { - return tableConfig.columns?.some(col => col.dynamicSelectOptions?.enabled); + return tableConfig.columns?.some((col) => col.dynamicSelectOptions?.enabled); }, [tableConfig.columns]); - + // ์†Œ์Šค ํ…Œ์ด๋ธ” ๋ฐ์ดํ„ฐ ๋กœ๋“œ (๋™์  Select ์˜ต์…˜์šฉ) useEffect(() => { if (!hasDynamicSelectColumns) return; if (sourceDataLoadedRef.current) return; if (!tableConfig.source?.tableName) return; - + const loadSourceData = async () => { try { // ์กฐ๊ฑด๋ถ€ ํ…Œ์ด๋ธ” ํ•„ํ„ฐ ์กฐ๊ฑด ์ ์šฉ const filterCondition: Record = {}; - + // ์†Œ์Šค ํ•„ํ„ฐ๊ฐ€ ํ™œ์„ฑํ™”๋˜์–ด ์žˆ๊ณ  ์กฐ๊ฑด์ด ์„ ํƒ๋˜์–ด ์žˆ์œผ๋ฉด ํ•„ํ„ฐ ์ ์šฉ if (conditionalConfig?.sourceFilter?.enabled && activeConditionTab) { filterCondition[conditionalConfig.sourceFilter.filterColumn] = activeConditionTab; } - - const response = await apiClient.post( - `/table-management/tables/${tableConfig.source.tableName}/data`, - { - search: filterCondition, - size: 1000, - page: 1, - } - ); - + + const response = await apiClient.post(`/table-management/tables/${tableConfig.source.tableName}/data`, { + search: filterCondition, + size: 1000, + page: 1, + }); + if (response.data.success && response.data.data?.data) { setSourceDataCache(response.data.data.data); sourceDataLoadedRef.current = true; @@ -489,36 +483,33 @@ export function TableSectionRenderer({ console.error("[TableSectionRenderer] ์†Œ์Šค ๋ฐ์ดํ„ฐ ๋กœ๋“œ ์‹คํŒจ:", error); } }; - + loadSourceData(); }, [hasDynamicSelectColumns, tableConfig.source?.tableName, conditionalConfig?.sourceFilter, activeConditionTab]); - + // ์กฐ๊ฑด ํƒญ ๋ณ€๊ฒฝ ์‹œ ์†Œ์Šค ๋ฐ์ดํ„ฐ ๋‹ค์‹œ ๋กœ๋“œ useEffect(() => { if (!hasDynamicSelectColumns) return; if (!conditionalConfig?.sourceFilter?.enabled) return; if (!activeConditionTab) return; if (!tableConfig.source?.tableName) return; - + // ์กฐ๊ฑด ๋ณ€๊ฒฝ ์‹œ ์บ์‹œ ๋ฆฌ์…‹ํ•˜๊ณ  ์ฆ‰์‹œ ๋‹ค์‹œ ๋กœ๋“œ sourceDataLoadedRef.current = false; setSourceDataCache([]); - + // ์ฆ‰์‹œ ๋ฐ์ดํ„ฐ ๋‹ค์‹œ ๋กœ๋“œ (๊ธฐ์กด useEffect์— ์˜์กดํ•˜์ง€ ์•Š๊ณ  ์ง์ ‘ ํ˜ธ์ถœ) const loadSourceData = async () => { try { const filterCondition: Record = {}; filterCondition[conditionalConfig.sourceFilter!.filterColumn] = activeConditionTab; - - const response = await apiClient.post( - `/table-management/tables/${tableConfig.source!.tableName}/data`, - { - search: filterCondition, - size: 1000, - page: 1, - } - ); - + + const response = await apiClient.post(`/table-management/tables/${tableConfig.source!.tableName}/data`, { + search: filterCondition, + size: 1000, + page: 1, + }); + if (response.data.success && response.data.data?.data) { setSourceDataCache(response.data.data.data); sourceDataLoadedRef.current = true; @@ -532,96 +523,100 @@ export function TableSectionRenderer({ console.error("[TableSectionRenderer] ์†Œ์Šค ๋ฐ์ดํ„ฐ ๋กœ๋“œ ์‹คํŒจ:", error); } }; - + loadSourceData(); - }, [activeConditionTab, hasDynamicSelectColumns, conditionalConfig?.sourceFilter?.enabled, conditionalConfig?.sourceFilter?.filterColumn, tableConfig.source?.tableName]); - + }, [ + activeConditionTab, + hasDynamicSelectColumns, + conditionalConfig?.sourceFilter?.enabled, + conditionalConfig?.sourceFilter?.filterColumn, + tableConfig.source?.tableName, + ]); + // ์ปฌ๋Ÿผ๋ณ„ ๋™์  Select ์˜ต์…˜ ์ƒ์„ฑ const dynamicSelectOptionsMap = useMemo(() => { const optionsMap: Record = {}; - + if (!sourceDataCache.length) return optionsMap; - + for (const col of tableConfig.columns || []) { if (!col.dynamicSelectOptions?.enabled) continue; - + const { sourceField, labelField, distinct = true } = col.dynamicSelectOptions; - + if (!sourceField) continue; - + // ์†Œ์Šค ๋ฐ์ดํ„ฐ์—์„œ ์˜ต์…˜ ์ถ”์ถœ const seenValues = new Set(); const options: { value: string; label: string }[] = []; - + for (const row of sourceDataCache) { const value = row[sourceField]; if (value === undefined || value === null || value === "") continue; - + const stringValue = String(value); - + if (distinct && seenValues.has(stringValue)) continue; seenValues.add(stringValue); - - const label = labelField ? (row[labelField] || stringValue) : stringValue; + + const label = labelField ? row[labelField] || stringValue : stringValue; options.push({ value: stringValue, label: String(label) }); } - + optionsMap[col.field] = options; } - + return optionsMap; }, [sourceDataCache, tableConfig.columns]); - + // ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ํ•ธ๋“ค๋Ÿฌ (๋‚ ์งœ ์ผ๊ด„ ์ ์šฉ ๋กœ์ง ํฌํ•จ) - ๋‹ค๋ฅธ ํ•จ์ˆ˜์—์„œ ์ฐธ์กฐํ•˜๋ฏ€๋กœ ๋จผ์ € ์ •์˜ const handleDataChange = useCallback( (newData: any[]) => { let processedData = newData; - + // ๋‚ ์งœ ์ผ๊ด„ ์ ์šฉ ๋กœ์ง: batchApply๊ฐ€ ํ™œ์„ฑํ™”๋œ ๋‚ ์งœ ์ปฌ๋Ÿผ ์ฒ˜๋ฆฌ - const batchApplyColumns = tableConfig.columns.filter( - (col) => col.type === "date" && col.batchApply === true - ); - + const batchApplyColumns = tableConfig.columns.filter((col) => col.type === "date" && col.batchApply === true); + for (const dateCol of batchApplyColumns) { // ์ด๋ฏธ ์ผ๊ด„ ์ ์šฉ๋œ ์ปฌ๋Ÿผ์€ ๊ฑด๋„ˆ๋œ€ if (batchAppliedFields.has(dateCol.field)) continue; - + // ํ•ด๋‹น ์ปฌ๋Ÿผ์— ๊ฐ’์ด ์žˆ๋Š” ํ–‰๊ณผ ์—†๋Š” ํ–‰ ๋ถ„๋ฅ˜ const itemsWithDate = processedData.filter((item) => item[dateCol.field]); const itemsWithoutDate = processedData.filter((item) => !item[dateCol.field]); - + // ์กฐ๊ฑด: ์ •ํ™•ํžˆ 1๊ฐœ๋งŒ ๋‚ ์งœ๊ฐ€ ์žˆ๊ณ , ๋‚˜๋จธ์ง€๋Š” ๋ชจ๋‘ ๋น„์–ด์žˆ์„ ๋•Œ if (itemsWithDate.length === 1 && itemsWithoutDate.length > 0) { const selectedDate = itemsWithDate[0][dateCol.field]; - + // ๋ชจ๋“  ํ–‰์— ๋™์ผํ•œ ๋‚ ์งœ ์ ์šฉ processedData = processedData.map((item) => ({ ...item, [dateCol.field]: selectedDate, })); - + // ํ”Œ๋ž˜๊ทธ ํ™œ์„ฑํ™” (์ดํ›„ ๊ฐœ๋ณ„ ์ˆ˜์ • ๊ฐ€๋Šฅ) setBatchAppliedFields((prev) => new Set([...prev, dateCol.field])); } } - + setTableData(processedData); onTableDataChange(processedData); }, - [onTableDataChange, tableConfig.columns, batchAppliedFields] + [onTableDataChange, tableConfig.columns, batchAppliedFields], ); - + // ํ–‰ ์„ ํƒ ๋ชจ๋“œ: ๋“œ๋กญ๋‹ค์šด ๊ฐ’ ๋ณ€๊ฒฝ ์‹œ ๊ฐ™์€ ์†Œ์Šค ํ–‰์˜ ๋‹ค๋ฅธ ์ปฌ๋Ÿผ๋“ค ์ž๋™ ์ฑ„์›€ const handleDynamicSelectChange = useCallback( (rowIndex: number, columnField: string, selectedValue: string, conditionValue?: string) => { - const column = tableConfig.columns?.find(col => col.field === columnField); + const column = tableConfig.columns?.find((col) => col.field === columnField); if (!column?.dynamicSelectOptions?.rowSelectionMode?.enabled) { // ํ–‰ ์„ ํƒ ๋ชจ๋“œ๊ฐ€ ์•„๋‹ˆ๋ฉด ์ผ๋ฐ˜ ๊ฐ’ ๋ณ€๊ฒฝ๋งŒ if (conditionValue && isConditionalMode) { const currentData = conditionalTableData[conditionValue] || []; const newData = [...currentData]; newData[rowIndex] = { ...newData[rowIndex], [columnField]: selectedValue }; - setConditionalTableData(prev => ({ ...prev, [conditionValue]: newData })); + setConditionalTableData((prev) => ({ ...prev, [conditionValue]: newData })); onConditionalTableDataChange?.(conditionValue, newData); } else { const newData = [...tableData]; @@ -630,18 +625,18 @@ export function TableSectionRenderer({ } return; } - + // ํ–‰ ์„ ํƒ ๋ชจ๋“œ: ์†Œ์Šค ๋ฐ์ดํ„ฐ์—์„œ ํ•ด๋‹น ๊ฐ’์„ ๊ฐ€์ง„ ํ–‰ ์ฐพ๊ธฐ const { sourceField } = column.dynamicSelectOptions; const { autoFillColumns, sourceIdColumn, targetIdField } = column.dynamicSelectOptions.rowSelectionMode; - - const sourceRow = sourceDataCache.find(row => String(row[sourceField]) === selectedValue); - + + const sourceRow = sourceDataCache.find((row) => String(row[sourceField]) === selectedValue); + if (!sourceRow) { console.warn(`[TableSectionRenderer] ์†Œ์Šค ํ–‰์„ ์ฐพ์„ ์ˆ˜ ์—†์Œ: ${sourceField} = ${selectedValue}`); return; } - + // ํ˜„์žฌ ํ–‰ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ let currentData: any[]; if (conditionValue && isConditionalMode) { @@ -649,10 +644,10 @@ export function TableSectionRenderer({ } else { currentData = tableData; } - + const newData = [...currentData]; const updatedRow = { ...newData[rowIndex], [columnField]: selectedValue }; - + // ์ž๋™ ์ฑ„์›€ ๋งคํ•‘ ์ ์šฉ if (autoFillColumns) { for (const mapping of autoFillColumns) { @@ -662,22 +657,22 @@ export function TableSectionRenderer({ } } } - + // ์†Œ์Šค ID ์ €์žฅ if (sourceIdColumn && targetIdField) { updatedRow[targetIdField] = sourceRow[sourceIdColumn]; } - + newData[rowIndex] = updatedRow; - + // ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ if (conditionValue && isConditionalMode) { - setConditionalTableData(prev => ({ ...prev, [conditionValue]: newData })); + setConditionalTableData((prev) => ({ ...prev, [conditionValue]: newData })); onConditionalTableDataChange?.(conditionValue, newData); } else { handleDataChange(newData); } - + console.log("[TableSectionRenderer] ํ–‰ ์„ ํƒ ๋ชจ๋“œ ์ž๋™ ์ฑ„์›€:", { columnField, selectedValue, @@ -685,93 +680,101 @@ export function TableSectionRenderer({ updatedRow, }); }, - [tableConfig.columns, sourceDataCache, tableData, conditionalTableData, isConditionalMode, handleDataChange, onConditionalTableDataChange] + [ + tableConfig.columns, + sourceDataCache, + tableData, + conditionalTableData, + isConditionalMode, + handleDataChange, + onConditionalTableDataChange, + ], ); // ์ฐธ์กฐ ์ปฌ๋Ÿผ ๊ฐ’ ์กฐํšŒ ํ•จ์ˆ˜ (saveToTarget: false์ธ ์ปฌ๋Ÿผ์— ๋Œ€ํ•ด ์†Œ์Šค ํ…Œ์ด๋ธ” ์กฐํšŒ) - const loadReferenceColumnValues = useCallback(async (data: any[]) => { - // saveToTarget: false์ด๊ณ  referenceDisplay๊ฐ€ ์„ค์ •๋œ ์ปฌ๋Ÿผ ์ฐพ๊ธฐ - const referenceColumns = (tableConfig.columns || []).filter( - (col) => col.saveConfig?.saveToTarget === false && col.saveConfig?.referenceDisplay - ); - - if (referenceColumns.length === 0) return; - - const sourceTableName = tableConfig.source?.tableName; - if (!sourceTableName) { - console.warn("[TableSectionRenderer] ์ฐธ์กฐ ์กฐํšŒ๋ฅผ ์œ„ํ•œ ์†Œ์Šค ํ…Œ์ด๋ธ”์ด ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."); - return; - } - - // ์ฐธ์กฐ ID๋“ค ์ˆ˜์ง‘ (์ค‘๋ณต ์ œ๊ฑฐ) - const referenceIdSet = new Set(); - - for (const col of referenceColumns) { - const refDisplay = col.saveConfig!.referenceDisplay!; - - for (const row of data) { - const refId = row[refDisplay.referenceIdField]; - if (refId !== undefined && refId !== null && refId !== "") { - referenceIdSet.add(String(refId)); + const loadReferenceColumnValues = useCallback( + async (data: any[]) => { + // saveToTarget: false์ด๊ณ  referenceDisplay๊ฐ€ ์„ค์ •๋œ ์ปฌ๋Ÿผ ์ฐพ๊ธฐ + const referenceColumns = (tableConfig.columns || []).filter( + (col) => col.saveConfig?.saveToTarget === false && col.saveConfig?.referenceDisplay, + ); + + if (referenceColumns.length === 0) return; + + const sourceTableName = tableConfig.source?.tableName; + if (!sourceTableName) { + console.warn("[TableSectionRenderer] ์ฐธ์กฐ ์กฐํšŒ๋ฅผ ์œ„ํ•œ ์†Œ์Šค ํ…Œ์ด๋ธ”์ด ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."); + return; + } + + // ์ฐธ์กฐ ID๋“ค ์ˆ˜์ง‘ (์ค‘๋ณต ์ œ๊ฑฐ) + const referenceIdSet = new Set(); + + for (const col of referenceColumns) { + const refDisplay = col.saveConfig!.referenceDisplay!; + + for (const row of data) { + const refId = row[refDisplay.referenceIdField]; + if (refId !== undefined && refId !== null && refId !== "") { + referenceIdSet.add(String(refId)); + } } } - } - - if (referenceIdSet.size === 0) return; - - try { - // ์†Œ์Šค ํ…Œ์ด๋ธ”์—์„œ ์ฐธ์กฐ ID์— ํ•ด๋‹นํ•˜๋Š” ๋ฐ์ดํ„ฐ ์กฐํšŒ - const response = await apiClient.post( - `/table-management/tables/${sourceTableName}/data`, - { + + if (referenceIdSet.size === 0) return; + + try { + // ์†Œ์Šค ํ…Œ์ด๋ธ”์—์„œ ์ฐธ์กฐ ID์— ํ•ด๋‹นํ•˜๋Š” ๋ฐ์ดํ„ฐ ์กฐํšŒ + const response = await apiClient.post(`/table-management/tables/${sourceTableName}/data`, { search: { id: Array.from(referenceIdSet) }, // ID ๋ฐฐ์—ด๋กœ ์กฐํšŒ size: 1000, page: 1, + }); + + if (!response.data?.success || !response.data?.data?.data) { + console.warn("[TableSectionRenderer] ์ฐธ์กฐ ๋ฐ์ดํ„ฐ ์กฐํšŒ ์‹คํŒจ"); + return; } - ); - - if (!response.data?.success || !response.data?.data?.data) { - console.warn("[TableSectionRenderer] ์ฐธ์กฐ ๋ฐ์ดํ„ฐ ์กฐํšŒ ์‹คํŒจ"); - return; - } - - const sourceData: any[] = response.data.data.data; - - // ID๋ฅผ ํ‚ค๋กœ ํ•˜๋Š” ๋งต ์ƒ์„ฑ - const sourceDataMap: Record = {}; - for (const sourceRow of sourceData) { - sourceDataMap[String(sourceRow.id)] = sourceRow; - } - - // ๊ฐ ํ–‰์— ์ฐธ์กฐ ์ปฌ๋Ÿผ ๊ฐ’ ์ฑ„์šฐ๊ธฐ - const updatedData = data.map((row) => { - const newRow = { ...row }; - - for (const col of referenceColumns) { - const refDisplay = col.saveConfig!.referenceDisplay!; - const refId = row[refDisplay.referenceIdField]; - - if (refId !== undefined && refId !== null && refId !== "") { - const sourceRow = sourceDataMap[String(refId)]; - if (sourceRow) { - newRow[col.field] = sourceRow[refDisplay.sourceColumn]; + + const sourceData: any[] = response.data.data.data; + + // ID๋ฅผ ํ‚ค๋กœ ํ•˜๋Š” ๋งต ์ƒ์„ฑ + const sourceDataMap: Record = {}; + for (const sourceRow of sourceData) { + sourceDataMap[String(sourceRow.id)] = sourceRow; + } + + // ๊ฐ ํ–‰์— ์ฐธ์กฐ ์ปฌ๋Ÿผ ๊ฐ’ ์ฑ„์šฐ๊ธฐ + const updatedData = data.map((row) => { + const newRow = { ...row }; + + for (const col of referenceColumns) { + const refDisplay = col.saveConfig!.referenceDisplay!; + const refId = row[refDisplay.referenceIdField]; + + if (refId !== undefined && refId !== null && refId !== "") { + const sourceRow = sourceDataMap[String(refId)]; + if (sourceRow) { + newRow[col.field] = sourceRow[refDisplay.sourceColumn]; + } } } - } - - return newRow; - }); - - console.log("[TableSectionRenderer] ์ฐธ์กฐ ์ปฌ๋Ÿผ ๊ฐ’ ์กฐํšŒ ์™„๋ฃŒ:", { - referenceColumns: referenceColumns.map((c) => c.field), - updatedRowCount: updatedData.length, - }); - - setTableData(updatedData); - } catch (error) { - console.error("[TableSectionRenderer] ์ฐธ์กฐ ๋ฐ์ดํ„ฐ ์กฐํšŒ ์‹คํŒจ:", error); - } - }, [tableConfig.columns, tableConfig.source?.tableName]); + + return newRow; + }); + + console.log("[TableSectionRenderer] ์ฐธ์กฐ ์ปฌ๋Ÿผ ๊ฐ’ ์กฐํšŒ ์™„๋ฃŒ:", { + referenceColumns: referenceColumns.map((c) => c.field), + updatedRowCount: updatedData.length, + }); + + setTableData(updatedData); + } catch (error) { + console.error("[TableSectionRenderer] ์ฐธ์กฐ ๋ฐ์ดํ„ฐ ์กฐํšŒ ์‹คํŒจ:", error); + } + }, + [tableConfig.columns, tableConfig.source?.tableName], + ); // formData์—์„œ ์ดˆ๊ธฐ ํ…Œ์ด๋ธ” ๋ฐ์ดํ„ฐ ๋กœ๋“œ (์ˆ˜์ • ๋ชจ๋“œ์—์„œ _groupedData ํ‘œ์‹œ) useEffect(() => { @@ -788,7 +791,7 @@ export function TableSectionRenderer({ }); setTableData(initialData); initialDataLoadedRef.current = true; - + // ์ฐธ์กฐ ์ปฌ๋Ÿผ ๊ฐ’ ์กฐํšŒ (saveToTarget: false์ธ ์ปฌ๋Ÿผ) loadReferenceColumnValues(initialData); } @@ -796,14 +799,14 @@ export function TableSectionRenderer({ // RepeaterColumnConfig๋กœ ๋ณ€ํ™˜ (๋™์  Select ์˜ต์…˜ ๋ฐ˜์˜) const columns: RepeaterColumnConfig[] = useMemo(() => { - return (tableConfig.columns || []).map(col => { + return (tableConfig.columns || []).map((col) => { const baseColumn = convertToRepeaterColumn(col); - + // ๋™์  Select ์˜ต์…˜์ด ์žˆ์œผ๋ฉด ์ ์šฉ if (col.dynamicSelectOptions?.enabled && dynamicSelectOptionsMap[col.field]) { baseColumn.selectOptions = dynamicSelectOptionsMap[col.field]; } - + return baseColumn; }); }, [tableConfig.columns, dynamicSelectOptionsMap]); @@ -840,23 +843,24 @@ export function TableSectionRenderer({ return updatedRow; }, - [calculationRules] + [calculationRules], ); const calculateAll = useCallback( (data: any[]): any[] => { return data.map((row) => calculateRow(row)); }, - [calculateRow] + [calculateRow], ); // ํ–‰ ๋ณ€๊ฒฝ ํ•ธ๋“ค๋Ÿฌ (๋™์  Select ํ–‰ ์„ ํƒ ๋ชจ๋“œ ์ง€์›) const handleRowChange = useCallback( (index: number, newRow: any, conditionValue?: string) => { - const oldRow = conditionValue && isConditionalMode - ? (conditionalTableData[conditionValue]?.[index] || {}) - : (tableData[index] || {}); - + const oldRow = + conditionValue && isConditionalMode + ? conditionalTableData[conditionValue]?.[index] || {} + : tableData[index] || {}; + // ๋ณ€๊ฒฝ๋œ ํ•„๋“œ ์ฐพ๊ธฐ const changedFields: string[] = []; for (const key of Object.keys(newRow)) { @@ -864,25 +868,25 @@ export function TableSectionRenderer({ changedFields.push(key); } } - + // ๋™์  Select ์ปฌ๋Ÿผ์˜ ํ–‰ ์„ ํƒ ๋ชจ๋“œ ํ™•์ธ for (const changedField of changedFields) { - const column = tableConfig.columns?.find(col => col.field === changedField); + const column = tableConfig.columns?.find((col) => col.field === changedField); if (column?.dynamicSelectOptions?.rowSelectionMode?.enabled) { // ํ–‰ ์„ ํƒ ๋ชจ๋“œ ์ฒ˜๋ฆฌ (์ž๋™ ์ฑ„์›€) handleDynamicSelectChange(index, changedField, newRow[changedField], conditionValue); return; // ํ–‰ ์„ ํƒ ๋ชจ๋“œ์—์„œ ์ฒ˜๋ฆฌ ์™„๋ฃŒ } } - + // ์ผ๋ฐ˜ ํ–‰ ๋ณ€๊ฒฝ ์ฒ˜๋ฆฌ const calculatedRow = calculateRow(newRow); - + if (conditionValue && isConditionalMode) { const currentData = conditionalTableData[conditionValue] || []; const newData = [...currentData]; newData[index] = calculatedRow; - setConditionalTableData(prev => ({ ...prev, [conditionValue]: newData })); + setConditionalTableData((prev) => ({ ...prev, [conditionValue]: newData })); onConditionalTableDataChange?.(conditionValue, newData); } else { const newData = [...tableData]; @@ -890,7 +894,16 @@ export function TableSectionRenderer({ handleDataChange(newData); } }, - [tableData, conditionalTableData, isConditionalMode, tableConfig.columns, calculateRow, handleDataChange, handleDynamicSelectChange, onConditionalTableDataChange] + [ + tableData, + conditionalTableData, + isConditionalMode, + tableConfig.columns, + calculateRow, + handleDataChange, + handleDynamicSelectChange, + onConditionalTableDataChange, + ], ); // ํ–‰ ์‚ญ์ œ ํ•ธ๋“ค๋Ÿฌ @@ -899,7 +912,7 @@ export function TableSectionRenderer({ const newData = tableData.filter((_, i) => i !== index); handleDataChange(newData); }, - [tableData, handleDataChange] + [tableData, handleDataChange], ); // ์„ ํƒ๋œ ํ•ญ๋ชฉ ์ผ๊ด„ ์‚ญ์ œ @@ -908,7 +921,7 @@ export function TableSectionRenderer({ const newData = tableData.filter((_, index) => !selectedRows.has(index)); handleDataChange(newData); setSelectedRows(new Set()); - + // ๋ฐ์ดํ„ฐ๊ฐ€ ๋ชจ๋‘ ์‚ญ์ œ๋˜๋ฉด ์ผ๊ด„ ์ ์šฉ ํ”Œ๋ž˜๊ทธ๋„ ๋ฆฌ์…‹ if (newData.length === 0) { setBatchAppliedFields(new Set()); @@ -931,7 +944,7 @@ export function TableSectionRenderer({ // ํ˜„์žฌ ํ™œ์„ฑํ™”๋œ ์˜ต์…˜ ๋˜๋Š” ๊ธฐ๋ณธ ์˜ต์…˜ ์‚ฌ์šฉ const activeOptionId = activeDataSources[col.field]; const defaultOption = col.lookup.options.find((o) => o.isDefault) || col.lookup.options[0]; - const selectedOption = activeOptionId + const selectedOption = activeOptionId ? col.lookup.options.find((o) => o.id === activeOptionId) || defaultOption : defaultOption; @@ -969,11 +982,13 @@ export function TableSectionRenderer({ // ์™ธ๋ถ€ ํ…Œ์ด๋ธ” ์กฐํšŒ ์„ค์ • externalLookup: cond.externalLookup, // ๊ฐ’ ๋ณ€ํ™˜ ์„ค์ • ์ „๋‹ฌ (๋ ˆ๊ฑฐ์‹œ ํ˜ธํ™˜) - transform: cond.transform?.enabled ? { - tableName: cond.transform.tableName, - matchColumn: cond.transform.matchColumn, - resultColumn: cond.transform.resultColumn, - } : undefined, + transform: cond.transform?.enabled + ? { + tableName: cond.transform.tableName, + matchColumn: cond.transform.matchColumn, + resultColumn: cond.transform.resultColumn, + } + : undefined, }; }); @@ -982,15 +997,15 @@ export function TableSectionRenderer({ selectedOption.tableName, selectedOption.valueColumn, joinConditions, - { ...sourceItem, ...newItem }, // rowData (ํ˜„์žฌ ํ–‰) - sourceItem, // sourceData (์†Œ์Šค ํ…Œ์ด๋ธ” ์›๋ณธ) - formData + { ...sourceItem, ...newItem }, // rowData (ํ˜„์žฌ ํ–‰) + sourceItem, // sourceData (์†Œ์Šค ํ…Œ์ด๋ธ” ์›๋ณธ) + formData, ); - + if (value !== undefined) { newItem[col.field] = value; } - + // _sourceData์— ์›๋ณธ ์ €์žฅ newItem._sourceData = sourceItem; } @@ -1045,8 +1060,8 @@ export function TableSectionRenderer({ valueColumn, joinConditions, { ...sourceItem, ...newItem }, // rowData - sourceItem, // sourceData - formData + sourceItem, // sourceData + formData, ); if (value !== undefined) { newItem[col.field] = value; @@ -1070,7 +1085,7 @@ export function TableSectionRenderer({ } return newItem; - }) + }), ); // ๊ณ„์‚ฐ ํ•„๋“œ ์—…๋ฐ์ดํŠธ @@ -1080,7 +1095,7 @@ export function TableSectionRenderer({ const newData = [...tableData, ...calculatedItems]; handleDataChange(newData); }, - [tableConfig.columns, formData, tableData, calculateAll, handleDataChange, activeDataSources] + [tableConfig.columns, formData, tableData, calculateAll, handleDataChange, activeDataSources], ); // ์ปฌ๋Ÿผ ๋ชจ๋“œ/์กฐํšŒ ์˜ต์…˜ ๋ณ€๊ฒฝ ํ•ธ๋“ค๋Ÿฌ @@ -1093,7 +1108,7 @@ export function TableSectionRenderer({ // ํ•ด๋‹น ์ปฌ๋Ÿผ์˜ ๋ชจ๋“  ํ–‰ ๋ฐ์ดํ„ฐ ์žฌ์กฐํšŒ const column = tableConfig.columns.find((col) => col.field === columnField); - + // lookup ์„ค์ •์ด ์žˆ๋Š” ๊ฒฝ์šฐ (์ƒˆ๋กœ์šด ์กฐํšŒ ๊ธฐ๋Šฅ) if (column?.lookup?.enabled && column.lookup.options) { const selectedOption = column.lookup.options.find((opt) => opt.id === optionId); @@ -1140,11 +1155,13 @@ export function TableSectionRenderer({ // ์™ธ๋ถ€ ํ…Œ์ด๋ธ” ์กฐํšŒ ์„ค์ • externalLookup: cond.externalLookup, // ๊ฐ’ ๋ณ€ํ™˜ ์„ค์ • ์ „๋‹ฌ (๋ ˆ๊ฑฐ์‹œ ํ˜ธํ™˜) - transform: cond.transform?.enabled ? { - tableName: cond.transform.tableName, - matchColumn: cond.transform.matchColumn, - resultColumn: cond.transform.resultColumn, - } : undefined, + transform: cond.transform?.enabled + ? { + tableName: cond.transform.tableName, + matchColumn: cond.transform.matchColumn, + resultColumn: cond.transform.resultColumn, + } + : undefined, }; }); @@ -1156,15 +1173,15 @@ export function TableSectionRenderer({ joinConditions, row, sourceData, - formData + formData, ); - + if (value !== undefined) { newValue = value; } return { ...row, [columnField]: newValue }; - }) + }), ); // ๊ณ„์‚ฐ ํ•„๋“œ ์—…๋ฐ์ดํŠธ @@ -1199,14 +1216,14 @@ export function TableSectionRenderer({ } return { ...row, [columnField]: newValue }; - }) + }), ); // ๊ณ„์‚ฐ ํ•„๋“œ ์—…๋ฐ์ดํŠธ const calculatedData = calculateAll(updatedData); handleDataChange(calculatedData); }, - [tableConfig.columns, tableData, formData, calculateAll, handleDataChange] + [tableConfig.columns, tableData, formData, calculateAll, handleDataChange], ); // ์†Œ์Šค ํ…Œ์ด๋ธ” ์ •๋ณด @@ -1216,10 +1233,16 @@ export function TableSectionRenderer({ const sourceSearchFields = source.searchColumns; const columnLabels = source.columnLabels || {}; const modalTitle = uiConfig?.modalTitle || "ํ•ญ๋ชฉ ๊ฒ€์ƒ‰ ๋ฐ ์„ ํƒ"; - const addButtonType = uiConfig?.addButtonType || "search"; - const addButtonText = uiConfig?.addButtonText || (addButtonType === "addRow" ? "ํ•ญ๋ชฉ ์ถ”๊ฐ€" : "ํ•ญ๋ชฉ ๊ฒ€์ƒ‰"); const multiSelect = uiConfig?.multiSelect ?? true; + // ๋ฒ„ํŠผ ํ‘œ์‹œ ์„ค์ • (๋‘ ๋ฒ„ํŠผ ๋™์‹œ ํ‘œ์‹œ ๊ฐ€๋Šฅ) + // ๋ ˆ๊ฑฐ์‹œ ํ˜ธํ™˜: ๊ธฐ์กด addButtonType ์„ค์ •์ด ์žˆ์œผ๋ฉด ๊ทธ์— ๋งž๊ฒŒ ๋ณ€ํ™˜ + const legacyAddButtonType = uiConfig?.addButtonType; + const showSearchButton = legacyAddButtonType === "addRow" ? false : (uiConfig?.showSearchButton ?? true); + const showAddRowButton = legacyAddButtonType === "addRow" ? true : (uiConfig?.showAddRowButton ?? false); + const searchButtonText = uiConfig?.searchButtonText || uiConfig?.addButtonText || "ํ’ˆ๋ชฉ ๊ฒ€์ƒ‰"; + const addRowButtonText = uiConfig?.addRowButtonText || "์ง์ ‘ ์ž…๋ ฅ"; + // ๊ธฐ๋ณธ ํ•„ํ„ฐ ์กฐ๊ฑด ์ƒ์„ฑ (์‚ฌ์ „ ํ•„ํ„ฐ๋งŒ - ๋ชจ๋‹ฌ ํ•„ํ„ฐ๋Š” ItemSelectionModal์—์„œ ์ฒ˜๋ฆฌ) const baseFilterCondition: Record = useMemo(() => { const condition: Record = {}; @@ -1233,19 +1256,19 @@ export function TableSectionRenderer({ } return condition; }, [filters?.preFilters]); - + // ์กฐ๊ฑด๋ถ€ ํ…Œ์ด๋ธ”์šฉ ํ•„ํ„ฐ ์กฐ๊ฑด ์ƒ์„ฑ (์„ ํƒ๋œ ์กฐ๊ฑด๊ฐ’์œผ๋กœ ์†Œ์Šค ํ…Œ์ด๋ธ” ํ•„ํ„ฐ๋ง) const conditionalFilterCondition = useMemo(() => { const filter = { ...baseFilterCondition }; - + // ์กฐ๊ฑด๋ถ€ ํ…Œ์ด๋ธ”์˜ ์†Œ์Šค ํ•„ํ„ฐ ์„ค์ •์ด ์žˆ๊ณ , ๋ชจ๋‹ฌ์—์„œ ์„ ํƒ๋œ ์กฐ๊ฑด์ด ์žˆ์œผ๋ฉด ์ ์šฉ if (conditionalConfig?.sourceFilter?.enabled && modalCondition) { filter[conditionalConfig.sourceFilter.filterColumn] = modalCondition; } - + return filter; }, [baseFilterCondition, conditionalConfig?.sourceFilter, modalCondition]); - + // ๋ชจ๋‹ฌ ํ•„ํ„ฐ ์„ค์ •์„ ItemSelectionModal์— ์ „๋‹ฌํ•  ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ const modalFiltersForModal = useMemo(() => { if (!filters?.modalFilters) return []; @@ -1253,7 +1276,7 @@ export function TableSectionRenderer({ column: filter.column, label: filter.label || filter.column, // category ํƒ€์ž…์„ select๋กœ ๋ณ€ํ™˜ (ModalFilterConfig ํ˜ธํ™˜) - type: filter.type === "category" ? "select" as const : filter.type as "text" | "select", + type: filter.type === "category" ? ("select" as const) : (filter.type as "text" | "select"), options: filter.options, categoryRef: filter.categoryRef, defaultValue: filter.defaultValue, @@ -1265,138 +1288,156 @@ export function TableSectionRenderer({ // ============================================ // ์กฐ๊ฑด๋ถ€ ํ…Œ์ด๋ธ”: ์กฐ๊ฑด ์ฒดํฌ๋ฐ•์Šค ํ† ๊ธ€ - const handleConditionToggle = useCallback((conditionValue: string, checked: boolean) => { - setSelectedConditions((prev) => { - if (checked) { - const newConditions = [...prev, conditionValue]; - // ์ฒซ ๋ฒˆ์งธ ์กฐ๊ฑด ์„ ํƒ ์‹œ ํ•ด๋‹น ํƒญ ํ™œ์„ฑํ™” - if (prev.length === 0) { - setActiveConditionTab(conditionValue); + const handleConditionToggle = useCallback( + (conditionValue: string, checked: boolean) => { + setSelectedConditions((prev) => { + if (checked) { + const newConditions = [...prev, conditionValue]; + // ์ฒซ ๋ฒˆ์งธ ์กฐ๊ฑด ์„ ํƒ ์‹œ ํ•ด๋‹น ํƒญ ํ™œ์„ฑํ™” + if (prev.length === 0) { + setActiveConditionTab(conditionValue); + } + return newConditions; + } else { + const newConditions = prev.filter((c) => c !== conditionValue); + // ํ˜„์žฌ ํ™œ์„ฑ ํƒญ์ด ์ œ๊ฑฐ๋œ ๊ฒฝ์šฐ ๋‹ค๋ฅธ ํƒญ์œผ๋กœ ์ „ํ™˜ + if (activeConditionTab === conditionValue && newConditions.length > 0) { + setActiveConditionTab(newConditions[0]); + } + return newConditions; } - return newConditions; - } else { - const newConditions = prev.filter((c) => c !== conditionValue); - // ํ˜„์žฌ ํ™œ์„ฑ ํƒญ์ด ์ œ๊ฑฐ๋œ ๊ฒฝ์šฐ ๋‹ค๋ฅธ ํƒญ์œผ๋กœ ์ „ํ™˜ - if (activeConditionTab === conditionValue && newConditions.length > 0) { - setActiveConditionTab(newConditions[0]); - } - return newConditions; - } - }); - }, [activeConditionTab]); + }); + }, + [activeConditionTab], + ); // ์กฐ๊ฑด๋ถ€ ํ…Œ์ด๋ธ”: ์กฐ๊ฑด๋ณ„ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ - const handleConditionalDataChange = useCallback((conditionValue: string, newData: any[]) => { - setConditionalTableData((prev) => ({ - ...prev, - [conditionValue]: newData, - })); - - // ๋ถ€๋ชจ์—๊ฒŒ ์กฐ๊ฑด๋ณ„ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์•Œ๋ฆผ - if (onConditionalTableDataChange) { - onConditionalTableDataChange(conditionValue, newData); - } - - // ์ „์ฒด ๋ฐ์ดํ„ฐ๋ฅผ flat array๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ onTableDataChange ํ˜ธ์ถœ - // (์ €์žฅ ์‹œ ์กฐ๊ฑด ์ปฌ๋Ÿผ ๊ฐ’์ด ์ž๋™์œผ๋กœ ์ถ”๊ฐ€๋จ) - const conditionColumn = conditionalConfig?.conditionColumn; - const allData: any[] = []; - - // ํ˜„์žฌ ๋ณ€๊ฒฝ๋œ ์กฐ๊ฑด์˜ ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ - const updatedConditionalData = { ...conditionalTableData, [conditionValue]: newData }; - - for (const [condition, data] of Object.entries(updatedConditionalData)) { - for (const row of data) { - allData.push({ - ...row, - ...(conditionColumn ? { [conditionColumn]: condition } : {}), - }); + const handleConditionalDataChange = useCallback( + (conditionValue: string, newData: any[]) => { + setConditionalTableData((prev) => ({ + ...prev, + [conditionValue]: newData, + })); + + // ๋ถ€๋ชจ์—๊ฒŒ ์กฐ๊ฑด๋ณ„ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์•Œ๋ฆผ + if (onConditionalTableDataChange) { + onConditionalTableDataChange(conditionValue, newData); } - } - - onTableDataChange(allData); - }, [conditionalTableData, conditionalConfig?.conditionColumn, onConditionalTableDataChange, onTableDataChange]); + + // ์ „์ฒด ๋ฐ์ดํ„ฐ๋ฅผ flat array๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ onTableDataChange ํ˜ธ์ถœ + // (์ €์žฅ ์‹œ ์กฐ๊ฑด ์ปฌ๋Ÿผ ๊ฐ’์ด ์ž๋™์œผ๋กœ ์ถ”๊ฐ€๋จ) + const conditionColumn = conditionalConfig?.conditionColumn; + const allData: any[] = []; + + // ํ˜„์žฌ ๋ณ€๊ฒฝ๋œ ์กฐ๊ฑด์˜ ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ + const updatedConditionalData = { ...conditionalTableData, [conditionValue]: newData }; + + for (const [condition, data] of Object.entries(updatedConditionalData)) { + for (const row of data) { + allData.push({ + ...row, + ...(conditionColumn ? { [conditionColumn]: condition } : {}), + }); + } + } + + onTableDataChange(allData); + }, + [conditionalTableData, conditionalConfig?.conditionColumn, onConditionalTableDataChange, onTableDataChange], + ); // ์กฐ๊ฑด๋ถ€ ํ…Œ์ด๋ธ”: ์กฐ๊ฑด๋ณ„ ํ–‰ ๋ณ€๊ฒฝ - const handleConditionalRowChange = useCallback((conditionValue: string, index: number, newRow: any) => { - const calculatedRow = calculateRow(newRow); - const currentData = conditionalTableData[conditionValue] || []; - const newData = [...currentData]; - newData[index] = calculatedRow; - handleConditionalDataChange(conditionValue, newData); - }, [conditionalTableData, calculateRow, handleConditionalDataChange]); + const handleConditionalRowChange = useCallback( + (conditionValue: string, index: number, newRow: any) => { + const calculatedRow = calculateRow(newRow); + const currentData = conditionalTableData[conditionValue] || []; + const newData = [...currentData]; + newData[index] = calculatedRow; + handleConditionalDataChange(conditionValue, newData); + }, + [conditionalTableData, calculateRow, handleConditionalDataChange], + ); // ์กฐ๊ฑด๋ถ€ ํ…Œ์ด๋ธ”: ์กฐ๊ฑด๋ณ„ ํ–‰ ์‚ญ์ œ - const handleConditionalRowDelete = useCallback((conditionValue: string, index: number) => { - const currentData = conditionalTableData[conditionValue] || []; - const newData = currentData.filter((_, i) => i !== index); - handleConditionalDataChange(conditionValue, newData); - }, [conditionalTableData, handleConditionalDataChange]); + const handleConditionalRowDelete = useCallback( + (conditionValue: string, index: number) => { + const currentData = conditionalTableData[conditionValue] || []; + const newData = currentData.filter((_, i) => i !== index); + handleConditionalDataChange(conditionValue, newData); + }, + [conditionalTableData, handleConditionalDataChange], + ); // ์กฐ๊ฑด๋ถ€ ํ…Œ์ด๋ธ”: ์กฐ๊ฑด๋ณ„ ์„ ํƒ ํ–‰ ์ผ๊ด„ ์‚ญ์ œ - const handleConditionalBulkDelete = useCallback((conditionValue: string) => { - const selected = conditionalSelectedRows[conditionValue] || new Set(); - if (selected.size === 0) return; - - const currentData = conditionalTableData[conditionValue] || []; - const newData = currentData.filter((_, index) => !selected.has(index)); - handleConditionalDataChange(conditionValue, newData); - - // ์„ ํƒ ์ƒํƒœ ์ดˆ๊ธฐํ™” - setConditionalSelectedRows((prev) => ({ - ...prev, - [conditionValue]: new Set(), - })); - }, [conditionalTableData, conditionalSelectedRows, handleConditionalDataChange]); + const handleConditionalBulkDelete = useCallback( + (conditionValue: string) => { + const selected = conditionalSelectedRows[conditionValue] || new Set(); + if (selected.size === 0) return; + + const currentData = conditionalTableData[conditionValue] || []; + const newData = currentData.filter((_, index) => !selected.has(index)); + handleConditionalDataChange(conditionValue, newData); + + // ์„ ํƒ ์ƒํƒœ ์ดˆ๊ธฐํ™” + setConditionalSelectedRows((prev) => ({ + ...prev, + [conditionValue]: new Set(), + })); + }, + [conditionalTableData, conditionalSelectedRows, handleConditionalDataChange], + ); // ์กฐ๊ฑด๋ถ€ ํ…Œ์ด๋ธ”: ์•„์ดํ…œ ์ถ”๊ฐ€ (ํŠน์ • ์กฐ๊ฑด์—) - const handleConditionalAddItems = useCallback(async (items: any[]) => { - if (!modalCondition) return; - - // ๊ธฐ์กด handleAddItems ๋กœ์ง์„ ์žฌ์‚ฌ์šฉํ•˜์—ฌ ๋งคํ•‘๋œ ์•„์ดํ…œ ์ƒ์„ฑ - const mappedItems = await Promise.all( - items.map(async (sourceItem) => { - const newItem: any = {}; - - for (const col of tableConfig.columns) { - const mapping = col.valueMapping; - - // ์†Œ์Šค ํ•„๋“œ์—์„œ ๊ฐ’ ๋ณต์‚ฌ (๊ธฐ๋ณธ) - if (!mapping) { - const sourceField = col.sourceField || col.field; - if (sourceItem[sourceField] !== undefined) { - newItem[col.field] = sourceItem[sourceField]; + const handleConditionalAddItems = useCallback( + async (items: any[]) => { + if (!modalCondition) return; + + // ๊ธฐ์กด handleAddItems ๋กœ์ง์„ ์žฌ์‚ฌ์šฉํ•˜์—ฌ ๋งคํ•‘๋œ ์•„์ดํ…œ ์ƒ์„ฑ + const mappedItems = await Promise.all( + items.map(async (sourceItem) => { + const newItem: any = {}; + + for (const col of tableConfig.columns) { + const mapping = col.valueMapping; + + // ์†Œ์Šค ํ•„๋“œ์—์„œ ๊ฐ’ ๋ณต์‚ฌ (๊ธฐ๋ณธ) + if (!mapping) { + const sourceField = col.sourceField || col.field; + if (sourceItem[sourceField] !== undefined) { + newItem[col.field] = sourceItem[sourceField]; + } + continue; } - continue; - } - - // valueMapping ์ฒ˜๋ฆฌ - if (mapping.type === "source" && mapping.sourceField) { - const value = sourceItem[mapping.sourceField]; - if (value !== undefined) { - newItem[col.field] = value; + + // valueMapping ์ฒ˜๋ฆฌ + if (mapping.type === "source" && mapping.sourceField) { + const value = sourceItem[mapping.sourceField]; + if (value !== undefined) { + newItem[col.field] = value; + } + } else if (mapping.type === "manual") { + newItem[col.field] = col.defaultValue || ""; + } else if (mapping.type === "internal" && mapping.internalField) { + newItem[col.field] = formData[mapping.internalField]; } - } else if (mapping.type === "manual") { - newItem[col.field] = col.defaultValue || ""; - } else if (mapping.type === "internal" && mapping.internalField) { - newItem[col.field] = formData[mapping.internalField]; } - } - - // ์›๋ณธ ์†Œ์Šค ๋ฐ์ดํ„ฐ ๋ณด์กด - newItem._sourceData = sourceItem; - - return newItem; - }) - ); - - // ํ˜„์žฌ ์กฐ๊ฑด์˜ ๋ฐ์ดํ„ฐ์— ์ถ”๊ฐ€ - const currentData = conditionalTableData[modalCondition] || []; - const newData = [...currentData, ...mappedItems]; - handleConditionalDataChange(modalCondition, newData); - - setModalOpen(false); - }, [modalCondition, tableConfig.columns, formData, conditionalTableData, handleConditionalDataChange]); + + // ์›๋ณธ ์†Œ์Šค ๋ฐ์ดํ„ฐ ๋ณด์กด + newItem._sourceData = sourceItem; + + return newItem; + }), + ); + + // ํ˜„์žฌ ์กฐ๊ฑด์˜ ๋ฐ์ดํ„ฐ์— ์ถ”๊ฐ€ + const currentData = conditionalTableData[modalCondition] || []; + const newData = [...currentData, ...mappedItems]; + handleConditionalDataChange(modalCondition, newData); + + setModalOpen(false); + }, + [modalCondition, tableConfig.columns, formData, conditionalTableData, handleConditionalDataChange], + ); // ์กฐ๊ฑด๋ถ€ ํ…Œ์ด๋ธ”: ๋ชจ๋‹ฌ ์—ด๊ธฐ (ํŠน์ • ์กฐ๊ฑด์— ๋Œ€ํ•ด) const openConditionalModal = useCallback((conditionValue: string) => { @@ -1405,62 +1446,68 @@ export function TableSectionRenderer({ }, []); // ์กฐ๊ฑด๋ถ€ ํ…Œ์ด๋ธ”: ๋นˆ ํ–‰ ์ถ”๊ฐ€ (addRow ๋ชจ๋“œ์—์„œ ์‚ฌ์šฉ) - const addEmptyRowToCondition = useCallback((conditionValue: string) => { - const newRow: Record = {}; - - // ๊ฐ ์ปฌ๋Ÿผ์˜ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋นˆ ํ–‰ ์ƒ์„ฑ - for (const col of tableConfig.columns) { - if (col.defaultValue !== undefined) { - newRow[col.field] = col.defaultValue; - } else if (col.type === "number") { - newRow[col.field] = 0; - } else if (col.type === "checkbox") { - newRow[col.field] = false; - } else { - newRow[col.field] = ""; - } - } - - // ์กฐ๊ฑด ์ปฌ๋Ÿผ์— ํ˜„์žฌ ์กฐ๊ฑด ๊ฐ’ ์„ค์ • - if (conditionalConfig?.conditionColumn) { - newRow[conditionalConfig.conditionColumn] = conditionValue; - } - - // ํ˜„์žฌ ์กฐ๊ฑด์˜ ๋ฐ์ดํ„ฐ์— ์ถ”๊ฐ€ - const currentData = conditionalTableData[conditionValue] || []; - const newData = [...currentData, newRow]; - handleConditionalDataChange(conditionValue, newData); - }, [tableConfig.columns, conditionalConfig?.conditionColumn, conditionalTableData, handleConditionalDataChange]); + const addEmptyRowToCondition = useCallback( + (conditionValue: string) => { + const newRow: Record = {}; - // ๋ฒ„ํŠผ ํด๋ฆญ ํ•ธ๋“ค๋Ÿฌ (addButtonType์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ฒŒ ๋™์ž‘) - const handleAddButtonClick = useCallback((conditionValue: string) => { - const addButtonType = tableConfig.uiConfig?.addButtonType || "search"; - - if (addButtonType === "addRow") { - // ๋นˆ ํ–‰ ์ง์ ‘ ์ถ”๊ฐ€ - addEmptyRowToCondition(conditionValue); - } else { - // ๊ฒ€์ƒ‰ ๋ชจ๋‹ฌ ์—ด๊ธฐ + // ๊ฐ ์ปฌ๋Ÿผ์˜ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋นˆ ํ–‰ ์ƒ์„ฑ + for (const col of tableConfig.columns) { + if (col.defaultValue !== undefined) { + newRow[col.field] = col.defaultValue; + } else if (col.type === "number") { + newRow[col.field] = 0; + } else if (col.type === "checkbox") { + newRow[col.field] = false; + } else { + newRow[col.field] = ""; + } + } + + // ์กฐ๊ฑด ์ปฌ๋Ÿผ์— ํ˜„์žฌ ์กฐ๊ฑด ๊ฐ’ ์„ค์ • + if (conditionalConfig?.conditionColumn) { + newRow[conditionalConfig.conditionColumn] = conditionValue; + } + + // ํ˜„์žฌ ์กฐ๊ฑด์˜ ๋ฐ์ดํ„ฐ์— ์ถ”๊ฐ€ + const currentData = conditionalTableData[conditionValue] || []; + const newData = [...currentData, newRow]; + handleConditionalDataChange(conditionValue, newData); + }, + [tableConfig.columns, conditionalConfig?.conditionColumn, conditionalTableData, handleConditionalDataChange], + ); + + // ๊ฒ€์ƒ‰ ๋ฒ„ํŠผ ํด๋ฆญ ํ•ธ๋“ค๋Ÿฌ + const handleSearchButtonClick = useCallback( + (conditionValue: string) => { openConditionalModal(conditionValue); - } - }, [tableConfig.uiConfig?.addButtonType, addEmptyRowToCondition, openConditionalModal]); + }, + [openConditionalModal], + ); + + // ํ–‰ ์ถ”๊ฐ€ ๋ฒ„ํŠผ ํด๋ฆญ ํ•ธ๋“ค๋Ÿฌ + const handleAddRowButtonClick = useCallback( + (conditionValue: string) => { + addEmptyRowToCondition(conditionValue); + }, + [addEmptyRowToCondition], + ); // ์กฐ๊ฑด๋ถ€ ํ…Œ์ด๋ธ”: ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ ๋กœ๋“œ (์ˆ˜์ • ๋ชจ๋“œ) useEffect(() => { if (!isConditionalMode) return; if (initialDataLoadedRef.current) return; - + const tableSectionKey = `_tableSection_${sectionId}`; const initialData = formData[tableSectionKey]; - + if (Array.isArray(initialData) && initialData.length > 0) { const conditionColumn = conditionalConfig?.conditionColumn; - + if (conditionColumn) { // ์กฐ๊ฑด๋ณ„๋กœ ๋ฐ์ดํ„ฐ ๊ทธ๋ฃนํ•‘ const grouped: ConditionalTableData = {}; const conditions = new Set(); - + for (const row of initialData) { const conditionValue = row[conditionColumn] || ""; if (conditionValue) { @@ -1471,15 +1518,15 @@ export function TableSectionRenderer({ conditions.add(conditionValue); } } - + setConditionalTableData(grouped); setSelectedConditions(Array.from(conditions)); - + // ์ฒซ ๋ฒˆ์งธ ์กฐ๊ฑด์„ ํ™œ์„ฑ ํƒญ์œผ๋กœ ์„ค์ • if (conditions.size > 0) { setActiveConditionTab(Array.from(conditions)[0]); } - + initialDataLoadedRef.current = true; } } @@ -1495,27 +1542,29 @@ export function TableSectionRenderer({ // ============================================ if (isConditionalMode && conditionalConfig) { const { triggerType } = conditionalConfig; - + // ์ •์  ์˜ต์…˜๊ณผ ๋™์  ์˜ต์…˜ ๋ณ‘ํ•ฉ (๋™์  ์˜ต์…˜์ด ์žˆ์œผ๋ฉด ์šฐ์„  ์‚ฌ์šฉ) // ๋นˆ value๋ฅผ ๊ฐ€์ง„ ์˜ต์…˜์€ ์ œ์™ธ (Select.Item์€ ๋นˆ ๋ฌธ์ž์—ด value๋ฅผ ํ—ˆ์šฉํ•˜์ง€ ์•Š์Œ) - const effectiveOptions = (conditionalConfig.optionSource?.enabled && dynamicOptions.length > 0 - ? dynamicOptions - : conditionalConfig.options || []).filter(opt => opt.value && opt.value.trim() !== ""); - + const effectiveOptions = ( + conditionalConfig.optionSource?.enabled && dynamicOptions.length > 0 + ? dynamicOptions + : conditionalConfig.options || [] + ).filter((opt) => opt.value && opt.value.trim() !== ""); + // ๋กœ๋”ฉ ์ค‘์ด๋ฉด ๋กœ๋”ฉ ํ‘œ์‹œ if (dynamicOptionsLoading) { return (
-
+
-
+
์กฐ๊ฑด ์˜ต์…˜์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘...
); } - + return (
{/* ์กฐ๊ฑด ์„ ํƒ UI */} @@ -1525,7 +1574,7 @@ export function TableSectionRenderer({ {effectiveOptions.map((option) => (
- + {selectedConditions.length > 0 && ( -
+
{selectedConditions.length}๊ฐœ ์œ ํ˜• ์„ ํƒ๋จ, ์ด {totalConditionalItems}๊ฐœ ํ•ญ๋ชฉ
)}
)} - + {triggerType === "dropdown" && (
์œ ํ˜• ์„ ํƒ: @@ -1566,7 +1615,7 @@ export function TableSectionRenderer({ {effectiveOptions.map((option) => ( {option.label} - {conditionalTableData[option.value]?.length > 0 && + {conditionalTableData[option.value]?.length > 0 && ` (${conditionalTableData[option.value].length})`} ))} @@ -1574,7 +1623,7 @@ export function TableSectionRenderer({
)} - + {/* ์„ ํƒ๋œ ์กฐ๊ฑด๋“ค์˜ ํ…Œ์ด๋ธ” (ํƒญ ํ˜•ํƒœ) */} {selectedConditions.length > 0 && ( @@ -1594,17 +1643,17 @@ export function TableSectionRenderer({ ); })} - + {selectedConditions.map((conditionValue) => { const data = conditionalTableData[conditionValue] || []; const selected = conditionalSelectedRows[conditionValue] || new Set(); - + return ( {/* ํ…Œ์ด๋ธ” ์ƒ๋‹จ ์ปจํŠธ๋กค */}
- + {data.length > 0 && `${data.length}๊ฐœ ํ•ญ๋ชฉ`} {selected.size > 0 && ` (${selected.size}๊ฐœ ์„ ํƒ๋จ)`} @@ -1642,20 +1691,25 @@ export function TableSectionRenderer({ ์„ ํƒ ์‚ญ์ œ ({selected.size}) )} - + {searchButtonText} + + )} + {showAddRowButton && ( + + )}
- + {/* ํ…Œ์ด๋ธ” */} )} - + {/* tabs ๋ชจ๋“œ: ๋ชจ๋“  ์˜ต์…˜์„ ํƒญ์œผ๋กœ ํ‘œ์‹œ (์„ ํƒ UI ์—†์Œ) */} {triggerType === "tabs" && effectiveOptions.length > 0 && ( - @@ -1702,16 +1756,16 @@ export function TableSectionRenderer({ ); })} - + {effectiveOptions.map((option) => { const data = conditionalTableData[option.value] || []; const selected = conditionalSelectedRows[option.value] || new Set(); - + return (
- + {data.length > 0 && `${data.length}๊ฐœ ํ•ญ๋ชฉ`} {selected.size > 0 && ` (${selected.size}๊ฐœ ์„ ํƒ๋จ)`} @@ -1728,20 +1782,25 @@ export function TableSectionRenderer({ ์„ ํƒ ์‚ญ์ œ ({selected.size}) )} - + {searchButtonText} + + )} + {showAddRowButton && ( + + )}
- + )} - + {/* ์กฐ๊ฑด์ด ์„ ํƒ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ์•ˆ๋‚ด ๋ฉ”์‹œ์ง€ (checkbox/dropdown ๋ชจ๋“œ์—์„œ๋งŒ) */} {selectedConditions.length === 0 && triggerType !== "tabs" && (
-

- {triggerType === "checkbox" - ? "์œ„์—์„œ ์œ ํ˜•์„ ์„ ํƒํ•˜์—ฌ ๊ฒ€์‚ฌํ•ญ๋ชฉ์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”." - : "์œ ํ˜•์„ ์„ ํƒํ•˜์„ธ์š”."} +

+ {triggerType === "checkbox" ? "์œ„์—์„œ ์œ ํ˜•์„ ์„ ํƒํ•˜์—ฌ ๊ฒ€์‚ฌํ•ญ๋ชฉ์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”." : "์œ ํ˜•์„ ์„ ํƒํ•˜์„ธ์š”."}

)} - + {/* ์˜ต์…˜์ด ์—†๋Š” ๊ฒฝ์šฐ ์•ˆ๋‚ด ๋ฉ”์‹œ์ง€ */} {effectiveOptions.length === 0 && (
-

- ์กฐ๊ฑด ์˜ต์…˜์ด ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. -

+

์กฐ๊ฑด ์˜ต์…˜์ด ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

)} - + {/* ํ•ญ๋ชฉ ์„ ํƒ ๋ชจ๋‹ฌ (์กฐ๊ฑด๋ถ€ ํ…Œ์ด๋ธ”์šฉ) */} {/* ์ถ”๊ฐ€ ๋ฒ„ํŠผ ์˜์—ญ */} -
+
- + {tableData.length > 0 && `${tableData.length}๊ฐœ ํ•ญ๋ชฉ`} {selectedRows.size > 0 && ` (${selectedRows.size}๊ฐœ ์„ ํƒ๋จ)`} @@ -1822,17 +1877,17 @@ export function TableSectionRenderer({ variant="outline" size="sm" onClick={() => setWidthTrigger((prev) => prev + 1)} - className="h-7 text-xs px-2" + className="h-7 px-2 text-xs" title={widthTrigger % 2 === 0 ? "๋‚ด์šฉ์— ๋งž๊ฒŒ ์ž๋™ ์กฐ์ •" : "๊ท ๋“ฑ ๋ถ„๋ฐฐ"} > {widthTrigger % 2 === 0 ? ( <> - + ์ž๋™ ๋งž์ถค ) : ( <> - + ๊ท ๋“ฑ ๋ถ„๋ฐฐ )} @@ -1841,17 +1896,20 @@ export function TableSectionRenderer({
{selectedRows.size > 0 && ( - )} - + )} + {showAddRowButton && ( + + }} + className="h-8 text-xs sm:h-10 sm:text-sm" + > + + {addRowButtonText} + + )}
diff --git a/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx b/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx index b4921a51..9edf4054 100644 --- a/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx +++ b/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx @@ -963,6 +963,13 @@ export function UniversalFormModalComponent({ } } + // ๋ณ„๋„ ํ…Œ์ด๋ธ”์— ์ €์žฅํ•ด์•ผ ํ•˜๋Š” ํ…Œ์ด๋ธ” ์„น์…˜ ๋ชฉ๋ก + const tableSectionsForSeparateTable = config.sections.filter( + (s) => s.type === "table" && + s.tableConfig?.saveConfig?.targetTable && + s.tableConfig.saveConfig.targetTable !== config.saveConfig.tableName + ); + // ํ…Œ์ด๋ธ” ์„น์…˜์ด ์žˆ๊ณ  ๋ฉ”์ธ ํ…Œ์ด๋ธ”์— ํ’ˆ๋ชฉ๋ณ„๋กœ ์ €์žฅํ•˜๋Š” ๊ฒฝ์šฐ (๊ณตํ†ต + ๊ฐœ๋ณ„ ๋ณ‘ํ•ฉ ์ €์žฅ) // targetTable์ด ์—†๊ฑฐ๋‚˜ ๋ฉ”์ธ ํ…Œ์ด๋ธ”๊ณผ ๊ฐ™์€ ๊ฒฝ์šฐ const tableSectionsForMainTable = config.sections.filter( @@ -971,6 +978,12 @@ export function UniversalFormModalComponent({ s.tableConfig.saveConfig.targetTable === config.saveConfig.tableName) ); + console.log("[saveSingleRow] ๋ฉ”์ธ ํ…Œ์ด๋ธ”:", config.saveConfig.tableName); + console.log("[saveSingleRow] ๋ฉ”์ธ ํ…Œ์ด๋ธ”์— ์ €์žฅํ•  ํ…Œ์ด๋ธ” ์„น์…˜:", tableSectionsForMainTable.map(s => s.id)); + console.log("[saveSingleRow] ๋ณ„๋„ ํ…Œ์ด๋ธ”์— ์ €์žฅํ•  ํ…Œ์ด๋ธ” ์„น์…˜:", tableSectionsForSeparateTable.map(s => s.id)); + console.log("[saveSingleRow] ํ…Œ์ด๋ธ” ์„น์…˜ ๋ฐ์ดํ„ฐ ํ‚ค:", Object.keys(tableSectionData)); + console.log("[saveSingleRow] dataToSave ํ‚ค:", Object.keys(dataToSave)); + if (tableSectionsForMainTable.length > 0) { // ๊ณตํ†ต ์ €์žฅ ํ•„๋“œ ์ˆ˜์ง‘ (sectionSaveModes ์„ค์ •์— ๋”ฐ๋ผ) const commonFieldsData: Record = {}; @@ -1050,35 +1063,51 @@ export function UniversalFormModalComponent({ // ๋ฉ”์ธ ๋ ˆ์ฝ”๋“œ ID๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ (response.data์—์„œ ๊ฐ€์ ธ์˜ค๊ธฐ) const mainRecordId = response.data?.data?.id; - // ๊ณตํ†ต ์ €์žฅ ํ•„๋“œ ์ˆ˜์ง‘ (sectionSaveModes ์„ค์ •์— ๋”ฐ๋ผ) + // ๊ณตํ†ต ์ €์žฅ ํ•„๋“œ ์ˆ˜์ง‘: ๋‹ค๋ฅธ ์„น์…˜(ํ•„๋“œ ํƒ€์ž…)์—์„œ ๊ณตํ†ต ์ €์žฅ์œผ๋กœ ์„ค์ •๋œ ํ•„๋“œ ๊ฐ’ + // ๊ธฐ๋ณธ๊ฐ’: ํ•„๋“œ ํƒ€์ž… ์„น์…˜์€ 'common', ํ…Œ์ด๋ธ” ํƒ€์ž… ์„น์…˜์€ 'individual' const commonFieldsData: Record = {}; const { sectionSaveModes } = config.saveConfig; - if (sectionSaveModes && sectionSaveModes.length > 0) { - // ๋‹ค๋ฅธ ์„น์…˜์—์„œ ๊ณตํ†ต ์ €์žฅ์œผ๋กœ ์„ค์ •๋œ ํ•„๋“œ ๊ฐ’ ์ˆ˜์ง‘ - for (const otherSection of config.sections) { - if (otherSection.id === section.id) continue; // ํ˜„์žฌ ํ…Œ์ด๋ธ” ์„น์…˜์€ ๊ฑด๋„ˆ๋›ฐ๊ธฐ - - const sectionMode = sectionSaveModes.find((s) => s.sectionId === otherSection.id); - const defaultMode = otherSection.type === "table" ? "individual" : "common"; - const sectionSaveMode = sectionMode?.saveMode || defaultMode; - - // ํ•„๋“œ ํƒ€์ž… ์„น์…˜์˜ ํ•„๋“œ๋“ค ์ฒ˜๋ฆฌ - if (otherSection.type !== "table" && otherSection.fields) { - for (const field of otherSection.fields) { - // ํ•„๋“œ๋ณ„ ์˜ค๋ฒ„๋ผ์ด๋“œ ํ™•์ธ - const fieldOverride = sectionMode?.fieldOverrides?.find((f) => f.fieldName === field.columnName); - const fieldSaveMode = fieldOverride?.saveMode || sectionSaveMode; - - // ๊ณตํ†ต ์ €์žฅ์ด๋ฉด formData์—์„œ ๊ฐ’์„ ๊ฐ€์ ธ์™€ ๋ชจ๋“  ํ’ˆ๋ชฉ์— ์ ์šฉ - if (fieldSaveMode === "common" && formData[field.columnName] !== undefined) { - commonFieldsData[field.columnName] = formData[field.columnName]; + // ๋‹ค๋ฅธ ์„น์…˜์—์„œ ๊ณตํ†ต ์ €์žฅ์œผ๋กœ ์„ค์ •๋œ ํ•„๋“œ ๊ฐ’ ์ˆ˜์ง‘ + for (const otherSection of config.sections) { + if (otherSection.id === section.id) continue; // ํ˜„์žฌ ํ…Œ์ด๋ธ” ์„น์…˜์€ ๊ฑด๋„ˆ๋›ฐ๊ธฐ + + const sectionMode = sectionSaveModes?.find((s) => s.sectionId === otherSection.id); + // ๊ธฐ๋ณธ๊ฐ’: ํ•„๋“œ ํƒ€์ž… ์„น์…˜์€ 'common', ํ…Œ์ด๋ธ” ํƒ€์ž… ์„น์…˜์€ 'individual' + const defaultMode = otherSection.type === "table" ? "individual" : "common"; + const sectionSaveMode = sectionMode?.saveMode || defaultMode; + + // ํ•„๋“œ ํƒ€์ž… ์„น์…˜์˜ ํ•„๋“œ๋“ค ์ฒ˜๋ฆฌ + if (otherSection.type !== "table" && otherSection.fields) { + for (const field of otherSection.fields) { + // ํ•„๋“œ๋ณ„ ์˜ค๋ฒ„๋ผ์ด๋“œ ํ™•์ธ + const fieldOverride = sectionMode?.fieldOverrides?.find((f) => f.fieldName === field.columnName); + const fieldSaveMode = fieldOverride?.saveMode || sectionSaveMode; + + // ๊ณตํ†ต ์ €์žฅ์ด๋ฉด formData์—์„œ ๊ฐ’์„ ๊ฐ€์ ธ์™€ ๋ชจ๋“  ํ’ˆ๋ชฉ์— ์ ์šฉ + if (fieldSaveMode === "common" && formData[field.columnName] !== undefined) { + commonFieldsData[field.columnName] = formData[field.columnName]; + } + } + } + + // ๐Ÿ†• ์„ ํƒ์  ํ•„๋“œ ๊ทธ๋ฃน (optionalFieldGroups)๋„ ์ฒ˜๋ฆฌ + if (otherSection.optionalFieldGroups && otherSection.optionalFieldGroups.length > 0) { + for (const optGroup of otherSection.optionalFieldGroups) { + if (optGroup.fields) { + for (const field of optGroup.fields) { + // ์„ ํƒ์  ํ•„๋“œ ๊ทธ๋ฃน์€ ๊ธฐ๋ณธ์ ์œผ๋กœ common ์ €์žฅ + if (formData[field.columnName] !== undefined) { + commonFieldsData[field.columnName] = formData[field.columnName]; + } } } } } } + console.log("[saveSingleRow] ๋ณ„๋„ ํ…Œ์ด๋ธ” ์ €์žฅ - ๊ณตํ†ต ํ•„๋“œ:", Object.keys(commonFieldsData)); + for (const item of sectionData) { // ๊ณตํ†ต ํ•„๋“œ ๋ณ‘ํ•ฉ + ๊ฐœ๋ณ„ ํ’ˆ๋ชฉ ๋ฐ์ดํ„ฐ const itemToSave = { ...commonFieldsData, ...item }; @@ -1091,15 +1120,26 @@ export function UniversalFormModalComponent({ } } + // _sourceData ๋“ฑ ๋‚ด๋ถ€ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ œ๊ฑฐ + Object.keys(itemToSave).forEach((key) => { + if (key.startsWith("_")) { + delete itemToSave[key]; + } + }); + // ๋ฉ”์ธ ๋ ˆ์ฝ”๋“œ์™€ ์—ฐ๊ฒฐ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ if (mainRecordId && config.saveConfig.primaryKeyColumn) { itemToSave[config.saveConfig.primaryKeyColumn] = mainRecordId; } - await apiClient.post( + const saveResponse = await apiClient.post( `/table-management/tables/${section.tableConfig.saveConfig.targetTable}/add`, itemToSave ); + + if (!saveResponse.data?.success) { + throw new Error(saveResponse.data?.message || `${section.title || "ํ…Œ์ด๋ธ” ์„น์…˜"} ์ €์žฅ ์‹คํŒจ`); + } } } } diff --git a/frontend/lib/registry/components/universal-form-modal/modals/TableSectionSettingsModal.tsx b/frontend/lib/registry/components/universal-form-modal/modals/TableSectionSettingsModal.tsx index ebd16c44..d82db59b 100644 --- a/frontend/lib/registry/components/universal-form-modal/modals/TableSectionSettingsModal.tsx +++ b/frontend/lib/registry/components/universal-form-modal/modals/TableSectionSettingsModal.tsx @@ -2928,54 +2928,74 @@ export function TableSectionSettingsModal({ {/* UI ์„ค์ • */}

UI ์„ค์ •

-
-
- - + + {/* ๋ฒ„ํŠผ ํ‘œ์‹œ ์„ค์ • */} +
+ +

+ ๋‘ ๋ฒ„ํŠผ์„ ๋™์‹œ์— ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. +

+
+
+ updateUiConfig({ showSearchButton: checked })} + className="scale-75" + /> +
+ ๊ฒ€์ƒ‰ ๋ฒ„ํŠผ +

๊ธฐ์กด ๋ฐ์ดํ„ฐ์—์„œ ์„ ํƒ

+
+
+
+ updateUiConfig({ showAddRowButton: checked })} + className="scale-75" + /> +
+ ํ–‰ ์ถ”๊ฐ€ ๋ฒ„ํŠผ +

๋นˆ ํ–‰ ์ง์ ‘ ์ž…๋ ฅ

+
+
+
+ +
+ {/* ๊ฒ€์ƒ‰ ๋ฒ„ํŠผ ํ…์ŠคํŠธ */}
- + updateUiConfig({ addButtonText: e.target.value })} - placeholder={tableConfig.uiConfig?.addButtonType === "addRow" ? "ํ•ญ๋ชฉ ์ถ”๊ฐ€" : "ํ•ญ๋ชฉ ๊ฒ€์ƒ‰"} + value={tableConfig.uiConfig?.searchButtonText || ""} + onChange={(e) => updateUiConfig({ searchButtonText: e.target.value })} + placeholder="ํ’ˆ๋ชฉ ๊ฒ€์ƒ‰" className="h-8 text-xs mt-1" + disabled={!(tableConfig.uiConfig?.showSearchButton ?? true)} />
+ {/* ํ–‰ ์ถ”๊ฐ€ ๋ฒ„ํŠผ ํ…์ŠคํŠธ */}
- + + updateUiConfig({ addRowButtonText: e.target.value })} + placeholder="์ง์ ‘ ์ž…๋ ฅ" + className="h-8 text-xs mt-1" + disabled={!tableConfig.uiConfig?.showAddRowButton} + /> +
+ {/* ๋ชจ๋‹ฌ ์ œ๋ชฉ */} +
+ updateUiConfig({ modalTitle: e.target.value })} placeholder="ํ•ญ๋ชฉ ๊ฒ€์ƒ‰ ๋ฐ ์„ ํƒ" className="h-8 text-xs mt-1" - disabled={tableConfig.uiConfig?.addButtonType === "addRow"} + disabled={!(tableConfig.uiConfig?.showSearchButton ?? true)} /> - {tableConfig.uiConfig?.addButtonType === "addRow" && ( -

๋นˆ ํ–‰ ์ถ”๊ฐ€ ๋ชจ๋“œ์—์„œ๋Š” ๋ชจ๋‹ฌ์ด ์—ด๋ฆฌ์ง€ ์•Š์Šต๋‹ˆ๋‹ค

- )}
+ {/* ํ…Œ์ด๋ธ” ์ตœ๋Œ€ ๋†’์ด */}
+ {/* ๋‹ค์ค‘ ์„ ํƒ ํ—ˆ์šฉ */}
diff --git a/frontend/lib/registry/components/universal-form-modal/types.ts b/frontend/lib/registry/components/universal-form-modal/types.ts index 1f2015eb..a07feed6 100644 --- a/frontend/lib/registry/components/universal-form-modal/types.ts +++ b/frontend/lib/registry/components/universal-form-modal/types.ts @@ -253,15 +253,19 @@ export interface TableSectionConfig { // 6. UI ์„ค์ • uiConfig?: { - addButtonText?: string; // ์ถ”๊ฐ€ ๋ฒ„ํŠผ ํ…์ŠคํŠธ (๊ธฐ๋ณธ: "ํ’ˆ๋ชฉ ๊ฒ€์ƒ‰") modalTitle?: string; // ๋ชจ๋‹ฌ ์ œ๋ชฉ (๊ธฐ๋ณธ: "ํ•ญ๋ชฉ ๊ฒ€์ƒ‰ ๋ฐ ์„ ํƒ") multiSelect?: boolean; // ๋‹ค์ค‘ ์„ ํƒ ํ—ˆ์šฉ (๊ธฐ๋ณธ: true) maxHeight?: string; // ํ…Œ์ด๋ธ” ์ตœ๋Œ€ ๋†’์ด (๊ธฐ๋ณธ: "400px") - // ์ถ”๊ฐ€ ๋ฒ„ํŠผ ํƒ€์ž… - // - search: ๊ฒ€์ƒ‰ ๋ชจ๋‹ฌ ์—ด๊ธฐ (๊ธฐ๋ณธ๊ฐ’) - ๊ธฐ์กด ๋ฐ์ดํ„ฐ์—์„œ ์„ ํƒ - // - addRow: ๋นˆ ํ–‰ ์ง์ ‘ ์ถ”๊ฐ€ - ์ƒˆ ๋ฐ์ดํ„ฐ ์ง์ ‘ ์ž…๋ ฅ + // ๋ฒ„ํŠผ ํ‘œ์‹œ ์„ค์ • (๋™์‹œ ํ‘œ์‹œ ๊ฐ€๋Šฅ) + showSearchButton?: boolean; // ๊ฒ€์ƒ‰ ๋ฒ„ํŠผ ํ‘œ์‹œ (๊ธฐ๋ณธ: true) + showAddRowButton?: boolean; // ํ–‰ ์ถ”๊ฐ€ ๋ฒ„ํŠผ ํ‘œ์‹œ (๊ธฐ๋ณธ: false) + searchButtonText?: string; // ๊ฒ€์ƒ‰ ๋ฒ„ํŠผ ํ…์ŠคํŠธ (๊ธฐ๋ณธ: "ํ’ˆ๋ชฉ ๊ฒ€์ƒ‰") + addRowButtonText?: string; // ํ–‰ ์ถ”๊ฐ€ ๋ฒ„ํŠผ ํ…์ŠคํŠธ (๊ธฐ๋ณธ: "์ง์ ‘ ์ž…๋ ฅ") + + // ๋ ˆ๊ฑฐ์‹œ ํ˜ธํ™˜์šฉ (deprecated) addButtonType?: "search" | "addRow"; + addButtonText?: string; }; // 7. ์กฐ๊ฑด๋ถ€ ํ…Œ์ด๋ธ” ์„ค์ • (๊ณ ๊ธ‰) diff --git a/frontend/lib/utils/buttonActions.ts b/frontend/lib/utils/buttonActions.ts index de98028a..9a6a606e 100644 --- a/frontend/lib/utils/buttonActions.ts +++ b/frontend/lib/utils/buttonActions.ts @@ -1491,6 +1491,7 @@ export class ButtonActionExecutor { * ๐Ÿ†• Universal Form Modal ํ…Œ์ด๋ธ” ์„น์…˜ ๋ณ‘ํ•ฉ ์ €์žฅ ์ฒ˜๋ฆฌ * ๋ฒ”์šฉ_ํผ_๋ชจ๋‹ฌ ๋‚ด๋ถ€์˜ ๊ณตํ†ต ํ•„๋“œ + _tableSection_ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ‘ํ•ฉํ•˜์—ฌ ํ’ˆ๋ชฉ๋ณ„๋กœ ์ €์žฅ * ์ˆ˜์ • ๋ชจ๋“œ: INSERT/UPDATE/DELETE ์ง€์› + * ๐Ÿ†• ์„น์…˜๋ณ„ ์ €์žฅ ํ…Œ์ด๋ธ”(targetTable) ์ง€์› ์ถ”๊ฐ€ */ private static async handleUniversalFormModalTableSectionSave( config: ButtonActionConfig, @@ -1514,7 +1515,66 @@ export class ButtonActionExecutor { console.log("๐ŸŽฏ [handleUniversalFormModalTableSectionSave] Universal Form Modal ๊ฐ์ง€:", universalFormModalKey); const modalData = formData[universalFormModalKey]; - + + // ๐Ÿ†• universal-form-modal ์ปดํฌ๋„ŒํŠธ ์„ค์ • ๊ฐ€์ ธ์˜ค๊ธฐ + // 1. componentConfigs์—์„œ ์ปดํฌ๋„ŒํŠธ ID๋กœ ์ฐพ๊ธฐ + // 2. allComponents์—์„œ columnName์œผ๋กœ ์ฐพ๊ธฐ + // 3. ํ™”๋ฉด ๋ ˆ์ด์•„์›ƒ API์—์„œ ๊ฐ€์ ธ์˜ค๊ธฐ + let modalComponentConfig = context.componentConfigs?.[universalFormModalKey]; + + // componentConfigs์—์„œ ์ง์ ‘ ์ฐพ์ง€ ๋ชปํ•œ ๊ฒฝ์šฐ, allComponents์—์„œ columnName์œผ๋กœ ์ฐพ๊ธฐ + if (!modalComponentConfig && context.allComponents) { + const modalComponent = context.allComponents.find( + (comp: any) => + comp.columnName === universalFormModalKey || comp.properties?.columnName === universalFormModalKey, + ); + if (modalComponent) { + modalComponentConfig = modalComponent.componentConfig || modalComponent.properties?.componentConfig; + console.log("๐ŸŽฏ [handleUniversalFormModalTableSectionSave] allComponents์—์„œ ์„ค์ • ์ฐพ์Œ:", modalComponent.id); + } + } + + // ๐Ÿ†• ์•„์ง๋„ ์„ค์ •์„ ์ฐพ์ง€ ๋ชปํ–ˆ์œผ๋ฉด ํ™”๋ฉด ๋ ˆ์ด์•„์›ƒ API์—์„œ ๊ฐ€์ ธ์˜ค๊ธฐ + if (!modalComponentConfig && screenId) { + try { + console.log("๐Ÿ” [handleUniversalFormModalTableSectionSave] ํ™”๋ฉด ๋ ˆ์ด์•„์›ƒ API์—์„œ ์„ค์ • ์กฐํšŒ:", screenId); + const { screenApi } = await import("@/lib/api/screen"); + const layoutData = await screenApi.getLayout(screenId); + + if (layoutData && layoutData.components) { + // ๋ ˆ์ด์•„์›ƒ์—์„œ universal-form-modal ์ปดํฌ๋„ŒํŠธ ์ฐพ๊ธฐ + const modalLayout = (layoutData.components as any[]).find( + (comp) => + comp.properties?.columnName === universalFormModalKey || comp.columnName === universalFormModalKey, + ); + if (modalLayout) { + modalComponentConfig = modalLayout.properties?.componentConfig || modalLayout.componentConfig; + console.log( + "๐ŸŽฏ [handleUniversalFormModalTableSectionSave] ํ™”๋ฉด ๋ ˆ์ด์•„์›ƒ์—์„œ ์„ค์ • ์ฐพ์Œ:", + modalLayout.componentId, + ); + } + } + } catch (error) { + console.warn("โš ๏ธ [handleUniversalFormModalTableSectionSave] ํ™”๋ฉด ๋ ˆ์ด์•„์›ƒ ์กฐํšŒ ์‹คํŒจ:", error); + } + } + + const sections: any[] = modalComponentConfig?.sections || []; + const saveConfig = modalComponentConfig?.saveConfig || {}; + + console.log("๐ŸŽฏ [handleUniversalFormModalTableSectionSave] ์ปดํฌ๋„ŒํŠธ ์„ค์ •:", { + hasComponentConfig: !!modalComponentConfig, + sectionsCount: sections.length, + mainTableName: saveConfig.tableName || tableName, + sectionSaveModes: saveConfig.sectionSaveModes, + sectionDetails: sections.map((s: any) => ({ + id: s.id, + type: s.type, + targetTable: s.tableConfig?.saveConfig?.targetTable, + })), + }); + // _tableSection_ ๋ฐ์ดํ„ฐ ์ถ”์ถœ const tableSectionData: Record = {}; const commonFieldsData: Record = {}; @@ -1564,10 +1624,64 @@ export class ButtonActionExecutor { let insertedCount = 0; let updatedCount = 0; let deletedCount = 0; + let mainRecordId: number | null = null; + + // ๐Ÿ†• ๋จผ์ € ๋ฉ”์ธ ํ…Œ์ด๋ธ”์— ๊ณตํ†ต ๋ฐ์ดํ„ฐ ์ €์žฅ (๋ณ„๋„ ํ…Œ์ด๋ธ”์ด ์žˆ๋Š” ๊ฒฝ์šฐ์—๋งŒ) + const hasSeparateTargetTable = sections.some( + (s) => + s.type === "table" && + s.tableConfig?.saveConfig?.targetTable && + s.tableConfig.saveConfig.targetTable !== tableName, + ); + + if (hasSeparateTargetTable && Object.keys(commonFieldsData).length > 0) { + console.log("๐Ÿ“ฆ [handleUniversalFormModalTableSectionSave] ๋ฉ”์ธ ํ…Œ์ด๋ธ”์— ๊ณตํ†ต ๋ฐ์ดํ„ฐ ์ €์žฅ:", tableName); + + const mainRowToSave = { ...commonFieldsData, ...userInfo }; + + // ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ œ๊ฑฐ + Object.keys(mainRowToSave).forEach((key) => { + if (key.startsWith("_")) { + delete mainRowToSave[key]; + } + }); + + console.log("๐Ÿ“ฆ [handleUniversalFormModalTableSectionSave] ๋ฉ”์ธ ํ…Œ์ด๋ธ” ์ €์žฅ ๋ฐ์ดํ„ฐ:", mainRowToSave); + + const mainSaveResult = await DynamicFormApi.saveFormData({ + screenId: screenId!, + tableName: tableName!, + data: mainRowToSave, + }); + + if (!mainSaveResult.success) { + throw new Error(mainSaveResult.message || "๋ฉ”์ธ ๋ฐ์ดํ„ฐ ์ €์žฅ ์‹คํŒจ"); + } + + mainRecordId = mainSaveResult.data?.id || null; + console.log("โœ… [handleUniversalFormModalTableSectionSave] ๋ฉ”์ธ ํ…Œ์ด๋ธ” ์ €์žฅ ์™„๋ฃŒ, ID:", mainRecordId); + } // ๊ฐ ํ…Œ์ด๋ธ” ์„น์…˜ ์ฒ˜๋ฆฌ for (const [sectionId, currentItems] of Object.entries(tableSectionData)) { - console.log(`๐Ÿ”„ [handleUniversalFormModalTableSectionSave] ์„น์…˜ ${sectionId} ์ฒ˜๋ฆฌ ์‹œ์ž‘: ${currentItems.length}๊ฐœ ํ’ˆ๋ชฉ`); + console.log( + `๐Ÿ”„ [handleUniversalFormModalTableSectionSave] ์„น์…˜ ${sectionId} ์ฒ˜๋ฆฌ ์‹œ์ž‘: ${currentItems.length}๊ฐœ ํ’ˆ๋ชฉ`, + ); + + // ๐Ÿ†• ํ•ด๋‹น ์„น์…˜์˜ ์„ค์ • ์ฐพ๊ธฐ + const sectionConfig = sections.find((s) => s.id === sectionId); + const targetTableName = sectionConfig?.tableConfig?.saveConfig?.targetTable; + + // ๐Ÿ†• ์‹ค์ œ ์ €์žฅํ•  ํ…Œ์ด๋ธ” ๊ฒฐ์ • + // - targetTable์ด ์žˆ์œผ๋ฉด ํ•ด๋‹น ํ…Œ์ด๋ธ”์— ์ €์žฅ + // - targetTable์ด ์—†์œผ๋ฉด ๋ฉ”์ธ ํ…Œ์ด๋ธ”์— ์ €์žฅ + const saveTableName = targetTableName || tableName!; + + console.log(`๐Ÿ“Š [handleUniversalFormModalTableSectionSave] ์„น์…˜ ${sectionId} ์ €์žฅ ํ…Œ์ด๋ธ”:`, { + targetTableName, + saveTableName, + isMainTable: saveTableName === tableName, + }); // 1๏ธโƒฃ ์‹ ๊ทœ ํ’ˆ๋ชฉ INSERT (id๊ฐ€ ์—†๋Š” ํ•ญ๋ชฉ) const newItems = currentItems.filter((item) => !item.id); @@ -1581,11 +1695,16 @@ export class ButtonActionExecutor { } }); - console.log("โž• [INSERT] ์‹ ๊ทœ ํ’ˆ๋ชฉ:", rowToSave); + // ๐Ÿ†• ๋ฉ”์ธ ๋ ˆ์ฝ”๋“œ ID ์—ฐ๊ฒฐ (๋ณ„๋„ ํ…Œ์ด๋ธ”์— ์ €์žฅํ•˜๋Š” ๊ฒฝ์šฐ) + if (targetTableName && mainRecordId && saveConfig.primaryKeyColumn) { + rowToSave[saveConfig.primaryKeyColumn] = mainRecordId; + } + + console.log("โž• [INSERT] ์‹ ๊ทœ ํ’ˆ๋ชฉ:", { tableName: saveTableName, data: rowToSave }); const saveResult = await DynamicFormApi.saveFormData({ screenId: screenId!, - tableName: tableName!, + tableName: saveTableName, data: rowToSave, }); @@ -1612,9 +1731,14 @@ export class ButtonActionExecutor { }); delete rowToSave.id; // id ์ œ๊ฑฐํ•˜์—ฌ INSERT + // ๐Ÿ†• ๋ฉ”์ธ ๋ ˆ์ฝ”๋“œ ID ์—ฐ๊ฒฐ (๋ณ„๋„ ํ…Œ์ด๋ธ”์— ์ €์žฅํ•˜๋Š” ๊ฒฝ์šฐ) + if (targetTableName && mainRecordId && saveConfig.primaryKeyColumn) { + rowToSave[saveConfig.primaryKeyColumn] = mainRecordId; + } + const saveResult = await DynamicFormApi.saveFormData({ screenId: screenId!, - tableName: tableName!, + tableName: saveTableName, data: rowToSave, }); @@ -1631,14 +1755,14 @@ export class ButtonActionExecutor { const hasChanges = this.checkForChanges(originalItem, currentDataWithCommon); if (hasChanges) { - console.log(`๐Ÿ”„ [UPDATE] ํ’ˆ๋ชฉ ์ˆ˜์ •: id=${item.id}`); + console.log(`๐Ÿ”„ [UPDATE] ํ’ˆ๋ชฉ ์ˆ˜์ •: id=${item.id}, tableName=${saveTableName}`); // ๋ณ€๊ฒฝ๋œ ํ•„๋“œ๋งŒ ์ถ”์ถœํ•˜์—ฌ ๋ถ€๋ถ„ ์—…๋ฐ์ดํŠธ const updateResult = await DynamicFormApi.updateFormDataPartial( item.id, originalItem, currentDataWithCommon, - tableName!, + saveTableName, ); if (!updateResult.success) { @@ -1656,9 +1780,9 @@ export class ButtonActionExecutor { const deletedItems = originalGroupedData.filter((orig) => orig.id && !currentIds.has(orig.id)); for (const deletedItem of deletedItems) { - console.log(`๐Ÿ—‘๏ธ [DELETE] ํ’ˆ๋ชฉ ์‚ญ์ œ: id=${deletedItem.id}`); + console.log(`๐Ÿ—‘๏ธ [DELETE] ํ’ˆ๋ชฉ ์‚ญ์ œ: id=${deletedItem.id}, tableName=${saveTableName}`); - const deleteResult = await DynamicFormApi.deleteFormDataFromTable(tableName!, deletedItem.id); + const deleteResult = await DynamicFormApi.deleteFormDataFromTable(saveTableName, deletedItem.id); if (!deleteResult.success) { throw new Error(deleteResult.message || "ํ’ˆ๋ชฉ ์‚ญ์ œ ์‹คํŒจ"); @@ -1670,6 +1794,7 @@ export class ButtonActionExecutor { // ๊ฒฐ๊ณผ ๋ฉ”์‹œ์ง€ ์ƒ์„ฑ const resultParts: string[] = []; + if (mainRecordId) resultParts.push("๋ฉ”์ธ ๋ฐ์ดํ„ฐ ์ €์žฅ"); if (insertedCount > 0) resultParts.push(`${insertedCount}๊ฐœ ์ถ”๊ฐ€`); if (updatedCount > 0) resultParts.push(`${updatedCount}๊ฐœ ์ˆ˜์ •`); if (deletedCount > 0) resultParts.push(`${deletedCount}๊ฐœ ์‚ญ์ œ`); @@ -2145,17 +2270,20 @@ export class ButtonActionExecutor { * ์—ฐ๊ด€ ๋ฐ์ดํ„ฐ ๋ฒ„ํŠผ์˜ ์„ ํƒ ๋ฐ์ดํ„ฐ๋กœ ๋ชจ๋‹ฌ ์—ด๊ธฐ * RelatedDataButtons ์ปดํฌ๋„ŒํŠธ์—์„œ ์„ ํƒ๋œ ๋ฒ„ํŠผ ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ๋‹ฌ๋กœ ์ „๋‹ฌ */ - private static async handleOpenRelatedModal(config: ButtonActionConfig, context: ButtonActionContext): Promise { + private static async handleOpenRelatedModal( + config: ButtonActionConfig, + context: ButtonActionContext, + ): Promise { // ๋ฒ„ํŠผ ์„ค์ •์—์„œ targetScreenId ๊ฐ€์ ธ์˜ค๊ธฐ (์—ฌ๋Ÿฌ ์œ„์น˜์—์„œ ํ™•์ธ) const targetScreenId = config.relatedModalConfig?.targetScreenId || config.targetScreenId; - + console.log("๐Ÿ” [openRelatedModal] ์„ค์ • ํ™•์ธ:", { config, relatedModalConfig: config.relatedModalConfig, targetScreenId: config.targetScreenId, finalTargetScreenId: targetScreenId, }); - + if (!targetScreenId) { console.error("โŒ [openRelatedModal] targetScreenId๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."); toast.error("๋ชจ๋‹ฌ ํ™”๋ฉด ID๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."); @@ -2164,13 +2292,13 @@ export class ButtonActionExecutor { // RelatedDataButtons์—์„œ ์„ ํƒ๋œ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ const relatedData = window.__relatedButtonsSelectedData; - + console.log("๐Ÿ” [openRelatedModal] RelatedDataButtons ๋ฐ์ดํ„ฐ:", { relatedData, selectedItem: relatedData?.selectedItem, config: relatedData?.config, }); - + if (!relatedData?.selectedItem) { console.warn("โš ๏ธ [openRelatedModal] ์„ ํƒ๋œ ๋ฒ„ํŠผ์ด ์—†์Šต๋‹ˆ๋‹ค."); toast.warning("๋จผ์ € ๋ฒ„ํŠผ์„ ์„ ํƒํ•ด์ฃผ์„ธ์š”."); @@ -2181,14 +2309,14 @@ export class ButtonActionExecutor { // ๋ฐ์ดํ„ฐ ๋งคํ•‘ ์ ์šฉ const initialData: Record = {}; - + console.log("๐Ÿ” [openRelatedModal] ๋งคํ•‘ ์„ค์ •:", { modalLink: relatedConfig?.modalLink, dataMapping: relatedConfig?.modalLink?.dataMapping, }); - + if (relatedConfig?.modalLink?.dataMapping && relatedConfig.modalLink.dataMapping.length > 0) { - relatedConfig.modalLink.dataMapping.forEach(mapping => { + relatedConfig.modalLink.dataMapping.forEach((mapping) => { console.log("๐Ÿ” [openRelatedModal] ๋งคํ•‘ ์ฒ˜๋ฆฌ:", { mapping, sourceField: mapping.sourceField, @@ -2197,7 +2325,7 @@ export class ButtonActionExecutor { selectedItemId: selectedItem.id, rawDataValue: selectedItem.rawData[mapping.sourceField], }); - + if (mapping.sourceField === "value") { initialData[mapping.targetField] = selectedItem.value; } else if (mapping.sourceField === "id") { @@ -2219,18 +2347,20 @@ export class ButtonActionExecutor { }); // ๋ชจ๋‹ฌ ์—ด๊ธฐ ์ด๋ฒคํŠธ ๋ฐœ์ƒ (ScreenModal์€ editData๋ฅผ ์‚ฌ์šฉ) - window.dispatchEvent(new CustomEvent("openScreenModal", { - detail: { - screenId: targetScreenId, - title: config.modalTitle, - description: config.modalDescription, - editData: initialData, // ScreenModal์€ editData๋กœ ํผ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์Œ - onSuccess: () => { - // ์„ฑ๊ณต ํ›„ ๋ฐ์ดํ„ฐ ์ƒˆ๋กœ๊ณ ์นจ - window.dispatchEvent(new CustomEvent("refreshTableData")); + window.dispatchEvent( + new CustomEvent("openScreenModal", { + detail: { + screenId: targetScreenId, + title: config.modalTitle, + description: config.modalDescription, + editData: initialData, // ScreenModal์€ editData๋กœ ํผ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์Œ + onSuccess: () => { + // ์„ฑ๊ณต ํ›„ ๋ฐ์ดํ„ฐ ์ƒˆ๋กœ๊ณ ์นจ + window.dispatchEvent(new CustomEvent("refreshTableData")); + }, }, - }, - })); + }), + ); return true; } @@ -3296,10 +3426,7 @@ export class ButtonActionExecutor { * EditModal ๋“ฑ ์™ธ๋ถ€์—์„œ๋„ ํ˜ธ์ถœ ๊ฐ€๋Šฅํ•˜๋„๋ก public์œผ๋กœ ๋ณ€๊ฒฝ * ๋‹ค์ค‘ ์ œ์–ด ์ˆœ์ฐจ ์‹คํ–‰ ์ง€์› */ - public static async executeAfterSaveControl( - config: ButtonActionConfig, - context: ButtonActionContext, - ): Promise { + public static async executeAfterSaveControl(config: ButtonActionConfig, context: ButtonActionContext): Promise { console.log("๐ŸŽฏ ์ €์žฅ ํ›„ ์ œ์–ด ์‹คํ–‰:", { enableDataflowControl: config.enableDataflowControl, dataflowConfig: config.dataflowConfig, @@ -4742,7 +4869,7 @@ export class ButtonActionExecutor { // ์ถ”์  ์ค‘์ธ์ง€ ํ™•์ธ (์ƒˆ๋กœ๊ณ ์นจ ํ›„์—๋„ DB ์ƒํƒœ ๊ธฐ๋ฐ˜ ์ข…๋ฃŒ ๊ฐ€๋Šฅํ•˜๋„๋ก ์ˆ˜์ •) const isTrackingActive = !!this.trackingIntervalId; - + if (!isTrackingActive) { // ์ถ”์  ์ค‘์ด ์•„๋‹ˆ์–ด๋„ DB ์ƒํƒœ ๋ณ€๊ฒฝ์€ ์ง„ํ–‰ (์ƒˆ๋กœ๊ณ ์นจ ํ›„ ์ข…๋ฃŒ ์ง€์›) console.log("โš ๏ธ [handleTrackingStop] trackingIntervalId ์—†์Œ - DB ์ƒํƒœ ๊ธฐ๋ฐ˜ ์ข…๋ฃŒ ์ง„ํ–‰"); @@ -4758,25 +4885,26 @@ export class ButtonActionExecutor { let dbDeparture: string | null = null; let dbArrival: string | null = null; let dbVehicleId: string | null = null; - + const userId = context.userId || this.trackingUserId; if (userId) { try { const { apiClient } = await import("@/lib/api/client"); - const statusTableName = config.trackingStatusTableName || this.trackingConfig?.trackingStatusTableName || context.tableName || "vehicles"; + const statusTableName = + config.trackingStatusTableName || + this.trackingConfig?.trackingStatusTableName || + context.tableName || + "vehicles"; const keyField = config.trackingStatusKeyField || this.trackingConfig?.trackingStatusKeyField || "user_id"; - + // DB์—์„œ ํ˜„์žฌ ์ฐจ๋Ÿ‰ ์ •๋ณด ์กฐํšŒ - const vehicleResponse = await apiClient.post( - `/table-management/tables/${statusTableName}/data`, - { - page: 1, - size: 1, - search: { [keyField]: userId }, - autoFilter: true, - }, - ); - + const vehicleResponse = await apiClient.post(`/table-management/tables/${statusTableName}/data`, { + page: 1, + size: 1, + search: { [keyField]: userId }, + autoFilter: true, + }); + const vehicleData = vehicleResponse.data?.data?.data?.[0] || vehicleResponse.data?.data?.rows?.[0]; if (vehicleData) { dbDeparture = vehicleData.departure || null; @@ -4792,14 +4920,18 @@ export class ButtonActionExecutor { // ๋งˆ์ง€๋ง‰ ์œ„์น˜ ์ €์žฅ (์ถ”์  ์ค‘์ด์—ˆ๋˜ ๊ฒฝ์šฐ์—๋งŒ) if (isTrackingActive) { // DB ๊ฐ’ ์šฐ์„ , ์—†์œผ๋ฉด formData ์‚ฌ์šฉ - const departure = dbDeparture || - this.trackingContext?.formData?.[this.trackingConfig?.trackingDepartureField || "departure"] || null; - const arrival = dbArrival || - this.trackingContext?.formData?.[this.trackingConfig?.trackingArrivalField || "arrival"] || null; + const departure = + dbDeparture || + this.trackingContext?.formData?.[this.trackingConfig?.trackingDepartureField || "departure"] || + null; + const arrival = + dbArrival || this.trackingContext?.formData?.[this.trackingConfig?.trackingArrivalField || "arrival"] || null; const departureName = this.trackingContext?.formData?.["departure_name"] || null; const destinationName = this.trackingContext?.formData?.["destination_name"] || null; - const vehicleId = dbVehicleId || - this.trackingContext?.formData?.[this.trackingConfig?.trackingVehicleIdField || "vehicle_id"] || null; + const vehicleId = + dbVehicleId || + this.trackingContext?.formData?.[this.trackingConfig?.trackingVehicleIdField || "vehicle_id"] || + null; await this.saveLocationToHistory( tripId, @@ -5681,10 +5813,10 @@ export class ButtonActionExecutor { const columnMappings = quickInsertConfig.columnMappings || []; for (const mapping of columnMappings) { - console.log(`๐Ÿ“ ๋งคํ•‘ ์ฒ˜๋ฆฌ ์‹œ์ž‘:`, mapping); - + console.log("๐Ÿ“ ๋งคํ•‘ ์ฒ˜๋ฆฌ ์‹œ์ž‘:", mapping); + if (!mapping.targetColumn) { - console.log(`๐Ÿ“ targetColumn ์—†์Œ, ์Šคํ‚ต`); + console.log("๐Ÿ“ targetColumn ์—†์Œ, ์Šคํ‚ต"); continue; } @@ -5692,12 +5824,12 @@ export class ButtonActionExecutor { switch (mapping.sourceType) { case "component": - console.log(`๐Ÿ“ component ํƒ€์ž… ์ฒ˜๋ฆฌ:`, { + console.log("๐Ÿ“ component ํƒ€์ž… ์ฒ˜๋ฆฌ:", { sourceComponentId: mapping.sourceComponentId, sourceColumnName: mapping.sourceColumnName, targetColumn: mapping.targetColumn, }); - + // ์ปดํฌ๋„ŒํŠธ์˜ ํ˜„์žฌ ๊ฐ’ if (mapping.sourceComponentId) { // 1. sourceColumnName์ด ์žˆ์œผ๋ฉด ์ง์ ‘ ์‚ฌ์šฉ (๊ฐ€์žฅ ํ™•์‹คํ•œ ๋ฐฉ๋ฒ•) @@ -5705,34 +5837,34 @@ export class ButtonActionExecutor { value = formData?.[mapping.sourceColumnName]; console.log(`๐Ÿ“ ๋ฐฉ๋ฒ•1 (sourceColumnName): ${mapping.sourceColumnName} = ${value}`); } - + // 2. ์—†์œผ๋ฉด ์ปดํฌ๋„ŒํŠธ ID๋กœ ์ง์ ‘ ์ฐพ๊ธฐ if (value === undefined) { value = formData?.[mapping.sourceComponentId]; console.log(`๐Ÿ“ ๋ฐฉ๋ฒ•2 (sourceComponentId): ${mapping.sourceComponentId} = ${value}`); } - + // 3. ์—†์œผ๋ฉด allComponents์—์„œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ฐพ์•„ columnName์œผ๋กœ ์‹œ๋„ if (value === undefined && context.allComponents) { const comp = context.allComponents.find((c: any) => c.id === mapping.sourceComponentId); - console.log(`๐Ÿ“ ๋ฐฉ๋ฒ•3 ์ฐพ์€ ์ปดํฌ๋„ŒํŠธ:`, comp); + console.log("๐Ÿ“ ๋ฐฉ๋ฒ•3 ์ฐพ์€ ์ปดํฌ๋„ŒํŠธ:", comp); if (comp?.columnName) { value = formData?.[comp.columnName]; console.log(`๐Ÿ“ ๋ฐฉ๋ฒ•3 (allComponents): ${mapping.sourceComponentId} โ†’ ${comp.columnName} = ${value}`); } } - + // 4. targetColumn๊ณผ ๊ฐ™์€ ์ด๋ฆ„์˜ ํ‚ค๊ฐ€ formData์— ์žˆ์œผ๋ฉด ์‚ฌ์šฉ (ํด๋ฐฑ) if (value === undefined && mapping.targetColumn && formData?.[mapping.targetColumn] !== undefined) { value = formData[mapping.targetColumn]; console.log(`๐Ÿ“ ๋ฐฉ๋ฒ•4 (targetColumn ํด๋ฐฑ): ${mapping.targetColumn} = ${value}`); } - + // 5. ๊ทธ๋ž˜๋„ ์—†์œผ๋ฉด formData์˜ ๋ชจ๋“  ํ‚ค๋ฅผ ํ™•์ธํ•˜๊ณ  ๋กœ๊น… if (value === undefined) { console.log("๐Ÿ“ ๋ฐฉ๋ฒ•5: formData์—์„œ ๊ฐ’์„ ์ฐพ์ง€ ๋ชปํ•จ. formData ํ‚ค๋“ค:", Object.keys(formData || {})); } - + // sourceColumn์ด ์ง€์ •๋œ ๊ฒฝ์šฐ ํ•ด๋‹น ์†์„ฑ ์ถ”์ถœ if (mapping.sourceColumn && value && typeof value === "object") { value = value[mapping.sourceColumn]; @@ -5742,7 +5874,7 @@ export class ButtonActionExecutor { break; case "leftPanel": - console.log(`๐Ÿ“ leftPanel ํƒ€์ž… ์ฒ˜๋ฆฌ:`, { + console.log("๐Ÿ“ leftPanel ํƒ€์ž… ์ฒ˜๋ฆฌ:", { sourceColumn: mapping.sourceColumn, selectedLeftData: splitPanelContext?.selectedLeftData, }); @@ -5775,18 +5907,18 @@ export class ButtonActionExecutor { } console.log(`๐Ÿ“ currentUser ๊ฐ’: ${value}`); break; - + default: console.log(`๐Ÿ“ ์•Œ ์ˆ˜ ์—†๋Š” sourceType: ${mapping.sourceType}`); } console.log(`๐Ÿ“ ๋งคํ•‘ ๊ฒฐ๊ณผ: targetColumn=${mapping.targetColumn}, value=${value}, type=${typeof value}`); - + if (value !== undefined && value !== null && value !== "") { insertData[mapping.targetColumn] = value; console.log(`๐Ÿ“ insertData์— ์ถ”๊ฐ€๋จ: ${mapping.targetColumn} = ${value}`); } else { - console.log(`๐Ÿ“ ๊ฐ’์ด ๋น„์–ด์žˆ์–ด์„œ insertData์— ์ถ”๊ฐ€ ์•ˆ๋จ`); + console.log("๐Ÿ“ ๊ฐ’์ด ๋น„์–ด์žˆ์–ด์„œ insertData์— ์ถ”๊ฐ€ ์•ˆ๋จ"); } } @@ -5794,12 +5926,12 @@ export class ButtonActionExecutor { if (splitPanelContext?.selectedLeftData) { const leftData = splitPanelContext.selectedLeftData; console.log("๐Ÿ“ ์ขŒ์ธก ํŒจ๋„ ์ž๋™ ๋งคํ•‘ ์‹œ์ž‘:", leftData); - + // ๋Œ€์ƒ ํ…Œ์ด๋ธ”์˜ ์ปฌ๋Ÿผ ๋ชฉ๋ก ์กฐํšŒ let targetTableColumns: string[] = []; try { const columnsResponse = await apiClient.get( - `/table-management/tables/${quickInsertConfig.targetTable}/columns` + `/table-management/tables/${quickInsertConfig.targetTable}/columns`, ); if (columnsResponse.data?.success && columnsResponse.data?.data) { const columnsData = columnsResponse.data.data.columns || columnsResponse.data.data; @@ -5809,35 +5941,35 @@ export class ButtonActionExecutor { } catch (error) { console.error("๋Œ€์ƒ ํ…Œ์ด๋ธ” ์ปฌ๋Ÿผ ์กฐํšŒ ์‹คํŒจ:", error); } - + for (const [key, val] of Object.entries(leftData)) { // ์ด๋ฏธ ๋งคํ•‘๋œ ์ปฌ๋Ÿผ์€ ์Šคํ‚ต if (insertData[key] !== undefined) { console.log(`๐Ÿ“ ์ž๋™ ๋งคํ•‘ ์Šคํ‚ต (์ด๋ฏธ ์กด์žฌ): ${key}`); continue; } - + // ๋Œ€์ƒ ํ…Œ์ด๋ธ”์— ํ•ด๋‹น ์ปฌ๋Ÿผ์ด ์—†์œผ๋ฉด ์Šคํ‚ต if (targetTableColumns.length > 0 && !targetTableColumns.includes(key)) { console.log(`๐Ÿ“ ์ž๋™ ๋งคํ•‘ ์Šคํ‚ต (๋Œ€์ƒ ํ…Œ์ด๋ธ”์— ์—†๋Š” ์ปฌ๋Ÿผ): ${key}`); continue; } - + // ์‹œ์Šคํ…œ ์ปฌ๋Ÿผ ์ œ์™ธ (id, created_date, updated_date, writer ๋“ฑ) - const systemColumns = ['id', 'created_date', 'updated_date', 'writer', 'writer_name']; + const systemColumns = ["id", "created_date", "updated_date", "writer", "writer_name"]; if (systemColumns.includes(key)) { console.log(`๐Ÿ“ ์ž๋™ ๋งคํ•‘ ์Šคํ‚ต (์‹œ์Šคํ…œ ์ปฌ๋Ÿผ): ${key}`); continue; } - + // _label, _name ์œผ๋กœ ๋๋‚˜๋Š” ํ‘œ์‹œ์šฉ ์ปฌ๋Ÿผ ์ œ์™ธ - if (key.endsWith('_label') || key.endsWith('_name')) { + if (key.endsWith("_label") || key.endsWith("_name")) { console.log(`๐Ÿ“ ์ž๋™ ๋งคํ•‘ ์Šคํ‚ต (ํ‘œ์‹œ์šฉ ์ปฌ๋Ÿผ): ${key}`); continue; } - + // ๊ฐ’์ด ์žˆ์œผ๋ฉด ์ž๋™ ์ถ”๊ฐ€ - if (val !== undefined && val !== null && val !== '') { + if (val !== undefined && val !== null && val !== "") { insertData[key] = val; console.log(`๐Ÿ“ ์ž๋™ ๋งคํ•‘ ์ถ”๊ฐ€: ${key} = ${val}`); } @@ -5857,7 +5989,7 @@ export class ButtonActionExecutor { enabled: quickInsertConfig.duplicateCheck?.enabled, columns: quickInsertConfig.duplicateCheck?.columns, }); - + if (quickInsertConfig.duplicateCheck?.enabled && quickInsertConfig.duplicateCheck?.columns?.length > 0) { const duplicateCheckData: Record = {}; for (const col of quickInsertConfig.duplicateCheck.columns) { @@ -5877,15 +6009,20 @@ export class ButtonActionExecutor { page: 1, pageSize: 1, search: duplicateCheckData, - } + }, ); console.log("๐Ÿ“ ์ค‘๋ณต ์ฒดํฌ ์‘๋‹ต:", checkResponse.data); // ์‘๋‹ต ๊ตฌ์กฐ: { success: true, data: { data: [...], total: N } } ๋˜๋Š” { success: true, data: [...] } const existingData = checkResponse.data?.data?.data || checkResponse.data?.data || []; - console.log("๐Ÿ“ ๊ธฐ์กด ๋ฐ์ดํ„ฐ:", existingData, "๊ธธ์ด:", Array.isArray(existingData) ? existingData.length : 0); - + console.log( + "๐Ÿ“ ๊ธฐ์กด ๋ฐ์ดํ„ฐ:", + existingData, + "๊ธธ์ด:", + Array.isArray(existingData) ? existingData.length : 0, + ); + if (Array.isArray(existingData) && existingData.length > 0) { toast.error(quickInsertConfig.duplicateCheck.errorMessage || "์ด๋ฏธ ์กด์žฌํ•˜๋Š” ๋ฐ์ดํ„ฐ์ž…๋‹ˆ๋‹ค."); return false; @@ -5902,20 +6039,20 @@ export class ButtonActionExecutor { // ๋ฐ์ดํ„ฐ ์ €์žฅ const response = await apiClient.post( `/table-management/tables/${quickInsertConfig.targetTable}/add`, - insertData + insertData, ); if (response.data?.success) { console.log("โœ… Quick Insert ์ €์žฅ ์„ฑ๊ณต"); - + // ์ €์žฅ ํ›„ ๋™์ž‘ ์„ค์ • ๋กœ๊ทธ console.log("๐Ÿ“ afterInsert ์„ค์ •:", quickInsertConfig.afterInsert); - + // ๐Ÿ†• ๋ฐ์ดํ„ฐ ์ƒˆ๋กœ๊ณ ์นจ (ํ…Œ์ด๋ธ”๋ฆฌ์ŠคํŠธ, ์นด๋“œ ๋””์Šคํ”Œ๋ ˆ์ด ์ปดํฌ๋„ŒํŠธ ์ƒˆ๋กœ๊ณ ์นจ) // refreshData๊ฐ€ ๋ช…์‹œ์ ์œผ๋กœ false๊ฐ€ ์•„๋‹ˆ๋ฉด ๊ธฐ๋ณธ์ ์œผ๋กœ ์ƒˆ๋กœ๊ณ ์นจ ์‹คํ–‰ const shouldRefresh = quickInsertConfig.afterInsert?.refreshData !== false; console.log("๐Ÿ“ ๋ฐ์ดํ„ฐ ์ƒˆ๋กœ๊ณ ์นจ ์—ฌ๋ถ€:", shouldRefresh); - + if (shouldRefresh) { console.log("๐Ÿ“ ๋ฐ์ดํ„ฐ ์ƒˆ๋กœ๊ณ ์นจ ์ด๋ฒคํŠธ ๋ฐœ์†ก"); // ์ „์—ญ ์ด๋ฒคํŠธ๋กœ ํ…Œ์ด๋ธ”/์นด๋“œ ์ปดํฌ๋„ŒํŠธ๋“ค์—๊ฒŒ ์ƒˆ๋กœ๊ณ ์นจ ์•Œ๋ฆผ