From 0ee49b77aed778b7981efd67a31e4ff14586c4c7 Mon Sep 17 00:00:00 2001 From: kjs Date: Fri, 12 Dec 2025 10:44:59 +0900 Subject: [PATCH] =?UTF-8?q?=EC=84=A4=EB=B9=84=20=ED=92=88=EB=AA=A9=20?= =?UTF-8?q?=ED=95=98=EB=82=98=EB=A7=8C=20=EC=B6=94=EA=B0=80=EB=90=98?= =?UTF-8?q?=EB=8A=94=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/components/common/ScreenModal.tsx | 79 ++++++++++++++++------ frontend/lib/utils/buttonActions.ts | 79 ++++++++++++++++++---- 2 files changed, 124 insertions(+), 34 deletions(-) diff --git a/frontend/components/common/ScreenModal.tsx b/frontend/components/common/ScreenModal.tsx index 4da781e6..811249a7 100644 --- a/frontend/components/common/ScreenModal.tsx +++ b/frontend/components/common/ScreenModal.tsx @@ -1,13 +1,7 @@ "use client"; import React, { useState, useEffect, useRef } from "react"; -import { - Dialog, - DialogContent, - DialogHeader, - DialogTitle, - DialogDescription, -} from "@/components/ui/dialog"; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog"; import { Checkbox } from "@/components/ui/checkbox"; import { Label } from "@/components/ui/label"; import { InteractiveScreenViewerDynamic } from "@/components/screen/InteractiveScreenViewerDynamic"; @@ -183,15 +177,66 @@ export const ScreenModal: React.FC = ({ className }) => { setOriginalData(editData); // ๐Ÿ†• ์›๋ณธ ๋ฐ์ดํ„ฐ ์ €์žฅ (UPDATE ํŒ๋‹จ์šฉ) } else { // ๐Ÿ†• ์‹ ๊ทœ ๋“ฑ๋ก ๋ชจ๋“œ: ๋ถ„ํ•  ํŒจ๋„ ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์œผ๋ฉด ๋ฏธ๋ฆฌ ์„ค์ • - // 1์ˆœ์œ„: ์ด๋ฒคํŠธ๋กœ ์ „๋‹ฌ๋œ splitPanelParentData (ํƒญ ์•ˆ์—์„œ ์—ด๋ฆฐ ๋ชจ๋‹ฌ) - // 2์ˆœ์œ„: splitPanelContext์—์„œ ์ง์ ‘ ๊ฐ€์ ธ์˜จ ๋ฐ์ดํ„ฐ (๋ถ„ํ•  ํŒจ๋„ ๋‚ด์—์„œ ์—ด๋ฆฐ ๋ชจ๋‹ฌ) - const parentData = + // ๐Ÿ”ง ์ค‘์š”: ์‹ ๊ทœ ๋“ฑ๋ก ์‹œ์—๋Š” ์—ฐ๊ฒฐ ํ•„๋“œ(equipment_code ๋“ฑ)๋งŒ ์ „๋‹ฌํ•ด์•ผ ํ•จ + // ๋ชจ๋“  ํ•„๋“œ๋ฅผ ์ „๋‹ฌํ•˜๋ฉด ๋™์ผํ•œ ์ปฌ๋Ÿผ๋ช…์ด ์žˆ์„ ๋•Œ ๋ถ€๋ชจ ๊ฐ’์ด ๋“ค์–ด๊ฐ€๋Š” ๋ฌธ์ œ ๋ฐœ์ƒ + // ์˜ˆ: ์„ค๋น„์˜ manufacturer๊ฐ€ ์†Œ๋ชจํ’ˆ์˜ manufacturer๋กœ ๋“ค์–ด๊ฐ + + // parentDataMapping์—์„œ ๋ช…์‹œ๋œ ํ•„๋“œ๋งŒ ์ถ”์ถœ + const parentDataMapping = splitPanelContext?.parentDataMapping || []; + + // ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ ์†Œ์Šค + const rawParentData = splitPanelParentData && Object.keys(splitPanelParentData).length > 0 ? splitPanelParentData - : splitPanelContext?.getMappedParentData() || {}; + : splitPanelContext?.selectedLeftData || {}; + + // ๐Ÿ”ง ์‹ ๊ทœ ๋“ฑ๋ก ๋ชจ๋“œ์—์„œ๋Š” ์—ฐ๊ฒฐ์— ํ•„์š”ํ•œ ํ•„๋“œ๋งŒ ์ „๋‹ฌ + const parentData: Record = {}; + + // ํ•„์ˆ˜ ์—ฐ๊ฒฐ ํ•„๋“œ: company_code (๋ฉ€ํ‹ฐํ…Œ๋„Œ์‹œ) + if (rawParentData.company_code) { + parentData.company_code = rawParentData.company_code; + } + + // parentDataMapping์— ์ •์˜๋œ ํ•„๋“œ๋งŒ ์ „๋‹ฌ + for (const mapping of parentDataMapping) { + const sourceValue = rawParentData[mapping.sourceColumn]; + if (sourceValue !== undefined && sourceValue !== null) { + parentData[mapping.targetColumn] = sourceValue; + console.log( + `๐Ÿ”— [ScreenModal] ๋งคํ•‘ ํ•„๋“œ ์ „๋‹ฌ: ${mapping.sourceColumn} โ†’ ${mapping.targetColumn} = ${sourceValue}`, + ); + } + } + + // parentDataMapping์ด ๋น„์–ด์žˆ์œผ๋ฉด ์—ฐ๊ฒฐ ํ•„๋“œ ์ž๋™ ๊ฐ์ง€ (equipment_code, xxx_code, xxx_id ํŒจํ„ด) + if (parentDataMapping.length === 0) { + const linkFieldPatterns = ["_code", "_id"]; + const excludeFields = [ + "id", + "company_code", + "created_date", + "updated_date", + "created_at", + "updated_at", + "writer", + ]; + + for (const [key, value] of Object.entries(rawParentData)) { + if (excludeFields.includes(key)) continue; + if (value === undefined || value === null) continue; + + // ์—ฐ๊ฒฐ ํ•„๋“œ ํŒจํ„ด ํ™•์ธ + const isLinkField = linkFieldPatterns.some((pattern) => key.endsWith(pattern)); + if (isLinkField) { + parentData[key] = value; + console.log(`๐Ÿ”— [ScreenModal] ์—ฐ๊ฒฐ ํ•„๋“œ ์ž๋™ ๊ฐ์ง€: ${key} = ${value}`); + } + } + } if (Object.keys(parentData).length > 0) { - console.log("๐Ÿ”— [ScreenModal] ๋ถ„ํ•  ํŒจ๋„ ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ ์ดˆ๊ธฐ๊ฐ’ ์„ค์ •:", parentData); + console.log("๐Ÿ”— [ScreenModal] ๋ถ„ํ•  ํŒจ๋„ ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ ์ดˆ๊ธฐ๊ฐ’ ์„ค์ • (์—ฐ๊ฒฐ ํ•„๋“œ๋งŒ):", parentData); setFormData(parentData); } else { setFormData({}); @@ -604,19 +649,15 @@ export const ScreenModal: React.FC = ({ className }) => {
{modalState.title} {modalState.description && !loading && ( - - {modalState.description} - + {modalState.description} )} {loading && ( - - {loading ? "ํ™”๋ฉด์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค..." : ""} - + {loading ? "ํ™”๋ฉด์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค..." : ""} )}
-
+
{loading ? (
diff --git a/frontend/lib/utils/buttonActions.ts b/frontend/lib/utils/buttonActions.ts index 1ced2836..e7da5833 100644 --- a/frontend/lib/utils/buttonActions.ts +++ b/frontend/lib/utils/buttonActions.ts @@ -681,13 +681,52 @@ export class ButtonActionExecutor { console.log("๐Ÿ“ฆ ์ตœ์ข… formData:", JSON.stringify(formData, null, 2)); // ๐Ÿ†• ๋ถ„ํ•  ํŒจ๋„ ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ ๋ณ‘ํ•ฉ (์ขŒ์ธก ํ™”๋ฉด์—์„œ ์„ ํƒ๋œ ๋ฐ์ดํ„ฐ) - const splitPanelData = context.splitPanelParentData || {}; - if (Object.keys(splitPanelData).length > 0) { - console.log("๐Ÿ”— [handleSave] ๋ถ„ํ•  ํŒจ๋„ ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ ๋ณ‘ํ•ฉ:", splitPanelData); + // ๐Ÿ”ง ์ค‘์š”: ์‹ ๊ทœ ๋“ฑ๋ก ์‹œ์—๋Š” ์—ฐ๊ฒฐ ํ•„๋“œ(equipment_code ๋“ฑ)๋งŒ ๋ณ‘ํ•ฉํ•ด์•ผ ํ•จ + // ๋ชจ๋“  ํ•„๋“œ๋ฅผ ๋ณ‘ํ•ฉํ•˜๋ฉด ๋™์ผํ•œ ์ปฌ๋Ÿผ๋ช…์ด ์žˆ์„ ๋•Œ ๋ถ€๋ชจ ๊ฐ’์ด ๋“ค์–ด๊ฐ€๋Š” ๋ฌธ์ œ ๋ฐœ์ƒ + // ์˜ˆ: ์„ค๋น„์˜ manufacturer๊ฐ€ ์†Œ๋ชจํ’ˆ์˜ manufacturer๋กœ ๋“ค์–ด๊ฐ + const rawSplitPanelData = context.splitPanelParentData || {}; + + // INSERT ๋ชจ๋“œ์—์„œ๋Š” ์—ฐ๊ฒฐ์— ํ•„์š”ํ•œ ํ•„๋“œ๋งŒ ์ถ”์ถœ + const cleanedSplitPanelData: Record = {}; + + // ํ•„์ˆ˜ ์—ฐ๊ฒฐ ํ•„๋“œ: company_code (๋ฉ€ํ‹ฐํ…Œ๋„Œ์‹œ) + if (rawSplitPanelData.company_code) { + cleanedSplitPanelData.company_code = rawSplitPanelData.company_code; + } + + // ์—ฐ๊ฒฐ ํ•„๋“œ ํŒจํ„ด์œผ๋กœ ์ž๋™ ๊ฐ์ง€ (equipment_code, xxx_code, xxx_id ํŒจํ„ด) + const linkFieldPatterns = ["_code", "_id"]; + const excludeFields = [ + "id", + "company_code", + "created_date", + "updated_date", + "created_at", + "updated_at", + "writer", + "created_by", + "updated_by", + ]; + + for (const [key, value] of Object.entries(rawSplitPanelData)) { + if (excludeFields.includes(key)) continue; + if (value === undefined || value === null) continue; + + // ์—ฐ๊ฒฐ ํ•„๋“œ ํŒจํ„ด ํ™•์ธ + const isLinkField = linkFieldPatterns.some((pattern) => key.endsWith(pattern)); + if (isLinkField) { + cleanedSplitPanelData[key] = value; + console.log(`๐Ÿ”— [handleSave] INSERT ๋ชจ๋“œ - ์—ฐ๊ฒฐ ํ•„๋“œ๋งŒ ๋ณ‘ํ•ฉ: ${key} = ${value}`); + } + } + + if (Object.keys(rawSplitPanelData).length > 0) { + console.log("๐Ÿงน [handleSave] ์›๋ณธ ๋ถ„ํ•  ํŒจ๋„ ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ:", Object.keys(rawSplitPanelData)); + console.log("๐Ÿงน [handleSave] ์ •๋ฆฌ๋œ ๋ถ„ํ•  ํŒจ๋„ ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ (์—ฐ๊ฒฐ ํ•„๋“œ๋งŒ):", cleanedSplitPanelData); } const dataWithUserInfo = { - ...splitPanelData, // ๋ถ„ํ•  ํŒจ๋„ ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ ๋จผ์ € ์ ์šฉ + ...cleanedSplitPanelData, // ์ •๋ฆฌ๋œ ๋ถ„ํ•  ํŒจ๋„ ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ ๋จผ์ € ์ ์šฉ ...formData, // ํผ ๋ฐ์ดํ„ฐ๊ฐ€ ์šฐ์„  (๋ฎ์–ด์“ฐ๊ธฐ ๊ฐ€๋Šฅ) writer: formData.writer || writerValue, // โœ… ์ž…๋ ฅ๊ฐ’ ์šฐ์„ , ์—†์œผ๋ฉด userId created_by: writerValue, // created_by๋Š” ํ•ญ์ƒ ๋กœ๊ทธ์ธํ•œ ์‚ฌ๋žŒ @@ -695,6 +734,12 @@ export class ButtonActionExecutor { company_code: formData.company_code || companyCodeValue, // โœ… ์ž…๋ ฅ๊ฐ’ ์šฐ์„ , ์—†์œผ๋ฉด user.companyCode }; + // ๐Ÿ”ง formData์—์„œ๋„ id ์ œ๊ฑฐ (์‹ ๊ทœ INSERT์ด๋ฏ€๋กœ) + if ("id" in dataWithUserInfo && !formData.id) { + console.log("๐Ÿ—‘๏ธ [handleSave] INSERT ๋ชจ๋“œ - dataWithUserInfo์—์„œ id ์ œ๊ฑฐ:", dataWithUserInfo.id); + delete dataWithUserInfo.id; + } + // _numberingRuleId ํ•„๋“œ ์ œ๊ฑฐ (์‹ค์ œ ์ €์žฅํ•˜์ง€ ์•Š์Œ) for (const key of Object.keys(dataWithUserInfo)) { if (key.endsWith("_numberingRuleId")) { @@ -1578,14 +1623,16 @@ export class ButtonActionExecutor { /** * ๋ชจ๋‹ฌ ์•ก์…˜ ์ฒ˜๋ฆฌ + * ๐Ÿ”ง modal ์•ก์…˜์€ ํ•ญ์ƒ ์‹ ๊ทœ ๋“ฑ๋ก(INSERT) ๋ชจ๋“œ๋กœ ๋™์ž‘ + * edit ์•ก์…˜๋งŒ ์ˆ˜์ •(UPDATE) ๋ชจ๋“œ๋กœ ๋™์ž‘ํ•ด์•ผ ํ•จ */ private static async handleModal(config: ButtonActionConfig, context: ButtonActionContext): Promise { // ๋ชจ๋‹ฌ ์—ด๊ธฐ ๋กœ์ง - console.log("๋ชจ๋‹ฌ ์—ด๊ธฐ:", { + console.log("๋ชจ๋‹ฌ ์—ด๊ธฐ (์‹ ๊ทœ ๋“ฑ๋ก ๋ชจ๋“œ):", { title: config.modalTitle, size: config.modalSize, targetScreenId: config.targetScreenId, - selectedRowsData: context.selectedRowsData, + // ๐Ÿ”ง selectedRowsData๋Š” modal ์•ก์…˜์—์„œ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ (์‹ ๊ทœ ๋“ฑ๋ก์ด๋ฏ€๋กœ) }); if (config.targetScreenId) { @@ -1602,10 +1649,11 @@ export class ButtonActionExecutor { } } - // ๐Ÿ†• ์„ ํƒ๋œ ํ–‰ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘ - const selectedData = context.selectedRowsData || []; - console.log("๐Ÿ“ฆ [handleModal] ์„ ํƒ๋œ ๋ฐ์ดํ„ฐ:", selectedData); - console.log("๐Ÿ“ฆ [handleModal] ๋ถ„ํ•  ํŒจ๋„ ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ:", context.splitPanelParentData); + // ๐Ÿ”ง modal ์•ก์…˜์€ ์‹ ๊ทœ ๋“ฑ๋ก์ด๋ฏ€๋กœ selectedData๋ฅผ ์ „๋‹ฌํ•˜์ง€ ์•Š์Œ + // selectedData๊ฐ€ ์žˆ์œผ๋ฉด ScreenModal์—์„œ originalData๋กœ ์ธ์‹ํ•˜์—ฌ UPDATE ๋ชจ๋“œ๋กœ ๋™์ž‘ํ•˜๊ฒŒ ๋จ + // edit ์•ก์…˜๋งŒ selectedData/editData๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ UPDATE ๋ชจ๋“œ๋กœ ๋™์ž‘ + console.log("๐Ÿ“ฆ [handleModal] ์‹ ๊ทœ ๋“ฑ๋ก ๋ชจ๋“œ - selectedData ์ „๋‹ฌํ•˜์ง€ ์•Š์Œ"); + console.log("๐Ÿ“ฆ [handleModal] ๋ถ„ํ•  ํŒจ๋„ ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ (์ดˆ๊ธฐ๊ฐ’์œผ๋กœ ์‚ฌ์šฉ):", context.splitPanelParentData); // ์ „์—ญ ๋ชจ๋‹ฌ ์ƒํƒœ ์—…๋ฐ์ดํŠธ๋ฅผ ์œ„ํ•œ ์ด๋ฒคํŠธ ๋ฐœ์ƒ const modalEvent = new CustomEvent("openScreenModal", { @@ -1614,10 +1662,11 @@ export class ButtonActionExecutor { title: config.modalTitle || "ํ™”๋ฉด", description: description, size: config.modalSize || "md", - // ๐Ÿ†• ์„ ํƒ๋œ ํ–‰ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ - selectedData: selectedData, - selectedIds: selectedData.map((row: any) => row.id).filter(Boolean), - // ๐Ÿ†• ๋ถ„ํ•  ํŒจ๋„ ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ (ํƒญ ์•ˆ ๋ชจ๋‹ฌ์—์„œ ์‚ฌ์šฉ) + // ๐Ÿ”ง ์‹ ๊ทœ ๋“ฑ๋ก์ด๋ฏ€๋กœ selectedData/selectedIds๋ฅผ ์ „๋‹ฌํ•˜์ง€ ์•Š์Œ + // edit ์•ก์…˜์—์„œ๋งŒ ์ด ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉ + selectedData: [], + selectedIds: [], + // ๐Ÿ†• ๋ถ„ํ•  ํŒจ๋„ ๋ถ€๋ชจ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ (ํƒญ ์•ˆ ๋ชจ๋‹ฌ์—์„œ ์ดˆ๊ธฐ๊ฐ’์œผ๋กœ ์‚ฌ์šฉ) splitPanelParentData: context.splitPanelParentData || {}, }, }); @@ -2663,7 +2712,7 @@ export class ButtonActionExecutor { const { executeNodeFlow } = await import("@/lib/api/nodeFlows"); // ๋ฐ์ดํ„ฐ ์†Œ์Šค ์ค€๋น„ - let sourceData: any = context.formData || {}; + const sourceData: any = context.formData || {}; // repeat-screen-modal ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์œผ๋ฉด ๋ณ‘ํ•ฉ const repeatScreenModalKeys = Object.keys(context.formData || {}).filter((key) =>