From 7d6bff49aa7d737fb6bb6fa78490b99b2d595a0f Mon Sep 17 00:00:00 2001 From: kjs Date: Tue, 30 Dec 2025 15:36:28 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=ED=8F=BC=20=EC=B1=84=EB=B2=88=20=EC=98=A4?= =?UTF-8?q?=EC=9E=91=EB=8F=99=20=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/lib/utils/buttonActions.ts | 60 +++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/frontend/lib/utils/buttonActions.ts b/frontend/lib/utils/buttonActions.ts index 9a6a606e..944f7126 100644 --- a/frontend/lib/utils/buttonActions.ts +++ b/frontend/lib/utils/buttonActions.ts @@ -1608,6 +1608,66 @@ export class ButtonActionExecutor { return { handled: false, success: false }; } + // 🎯 μ±„λ²ˆ κ·œμΉ™ ν• λ‹Ή 처리 (μ €μž₯ μ‹œμ μ— μ‹€μ œ 순번 증가) + console.log("πŸ” [handleUniversalFormModalTableSectionSave] μ±„λ²ˆ κ·œμΉ™ ν• λ‹Ή 체크 μ‹œμž‘"); + + const fieldsWithNumbering: Record = {}; + + // commonFieldsData와 modalDataμ—μ„œ μ±„λ²ˆ κ·œμΉ™μ΄ μ„€μ •λœ ν•„λ“œ μ°ΎκΈ° + for (const [key, value] of Object.entries(modalData)) { + if (key.endsWith("_numberingRuleId") && value) { + const fieldName = key.replace("_numberingRuleId", ""); + fieldsWithNumbering[fieldName] = value as string; + console.log(`🎯 [handleUniversalFormModalTableSectionSave] μ±„λ²ˆ ν•„λ“œ 발견: ${fieldName} β†’ κ·œμΉ™ ${value}`); + } + } + + // formDataμ—μ„œλ„ 확인 (λͺ¨λ‹¬ 외뢀에 μžˆμ„ 수 있음) + for (const [key, value] of Object.entries(formData)) { + if (key.endsWith("_numberingRuleId") && value && !fieldsWithNumbering[key.replace("_numberingRuleId", "")]) { + const fieldName = key.replace("_numberingRuleId", ""); + fieldsWithNumbering[fieldName] = value as string; + console.log( + `🎯 [handleUniversalFormModalTableSectionSave] μ±„λ²ˆ ν•„λ“œ 발견 (formData): ${fieldName} β†’ κ·œμΉ™ ${value}`, + ); + } + } + + console.log("πŸ“‹ [handleUniversalFormModalTableSectionSave] μ±„λ²ˆ κ·œμΉ™μ΄ μ„€μ •λœ ν•„λ“œ:", fieldsWithNumbering); + + // πŸ”₯ μ €μž₯ μ‹œμ μ— allocateCode ν˜ΈμΆœν•˜μ—¬ μ‹€μ œ 순번 증가 + if (Object.keys(fieldsWithNumbering).length > 0) { + console.log("🎯 [handleUniversalFormModalTableSectionSave] μ±„λ²ˆ κ·œμΉ™ ν• λ‹Ή μ‹œμž‘ (allocateCode 호좜)"); + const { allocateNumberingCode } = await import("@/lib/api/numberingRule"); + + for (const [fieldName, ruleId] of Object.entries(fieldsWithNumbering)) { + try { + console.log( + `πŸ”„ [handleUniversalFormModalTableSectionSave] ${fieldName} ν•„λ“œμ— λŒ€ν•΄ allocateCode 호좜: ${ruleId}`, + ); + const allocateResult = await allocateNumberingCode(ruleId); + + if (allocateResult.success && allocateResult.data?.generatedCode) { + const newCode = allocateResult.data.generatedCode; + console.log( + `βœ… [handleUniversalFormModalTableSectionSave] ${fieldName} μƒˆ μ½”λ“œ ν• λ‹Ή: ${commonFieldsData[fieldName]} β†’ ${newCode}`, + ); + commonFieldsData[fieldName] = newCode; + } else { + console.warn( + `⚠️ [handleUniversalFormModalTableSectionSave] ${fieldName} μ½”λ“œ ν• λ‹Ή μ‹€νŒ¨, κΈ°μ‘΄ κ°’ μœ μ§€:`, + allocateResult.error, + ); + } + } catch (allocateError) { + console.error(`❌ [handleUniversalFormModalTableSectionSave] ${fieldName} μ½”λ“œ ν• λ‹Ή 였λ₯˜:`, allocateError); + // 였λ₯˜ μ‹œ κΈ°μ‘΄ κ°’ μœ μ§€ + } + } + } + + console.log("βœ… [handleUniversalFormModalTableSectionSave] μ±„λ²ˆ κ·œμΉ™ ν• λ‹Ή μ™„λ£Œ"); + try { // μ‚¬μš©μž 정보 μΆ”κ°€ if (!context.userId) { From bd49db16c6c55c226a28151491864797ae777203 Mon Sep 17 00:00:00 2001 From: kjs Date: Tue, 30 Dec 2025 17:45:38 +0900 Subject: [PATCH 2/3] 1 --- frontend/app/(main)/admin/systemMng/dataflow/page.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/app/(main)/admin/systemMng/dataflow/page.tsx b/frontend/app/(main)/admin/systemMng/dataflow/page.tsx index 87d937ec..d55a6cf1 100644 --- a/frontend/app/(main)/admin/systemMng/dataflow/page.tsx +++ b/frontend/app/(main)/admin/systemMng/dataflow/page.tsx @@ -51,17 +51,17 @@ export default function DataFlowPage() { // 에디터 λͺ¨λ“œμΌ λ•ŒλŠ” λ ˆμ΄μ•„μ›ƒ 없이 전체 ν™”λ©΄ μ‚¬μš© if (isEditorMode) { return ( -
+
{/* 에디터 헀더 */} -
+

λ…Έλ“œ ν”Œλ‘œμš° 에디터

-

+

λ“œλž˜κ·Έ μ•€ λ“œλ‘­μœΌλ‘œ 데이터 μ œμ–΄ ν”Œλ‘œμš°λ₯Ό μ‹œκ°μ μœΌλ‘œ μ„€κ³„ν•©λ‹ˆλ‹€

@@ -77,12 +77,12 @@ export default function DataFlowPage() { } return ( -
+
{/* νŽ˜μ΄μ§€ 헀더 */}

μ œμ–΄ 관리

-

λ…Έλ“œ 기반 데이터 ν”Œλ‘œμš°λ₯Ό μ‹œκ°μ μœΌλ‘œ μ„€κ³„ν•˜κ³  κ΄€λ¦¬ν•©λ‹ˆλ‹€

+

λ…Έλ“œ 기반 데이터 ν”Œλ‘œμš°λ₯Ό μ‹œκ°μ μœΌλ‘œ μ„€κ³„ν•˜κ³  κ΄€λ¦¬ν•©λ‹ˆλ‹€

{/* ν”Œλ‘œμš° λͺ©λ‘ */} From 5bdc903b0dee0382f21de3792b916937cf36fe8c Mon Sep 17 00:00:00 2001 From: kjs Date: Wed, 31 Dec 2025 10:54:07 +0900 Subject: [PATCH 3/3] =?UTF-8?q?=EB=B2=94=EC=9A=A9=20=ED=8F=BC=EB=AA=A8?= =?UTF-8?q?=EB=8B=AC=20=EC=A0=9C=EC=96=B4=EB=A1=9C=EC=A7=81=20=EC=97=B0?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/routes/dataflow/node-flows.ts | 67 ++++++++++++++++ frontend/lib/api/nodeFlows.ts | 38 +++++++++ frontend/lib/utils/buttonActions.ts | 78 +++++++++++++++++++ 3 files changed, 183 insertions(+) diff --git a/backend-node/src/routes/dataflow/node-flows.ts b/backend-node/src/routes/dataflow/node-flows.ts index 6de84866..177b4304 100644 --- a/backend-node/src/routes/dataflow/node-flows.ts +++ b/backend-node/src/routes/dataflow/node-flows.ts @@ -214,6 +214,73 @@ router.delete("/:flowId", async (req: Request, res: Response) => { } }); +/** + * ν”Œλ‘œμš° μ†ŒμŠ€ ν…Œμ΄λΈ” 쑰회 + * GET /api/dataflow/node-flows/:flowId/source-table + * ν”Œλ‘œμš°μ˜ 첫 번째 μ†ŒμŠ€ λ…Έλ“œ(tableSource, externalDBSource)μ—μ„œ ν…Œμ΄λΈ”λͺ… μΆ”μΆœ + */ +router.get("/:flowId/source-table", async (req: Request, res: Response) => { + try { + const { flowId } = req.params; + + const flow = await queryOne<{ flow_data: any }>( + `SELECT flow_data FROM node_flows WHERE flow_id = $1`, + [flowId] + ); + + if (!flow) { + return res.status(404).json({ + success: false, + message: "ν”Œλ‘œμš°λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.", + }); + } + + const flowData = + typeof flow.flow_data === "string" + ? JSON.parse(flow.flow_data) + : flow.flow_data; + + const nodes = flowData.nodes || []; + + // μ†ŒμŠ€ λ…Έλ“œ μ°ΎκΈ° (tableSource, externalDBSource νƒ€μž…) + const sourceNode = nodes.find( + (node: any) => + node.type === "tableSource" || node.type === "externalDBSource" + ); + + if (!sourceNode || !sourceNode.data?.tableName) { + return res.json({ + success: true, + data: { + sourceTable: null, + sourceNodeType: null, + message: "μ†ŒμŠ€ λ…Έλ“œκ°€ μ—†κ±°λ‚˜ ν…Œμ΄λΈ”λͺ…이 μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.", + }, + }); + } + + logger.info( + `ν”Œλ‘œμš° μ†ŒμŠ€ ν…Œμ΄λΈ” 쑰회: flowId=${flowId}, table=${sourceNode.data.tableName}` + ); + + return res.json({ + success: true, + data: { + sourceTable: sourceNode.data.tableName, + sourceNodeType: sourceNode.type, + sourceNodeId: sourceNode.id, + displayName: sourceNode.data.displayName, + }, + }); + } catch (error) { + logger.error("ν”Œλ‘œμš° μ†ŒμŠ€ ν…Œμ΄λΈ” 쑰회 μ‹€νŒ¨:", error); + return res.status(500).json({ + success: false, + message: "ν”Œλ‘œμš° μ†ŒμŠ€ ν…Œμ΄λΈ”μ„ μ‘°νšŒν•˜μ§€ λͺ»ν–ˆμŠ΅λ‹ˆλ‹€.", + }); + } +}); + /** * ν”Œλ‘œμš° μ‹€ν–‰ * POST /api/dataflow/node-flows/:flowId/execute diff --git a/frontend/lib/api/nodeFlows.ts b/frontend/lib/api/nodeFlows.ts index b42340d7..27bb1b96 100644 --- a/frontend/lib/api/nodeFlows.ts +++ b/frontend/lib/api/nodeFlows.ts @@ -120,3 +120,41 @@ export interface NodeExecutionSummary { duration?: number; error?: string; } + +/** + * ν”Œλ‘œμš° μ†ŒμŠ€ ν…Œμ΄λΈ” 정보 μΈν„°νŽ˜μ΄μŠ€ + */ +export interface FlowSourceTableInfo { + sourceTable: string | null; + sourceNodeType: string | null; + sourceNodeId?: string; + displayName?: string; + message?: string; +} + +/** + * ν”Œλ‘œμš° μ†ŒμŠ€ ν…Œμ΄λΈ” 쑰회 + * ν”Œλ‘œμš°μ˜ 첫 번째 μ†ŒμŠ€ λ…Έλ“œ(tableSource, externalDBSource)μ—μ„œ ν…Œμ΄λΈ”λͺ… μΆ”μΆœ + */ +export async function getFlowSourceTable(flowId: number): Promise { + try { + const response = await apiClient.get>( + `/dataflow/node-flows/${flowId}/source-table`, + ); + if (response.data.success && response.data.data) { + return response.data.data; + } + return { + sourceTable: null, + sourceNodeType: null, + message: response.data.message || "μ†ŒμŠ€ ν…Œμ΄λΈ” 정보λ₯Ό κ°€μ Έμ˜¬ 수 μ—†μŠ΅λ‹ˆλ‹€.", + }; + } catch (error) { + console.error("ν”Œλ‘œμš° μ†ŒμŠ€ ν…Œμ΄λΈ” 쑰회 μ‹€νŒ¨:", error); + return { + sourceTable: null, + sourceNodeType: null, + message: "API 호좜 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.", + }; + } +} diff --git a/frontend/lib/utils/buttonActions.ts b/frontend/lib/utils/buttonActions.ts index 944f7126..327cb87f 100644 --- a/frontend/lib/utils/buttonActions.ts +++ b/frontend/lib/utils/buttonActions.ts @@ -1864,6 +1864,84 @@ export class ButtonActionExecutor { console.log(`βœ… [handleUniversalFormModalTableSectionSave] μ™„λ£Œ: ${resultMessage}`); toast.success(`μ €μž₯ μ™„λ£Œ: ${resultMessage}`); + // πŸ†• μ €μž₯ 성곡 ν›„ μ œμ–΄ 관리 μ‹€ν–‰ (닀쀑 ν…Œμ΄λΈ” μ €μž₯ μ‹œ μ†ŒμŠ€ ν…Œμ΄λΈ”κ³Ό μΌμΉ˜ν•˜λŠ” μ„Ήμ…˜λ§Œ μ‹€ν–‰) + if (config.enableDataflowControl && config.dataflowConfig?.flowConfig?.flowId) { + const flowId = config.dataflowConfig.flowConfig.flowId; + console.log("🎯 [handleUniversalFormModalTableSectionSave] μ œμ–΄ 관리 μ‹€ν–‰ μ‹œμž‘:", { flowId }); + + try { + // ν”Œλ‘œμš° μ†ŒμŠ€ ν…Œμ΄λΈ” 쑰회 + const { getFlowSourceTable } = await import("@/lib/api/nodeFlows"); + const flowSourceInfo = await getFlowSourceTable(flowId); + + console.log("πŸ“Š [handleUniversalFormModalTableSectionSave] ν”Œλ‘œμš° μ†ŒμŠ€ ν…Œμ΄λΈ”:", flowSourceInfo); + + if (flowSourceInfo.sourceTable) { + // 각 μ„Ήμ…˜ ν™•μΈν•˜μ—¬ μ†ŒμŠ€ ν…Œμ΄λΈ”κ³Ό μΌμΉ˜ν•˜λŠ” μ„Ήμ…˜ μ°ΎκΈ° + let controlExecuted = false; + + for (const [sectionId, sectionItems] of Object.entries(tableSectionData)) { + const sectionConfig = sections.find((s: any) => s.id === sectionId); + const sectionTargetTable = sectionConfig?.tableConfig?.saveConfig?.targetTable || tableName; + + console.log(`πŸ” [handleUniversalFormModalTableSectionSave] μ„Ήμ…˜ ${sectionId} ν…Œμ΄λΈ” 비ꡐ:`, { + sectionTargetTable, + flowSourceTable: flowSourceInfo.sourceTable, + isMatch: sectionTargetTable === flowSourceInfo.sourceTable, + }); + + // μ†ŒμŠ€ ν…Œμ΄λΈ”κ³Ό μΌμΉ˜ν•˜λŠ” μ„Ήμ…˜λ§Œ μ œμ–΄ μ‹€ν–‰ + if (sectionTargetTable === flowSourceInfo.sourceTable && sectionItems.length > 0) { + console.log( + `βœ… [handleUniversalFormModalTableSectionSave] μ„Ήμ…˜ ${sectionId} β†’ ν”Œλ‘œμš° μ†ŒμŠ€ ν…Œμ΄λΈ” 일치! μ œμ–΄ μ‹€ν–‰`, + ); + + // 곡톡 ν•„λ“œ + ν•΄λ‹Ή μ„Ήμ…˜ 데이터 λ³‘ν•©ν•˜μ—¬ sourceData 생성 + const sourceData = sectionItems.map((item: any) => ({ + ...commonFieldsData, + ...item, + })); + + console.log( + `πŸ“¦ [handleUniversalFormModalTableSectionSave] μ œμ–΄ 전달 데이터: ${sourceData.length}건`, + sourceData[0], + ); + + // μ œμ–΄ κ΄€λ¦¬μš© μ»¨ν…μŠ€νŠΈ 생성 + const controlContext: ButtonActionContext = { + ...context, + selectedRowsData: sourceData, + formData: commonFieldsData, + }; + + // μ œμ–΄ 관리 μ‹€ν–‰ + await this.executeAfterSaveControl(config, controlContext); + controlExecuted = true; + break; // 첫 번째 λ§€μΉ­ μ„Ήμ…˜λ§Œ μ‹€ν–‰ + } + } + + // λ§€μΉ­λ˜λŠ” μ„Ήμ…˜μ΄ μ—†μœΌλ©΄ 메인 ν…Œμ΄λΈ” 확인 + if (!controlExecuted && tableName === flowSourceInfo.sourceTable) { + console.log("βœ… [handleUniversalFormModalTableSectionSave] 메인 ν…Œμ΄λΈ” 일치! 곡톡 ν•„λ“œλ‘œ μ œμ–΄ μ‹€ν–‰"); + + const controlContext: ButtonActionContext = { + ...context, + selectedRowsData: [commonFieldsData], + formData: commonFieldsData, + }; + + await this.executeAfterSaveControl(config, controlContext); + } + } else { + console.log("⚠️ [handleUniversalFormModalTableSectionSave] ν”Œλ‘œμš° μ†ŒμŠ€ ν…Œμ΄λΈ” μ—†μŒ - μ œμ–΄ μŠ€ν‚΅"); + } + } catch (controlError) { + console.error("❌ [handleUniversalFormModalTableSectionSave] μ œμ–΄ 관리 μ‹€ν–‰ 였λ₯˜:", controlError); + // μ œμ–΄ 관리 μ‹€νŒ¨λŠ” μ €μž₯ 성곡에 영ν–₯μ£Όμ§€ μ•ŠμŒ + } + } + // μ €μž₯ 성곡 이벀트 λ°œμƒ window.dispatchEvent(new CustomEvent("saveSuccess")); window.dispatchEvent(new CustomEvent("refreshTable"));