"use client"; import React, { useState, useEffect, useMemo } from "react"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, } from "@/components/ui/dialog"; import { InteractiveScreenViewerDynamic } from "@/components/screen/InteractiveScreenViewerDynamic"; import { screenApi } from "@/lib/api/screen"; import { ComponentData } from "@/types/screen"; import { toast } from "sonner"; import { dynamicFormApi } from "@/lib/api/dynamicForm"; import { useAuth } from "@/hooks/useAuth"; import { ConditionalZone, LayerDefinition } from "@/types/screen-management"; import { convertV2ToLegacy, isValidV2Layout } from "@/lib/utils/layoutV2Converter"; interface EditModalState { isOpen: boolean; screenId: number | null; title: string; description?: string; modalSize: "sm" | "md" | "lg" | "xl"; editData: Record; onSave?: () => void; groupByColumns?: string[]; // ๐Ÿ†• ๊ทธ๋ฃนํ•‘ ์ปฌ๋Ÿผ (์˜ˆ: ["order_no"]) tableName?: string; // ๐Ÿ†• ํ…Œ์ด๋ธ”๋ช… (๊ทธ๋ฃน ์กฐํšŒ์šฉ) buttonConfig?: any; // ๐Ÿ†• ๋ฒ„ํŠผ ์„ค์ • (์ œ์–ด๋กœ์ง ์‹คํ–‰์šฉ) buttonContext?: any; // ๐Ÿ†• ๋ฒ„ํŠผ ์ปจํ…์ŠคํŠธ (screenId, userId ๋“ฑ) saveButtonConfig?: { enableDataflowControl?: boolean; dataflowConfig?: any; dataflowTiming?: string; }; // ๐Ÿ†• ๋ชจ๋‹ฌ ๋‚ด๋ถ€ ์ €์žฅ ๋ฒ„ํŠผ์˜ ์ œ์–ด๋กœ์ง ์„ค์ • menuObjid?: number; // ๐Ÿ†• ๋ฉ”๋‰ด OBJID (์นดํ…Œ๊ณ ๋ฆฌ ์Šค์ฝ”ํ”„์šฉ) } interface EditModalProps { className?: string; } /** * ๋ชจ๋‹ฌ ๋‚ด๋ถ€์—์„œ ์ €์žฅ ๋ฒ„ํŠผ ์ฐพ๊ธฐ (์žฌ๊ท€์ ์œผ๋กœ ํƒ์ƒ‰) * action.type์ด "save"์ธ button-primary ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ฐพ์Œ */ const findSaveButtonInComponents = (components: any[]): any | null => { if (!components || !Array.isArray(components)) return null; for (const comp of components) { // button-primary์ด๊ณ  action.type์ด save์ธ ๊ฒฝ์šฐ if ( comp.componentType === "button-primary" && comp.componentConfig?.action?.type === "save" ) { return comp; } // conditional-container์˜ sections ๋‚ด๋ถ€ ํƒ์ƒ‰ if (comp.componentType === "conditional-container" && comp.componentConfig?.sections) { for (const section of comp.componentConfig.sections) { if (section.screenId) { // ์กฐ๊ฑด๋ถ€ ์ปจํ…Œ์ด๋„ˆ์˜ ๋‚ด๋ถ€ ํ™”๋ฉด์€ ๋ณ„๋„๋กœ ๋กœ๋“œํ•ด์•ผ ํ•จ // ์—ฌ๊ธฐ์„œ๋Š” null ๋ฐ˜ํ™˜ํ•˜๊ณ , loadSaveButtonConfig์—์„œ ์ฒ˜๋ฆฌ continue; } } } // ์ž์‹ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žˆ์œผ๋ฉด ์žฌ๊ท€ ํƒ์ƒ‰ if (comp.children && Array.isArray(comp.children)) { const found = findSaveButtonInComponents(comp.children); if (found) return found; } } return null; }; export const EditModal: React.FC = ({ className }) => { const { user } = useAuth(); const [modalState, setModalState] = useState({ isOpen: false, screenId: null, title: "", description: "", modalSize: "md", editData: {}, onSave: undefined, groupByColumns: undefined, tableName: undefined, buttonConfig: undefined, buttonContext: undefined, saveButtonConfig: undefined, menuObjid: undefined, }); const [screenData, setScreenData] = useState<{ components: ComponentData[]; screenInfo: any; } | null>(null); const [loading, setLoading] = useState(false); const [screenDimensions, setScreenDimensions] = useState<{ width: number; height: number; offsetX?: number; offsetY?: number; } | null>(null); // ํผ ๋ฐ์ดํ„ฐ ์ƒํƒœ (ํŽธ์ง‘ ๋ฐ์ดํ„ฐ๋กœ ์ดˆ๊ธฐํ™”๋จ) const [formData, setFormData] = useState>({}); const [originalData, setOriginalData] = useState>({}); // INSERT/UPDATE ํŒ๋‹จ์šฉ ํ”Œ๋ž˜๊ทธ (์ด๋ฒคํŠธ์—์„œ ๋ช…์‹œ์ ์œผ๋กœ ์ „๋‹ฌ๋ฐ›์Œ) // true = INSERT (๋“ฑ๋ก/๋ณต์‚ฌ), false = UPDATE (์ˆ˜์ •) // originalData ์ƒํƒœ์— ์˜์กดํ•˜์ง€ ์•Š๊ณ  ์ด๋ฒคํŠธ์˜ isCreateMode ๊ฐ’์„ ์ง์ ‘ ์‚ฌ์šฉ const [isCreateModeFlag, setIsCreateModeFlag] = useState(true); // ๐Ÿ†• ๊ทธ๋ฃน ๋ฐ์ดํ„ฐ ์ƒํƒœ (๊ฐ™์€ order_no์˜ ๋ชจ๋“  ํ’ˆ๋ชฉ) const [groupData, setGroupData] = useState[]>([]); const [originalGroupData, setOriginalGroupData] = useState[]>([]); // ๐Ÿ†• ์กฐ๊ฑด๋ถ€ ๋ ˆ์ด์–ด ์ƒํƒœ (Zone ๊ธฐ๋ฐ˜) const [zones, setZones] = useState([]); const [conditionalLayers, setConditionalLayers] = useState([]); // ํ™”๋ฉด์˜ ์‹ค์ œ ํฌ๊ธฐ ๊ณ„์‚ฐ ํ•จ์ˆ˜ (ScreenModal๊ณผ ๋™์ผ) const calculateScreenDimensions = (components: ComponentData[]) => { if (components.length === 0) { return { width: 400, height: 300, offsetX: 0, offsetY: 0, }; } // ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ์˜ ๊ฒฝ๊ณ„ ์ฐพ๊ธฐ let minX = Infinity; let minY = Infinity; let maxX = -Infinity; let maxY = -Infinity; components.forEach((component) => { const x = parseFloat(component.position?.x?.toString() || "0"); const y = parseFloat(component.position?.y?.toString() || "0"); const width = parseFloat(component.size?.width?.toString() || "100"); const height = parseFloat(component.size?.height?.toString() || "40"); minX = Math.min(minX, x); minY = Math.min(minY, y); maxX = Math.max(maxX, x + width); maxY = Math.max(maxY, y + height); }); // ์‹ค์ œ ์ปจํ…์ธ  ํฌ๊ธฐ ๊ณ„์‚ฐ const contentWidth = maxX - minX; const contentHeight = maxY - minY; // ์ ์ ˆํ•œ ์—ฌ๋ฐฑ ์ถ”๊ฐ€ (์ฃผ์„์ฒ˜๋ฆฌ - ์‚ฌ์šฉ์ž ์„ค์ • ํฌ๊ธฐ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉ) // const paddingX = 40; // const paddingY = 40; const finalWidth = Math.max(contentWidth, 400); // padding ์ œ๊ฑฐ const finalHeight = Math.max(contentHeight, 300); // padding ์ œ๊ฑฐ return { width: Math.min(finalWidth, window.innerWidth * 0.95), height: Math.min(finalHeight, window.innerHeight * 0.9), offsetX: Math.max(0, minX), // paddingX ์ œ๊ฑฐ offsetY: Math.max(0, minY), // paddingY ์ œ๊ฑฐ }; }; // ๐Ÿ†• ๋ชจ๋‹ฌ ๋‚ด๋ถ€ ์ €์žฅ ๋ฒ„ํŠผ์˜ ์ œ์–ด๋กœ์ง ์„ค์ • ์กฐํšŒ const loadSaveButtonConfig = async (targetScreenId: number): Promise<{ enableDataflowControl?: boolean; dataflowConfig?: any; dataflowTiming?: string; } | null> => { try { // 1. ๋Œ€์ƒ ํ™”๋ฉด์˜ ๋ ˆ์ด์•„์›ƒ ์กฐํšŒ const layoutData = await screenApi.getLayout(targetScreenId); if (!layoutData?.components) { console.log("[EditModal] ๋ ˆ์ด์•„์›ƒ ์ปดํฌ๋„ŒํŠธ ์—†์Œ:", targetScreenId); return null; } // 2. ์ €์žฅ ๋ฒ„ํŠผ ์ฐพ๊ธฐ let saveButton = findSaveButtonInComponents(layoutData.components); // 3. conditional-container๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ๋‚ด๋ถ€ ํ™”๋ฉด๋„ ํƒ์ƒ‰ if (!saveButton) { for (const comp of layoutData.components) { if (comp.componentType === "conditional-container" && comp.componentConfig?.sections) { for (const section of comp.componentConfig.sections) { if (section.screenId) { try { const innerLayoutData = await screenApi.getLayout(section.screenId); saveButton = findSaveButtonInComponents(innerLayoutData?.components || []); if (saveButton) { // console.log("[EditModal] ์กฐ๊ฑด๋ถ€ ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€์—์„œ ์ €์žฅ ๋ฒ„ํŠผ ๋ฐœ๊ฒฌ:", { // sectionScreenId: section.screenId, // sectionLabel: section.label, // }); break; } } catch (innerError) { // console.warn("[EditModal] ๋‚ด๋ถ€ ํ™”๋ฉด ๋ ˆ์ด์•„์›ƒ ์กฐํšŒ ์‹คํŒจ:", section.screenId); } } } if (saveButton) break; } } } if (!saveButton) { // console.log("[EditModal] ์ €์žฅ ๋ฒ„ํŠผ์„ ์ฐพ์„ ์ˆ˜ ์—†์Œ:", targetScreenId); return null; } // 4. webTypeConfig์—์„œ ์ œ์–ด๋กœ์ง ์„ค์ • ์ถ”์ถœ const webTypeConfig = saveButton.webTypeConfig; if (webTypeConfig?.enableDataflowControl) { const config = { enableDataflowControl: webTypeConfig.enableDataflowControl, dataflowConfig: webTypeConfig.dataflowConfig, dataflowTiming: webTypeConfig.dataflowConfig?.flowConfig?.executionTiming || "after", }; // console.log("[EditModal] ์ €์žฅ ๋ฒ„ํŠผ ์ œ์–ด๋กœ์ง ์„ค์ • ๋ฐœ๊ฒฌ:", config); return config; } // console.log("[EditModal] ์ €์žฅ ๋ฒ„ํŠผ์— ์ œ์–ด๋กœ์ง ์„ค์ • ์—†์Œ"); return null; } catch (error) { // console.warn("[EditModal] ์ €์žฅ ๋ฒ„ํŠผ ์„ค์ • ์กฐํšŒ ์‹คํŒจ:", error); return null; } }; // ์ „์—ญ ๋ชจ๋‹ฌ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ useEffect(() => { const handleOpenEditModal = async (event: CustomEvent) => { const { screenId, title, description, modalSize, editData, onSave, groupByColumns, tableName, isCreateMode, buttonConfig, buttonContext, menuObjid } = event.detail; // ๐Ÿ†• ๋ชจ๋‹ฌ ๋‚ด๋ถ€ ์ €์žฅ ๋ฒ„ํŠผ์˜ ์ œ์–ด๋กœ์ง ์„ค์ • ์กฐํšŒ let saveButtonConfig: EditModalState["saveButtonConfig"] = undefined; if (screenId) { const config = await loadSaveButtonConfig(screenId); if (config) { saveButtonConfig = config; } } setModalState({ isOpen: true, screenId, title, description: description || "", modalSize: modalSize || "lg", editData: editData || {}, onSave, groupByColumns, // ๐Ÿ†• ๊ทธ๋ฃนํ•‘ ์ปฌ๋Ÿผ tableName, // ๐Ÿ†• ํ…Œ์ด๋ธ”๋ช… buttonConfig, // ๐Ÿ†• ๋ฒ„ํŠผ ์„ค์ • buttonContext, // ๐Ÿ†• ๋ฒ„ํŠผ ์ปจํ…์ŠคํŠธ saveButtonConfig, // ๐Ÿ†• ๋ชจ๋‹ฌ ๋‚ด๋ถ€ ์ €์žฅ ๋ฒ„ํŠผ์˜ ์ œ์–ด๋กœ์ง ์„ค์ • menuObjid, // ๐Ÿ†• ๋ฉ”๋‰ด OBJID (์นดํ…Œ๊ณ ๋ฆฌ ์Šค์ฝ”ํ”„์šฉ) }); // ํŽธ์ง‘ ๋ฐ์ดํ„ฐ๋กœ ํผ ๋ฐ์ดํ„ฐ ์ดˆ๊ธฐํ™” setFormData(editData || {}); // originalData: changedData ๊ณ„์‚ฐ(PATCH)์—๋งŒ ์‚ฌ์šฉ // INSERT/UPDATE ํŒ๋‹จ์—๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ setOriginalData(isCreateMode ? {} : editData || {}); // INSERT/UPDATE ํŒ๋‹จ: ์ด๋ฒคํŠธ์˜ isCreateMode ํ”Œ๋ž˜๊ทธ๋ฅผ ์ง์ ‘ ์ €์žฅ // isCreateMode=true(๋ณต์‚ฌ/๋“ฑ๋ก) โ†’ INSERT, false/undefined(์ˆ˜์ •) โ†’ UPDATE setIsCreateModeFlag(!!isCreateMode); console.log("[EditModal] ๋ชจ๋‹ฌ ์—ด๋ฆผ:", { mode: isCreateMode ? "INSERT (์ƒ์„ฑ/๋ณต์‚ฌ)" : "UPDATE (์ˆ˜์ •)", hasEditData: !!editData, editDataId: editData?.id, isCreateMode, }); }; const handleCloseEditModal = () => { // ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์˜ onSave ์ฝœ๋ฐฑ ์‹คํ–‰ (ํ…Œ์ด๋ธ” ์ƒˆ๋กœ๊ณ ์นจ) if (modalState.onSave) { try { modalState.onSave(); } catch (callbackError) { console.error("โš ๏ธ onSave ์ฝœ๋ฐฑ ์—๋Ÿฌ:", callbackError); } } // ๋ชจ๋‹ฌ ๋‹ซ๊ธฐ handleClose(); }; window.addEventListener("openEditModal", handleOpenEditModal as EventListener); window.addEventListener("closeEditModal", handleCloseEditModal); return () => { window.removeEventListener("openEditModal", handleOpenEditModal as EventListener); window.removeEventListener("closeEditModal", handleCloseEditModal); }; }, [modalState.onSave]); // modalState.onSave๋ฅผ ์˜์กด์„ฑ์— ์ถ”๊ฐ€ํ•˜์—ฌ ์ตœ์‹  ์ฝœ๋ฐฑ ์ฐธ์กฐ // ํ™”๋ฉด ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ useEffect(() => { if (modalState.isOpen && modalState.screenId) { loadScreenData(modalState.screenId); // ๐Ÿ†• ๊ทธ๋ฃน ๋ฐ์ดํ„ฐ ์กฐํšŒ (groupByColumns๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ) if (modalState.groupByColumns && modalState.groupByColumns.length > 0 && modalState.tableName) { loadGroupData(); } } }, [modalState.isOpen, modalState.screenId, modalState.groupByColumns, modalState.tableName]); // ๐Ÿ†• ๊ทธ๋ฃน ๋ฐ์ดํ„ฐ ์กฐํšŒ ํ•จ์ˆ˜ const loadGroupData = async () => { if (!modalState.tableName || !modalState.groupByColumns || modalState.groupByColumns.length === 0) { return; } try { // ๊ทธ๋ฃนํ•‘ ์ปฌ๋Ÿผ ๊ฐ’ ์ถ”์ถœ (์˜ˆ: order_no = "ORD-20251124-001") const groupValues: Record = {}; modalState.groupByColumns.forEach((column) => { if (modalState.editData[column]) { groupValues[column] = modalState.editData[column]; } }); if (Object.keys(groupValues).length === 0) { return; } // ๊ฐ™์€ ๊ทธ๋ฃน์˜ ๋ชจ๋“  ๋ ˆ์ฝ”๋“œ ์กฐํšŒ (entityJoinApi ์‚ฌ์šฉ) const { entityJoinApi } = await import("@/lib/api/entityJoin"); const response = await entityJoinApi.getTableDataWithJoins(modalState.tableName, { page: 1, size: 1000, search: groupValues, // search ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ „๋‹ฌ (๋ฐฑ์—”๋“œ์—์„œ WHERE ์กฐ๊ฑด์œผ๋กœ ์ฒ˜๋ฆฌ) enableEntityJoin: true, }); // entityJoinApi๋Š” ๋ฐฐ์—ด ๋˜๋Š” { data: [] } ํ˜•์‹์œผ๋กœ ๋ฐ˜ํ™˜ const dataArray = Array.isArray(response) ? response : response?.data || []; if (dataArray.length > 0) { setGroupData(dataArray); setOriginalGroupData(JSON.parse(JSON.stringify(dataArray))); // Deep copy toast.info(`${dataArray.length}๊ฐœ์˜ ๊ด€๋ จ ํ’ˆ๋ชฉ์„ ๋ถˆ๋Ÿฌ์™”์Šต๋‹ˆ๋‹ค.`); } else { setGroupData([modalState.editData]); // ๊ธฐ๋ณธ๊ฐ’: ์„ ํƒ๋œ ํ–‰๋งŒ setOriginalGroupData([JSON.parse(JSON.stringify(modalState.editData))]); } } catch (error: any) { console.error("๊ทธ๋ฃน ๋ฐ์ดํ„ฐ ์กฐํšŒ ์˜ค๋ฅ˜:", error); toast.error("๊ด€๋ จ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค."); setGroupData([modalState.editData]); // ๊ธฐ๋ณธ๊ฐ’: ์„ ํƒ๋œ ํ–‰๋งŒ setOriginalGroupData([JSON.parse(JSON.stringify(modalState.editData))]); } }; const loadScreenData = async (screenId: number) => { try { setLoading(true); // ํ™”๋ฉด ์ •๋ณด์™€ ๋ ˆ์ด์•„์›ƒ ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ const [screenInfo, layoutData] = await Promise.all([ screenApi.getScreen(screenId), screenApi.getLayout(screenId), ]); if (screenInfo && layoutData) { const components = layoutData.components || []; // ํ™”๋ฉด์˜ ์‹ค์ œ ํฌ๊ธฐ ๊ณ„์‚ฐ const dimensions = calculateScreenDimensions(components); setScreenDimensions(dimensions); setScreenData({ components, screenInfo: screenInfo, }); // ๐Ÿ†• ์กฐ๊ฑด๋ถ€ ๋ ˆ์ด์–ด/์กด ๋กœ๋“œ (await์œผ๋กœ ์—๋Ÿฌ ํฌ์ฐฉ) console.log("[EditModal] ํ™”๋ฉด ๋ฐ์ดํ„ฐ ๋กœ๋“œ ์™„๋ฃŒ, ์กฐ๊ฑด๋ถ€ ๋ ˆ์ด์–ด ๋กœ๋“œ ์‹œ์ž‘:", screenId); try { await loadConditionalLayersAndZones(screenId, components); } catch (layerErr) { console.error("[EditModal] ์กฐ๊ฑด๋ถ€ ๋ ˆ์ด์–ด ๋กœ๋“œ ์—๋Ÿฌ:", layerErr); } } else { throw new Error("ํ™”๋ฉด ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค"); } } catch (error) { console.error("ํ™”๋ฉด ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ ์˜ค๋ฅ˜:", error); toast.error("ํ™”๋ฉด์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค."); handleClose(); } finally { setLoading(false); } }; // ๐Ÿ†• ์กฐ๊ฑด๋ถ€ ๋ ˆ์ด์–ด & ์กด ๋กœ๋“œ ํ•จ์ˆ˜ const loadConditionalLayersAndZones = async (screenId: number, baseComponents: ComponentData[]) => { console.log("[EditModal] loadConditionalLayersAndZones ํ˜ธ์ถœ๋จ:", screenId); try { // ๋ ˆ์ด์–ด ๋ชฉ๋ก & ์กด ๋ชฉ๋ก ๋ณ‘๋ ฌ ๋กœ๋“œ console.log("[EditModal] API ํ˜ธ์ถœ ์‹œ์ž‘: getScreenLayers, getScreenZones"); const [layersRes, zonesRes] = await Promise.all([ screenApi.getScreenLayers(screenId), screenApi.getScreenZones(screenId), ]); console.log("[EditModal] API ์‘๋‹ต:", { layers: layersRes?.length, zones: zonesRes?.length }); const loadedLayers = layersRes || []; const loadedZones: ConditionalZone[] = zonesRes || []; setZones(loadedZones); // ๊ธฐ๋ณธ ๋ ˆ์ด์–ด(layer_id=1) ์ œ์™ธํ•œ ์กฐ๊ฑด๋ถ€ ๋ ˆ์ด์–ด ์ฒ˜๋ฆฌ const nonBaseLayers = loadedLayers.filter((l: any) => l.layer_id !== 1); if (nonBaseLayers.length === 0) { setConditionalLayers([]); return; } // ๊ฐ ์กฐ๊ฑด๋ถ€ ๋ ˆ์ด์–ด์˜ ์ปดํฌ๋„ŒํŠธ ๋กœ๋“œ const layerDefinitions: LayerDefinition[] = []; for (const layer of nonBaseLayers) { try { const layerLayout = await screenApi.getLayerLayout(screenId, layer.layer_id); let layerComponents: ComponentData[] = []; if (layerLayout && isValidV2Layout(layerLayout)) { const legacyLayout = convertV2ToLegacy(layerLayout); layerComponents = (legacyLayout?.components || []) as unknown as ComponentData[]; } else if (layerLayout?.components) { layerComponents = layerLayout.components; } // condition_config์—์„œ zone_id, condition_value ์ถ”์ถœ const conditionConfig = layer.condition_config || {}; const layerZoneId = conditionConfig.zone_id; const layerConditionValue = conditionConfig.condition_value; // ์ด ๋ ˆ์ด์–ด๊ฐ€ ์†ํ•œ Zone ์ฐพ๊ธฐ const associatedZone = loadedZones.find( (z) => z.zone_id === layerZoneId ); layerDefinitions.push({ id: `layer-${layer.layer_id}`, name: layer.layer_name || `๋ ˆ์ด์–ด ${layer.layer_id}`, type: "conditional", zIndex: layer.layer_id, isVisible: false, isLocked: false, zoneId: layerZoneId, conditionValue: layerConditionValue, condition: associatedZone ? { targetComponentId: associatedZone.trigger_component_id || "", operator: (associatedZone.trigger_operator || "eq") as any, value: layerConditionValue || "", } : undefined, components: layerComponents, } as LayerDefinition & { components: ComponentData[] }); } catch (layerError) { console.warn(`[EditModal] ๋ ˆ์ด์–ด ${layer.layer_id} ๋กœ๋“œ ์‹คํŒจ:`, layerError); } } console.log("[EditModal] ์กฐ๊ฑด๋ถ€ ๋ ˆ์ด์–ด ๋กœ๋“œ ์™„๋ฃŒ:", layerDefinitions.length, "๊ฐœ", layerDefinitions.map((l) => ({ id: l.id, name: l.name, conditionValue: l.conditionValue, condition: l.condition, })) ); setConditionalLayers(layerDefinitions); } catch (error) { console.warn("[EditModal] ์กฐ๊ฑด๋ถ€ ๋ ˆ์ด์–ด ๋กœ๋“œ ์‹คํŒจ:", error); } }; // ๐Ÿ†• ์กฐ๊ฑด๋ถ€ ๋ ˆ์ด์–ด ํ™œ์„ฑํ™” ํ‰๊ฐ€ (formData ๋ณ€๊ฒฝ ์‹œ) const activeConditionalLayerIds = useMemo(() => { if (conditionalLayers.length === 0) return []; const newActiveIds: string[] = []; const allComponents = screenData?.components || []; conditionalLayers.forEach((layer) => { const layerWithComponents = layer as LayerDefinition & { components: ComponentData[] }; if (layerWithComponents.condition) { const { targetComponentId, operator, value } = layerWithComponents.condition; if (!targetComponentId) return; // ํŠธ๋ฆฌ๊ฑฐ ์ปดํฌ๋„ŒํŠธ์˜ columnName ์ฐพ๊ธฐ // V2 ๋ ˆ์ด์•„์›ƒ: overrides.columnName, ๋ ˆ๊ฑฐ์‹œ: componentConfig.columnName const targetComponent = allComponents.find((c) => c.id === targetComponentId); const fieldKey = (targetComponent as any)?.overrides?.columnName || (targetComponent as any)?.columnName || (targetComponent as any)?.componentConfig?.columnName || targetComponentId; const currentFormData = groupData.length > 0 ? groupData[0] : formData; const targetValue = currentFormData[fieldKey]; let isMatch = false; switch (operator) { case "eq": isMatch = String(targetValue ?? "") === String(value ?? ""); break; case "neq": isMatch = String(targetValue ?? "") !== String(value ?? ""); break; case "in": if (Array.isArray(value)) { isMatch = value.some((v) => String(v) === String(targetValue ?? "")); } else if (typeof value === "string" && value.includes(",")) { isMatch = value.split(",").map((v) => v.trim()).includes(String(targetValue ?? "")); } break; } // ๋””๋ฒ„๊ทธ ๋กœ๊น… console.log("[EditModal] ๋ ˆ์ด์–ด ์กฐ๊ฑด ํ‰๊ฐ€:", { layerId: layer.id, layerName: layer.name, targetComponentId, fieldKey, targetValue: targetValue !== undefined ? String(targetValue) : "(์—†์Œ)", conditionValue: String(value), operator, isMatch, componentFound: !!targetComponent, }); if (isMatch) { newActiveIds.push(layer.id); } } }); return newActiveIds; }, [formData, groupData, conditionalLayers, screenData?.components]); // ๐Ÿ†• ํ™œ์„ฑํ™”๋œ ์กฐ๊ฑด๋ถ€ ๋ ˆ์ด์–ด์˜ ์ปดํฌ๋„ŒํŠธ ๊ฐ€์ ธ์˜ค๊ธฐ const activeConditionalComponents = useMemo(() => { return conditionalLayers .filter((layer) => activeConditionalLayerIds.includes(layer.id)) .flatMap((layer) => (layer as LayerDefinition & { components: ComponentData[] }).components || []); }, [conditionalLayers, activeConditionalLayerIds]); const handleClose = () => { setModalState({ isOpen: false, screenId: null, title: "", description: "", modalSize: "md", editData: {}, onSave: undefined, groupByColumns: undefined, tableName: undefined, }); setScreenData(null); setFormData({}); setZones([]); setConditionalLayers([]); setOriginalData({}); setIsCreateModeFlag(true); // ๊ธฐ๋ณธ๊ฐ’์€ INSERT (์•ˆ์ „ ๋ฐฉํ–ฅ) setGroupData([]); // ๐Ÿ†• setOriginalGroupData([]); // ๐Ÿ†• }; // ์ €์žฅ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ - UPDATE ์•ก์…˜ ์‹คํ–‰ const handleSave = async (saveData?: any) => { // universal-form-modal ๋“ฑ์—์„œ ์ž์ฒด ์ €์žฅ ์™„๋ฃŒ ํ›„ ํ˜ธ์ถœ๋œ ๊ฒฝ์šฐ ์Šคํ‚ต if (saveData?._saveCompleted) { console.log("[EditModal] ์ž์ฒด ์ €์žฅ ์™„๋ฃŒ๋œ ์ปดํฌ๋„ŒํŠธ์—์„œ ํ˜ธ์ถœ๋จ - ์ €์žฅ ์Šคํ‚ต"); // ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์˜ onSave ์ฝœ๋ฐฑ ์‹คํ–‰ (ํ…Œ์ด๋ธ” ์ƒˆ๋กœ๊ณ ์นจ) if (modalState.onSave) { try { modalState.onSave(); } catch (callbackError) { console.error("onSave ์ฝœ๋ฐฑ ์—๋Ÿฌ:", callbackError); } } handleClose(); return; } if (!screenData?.screenInfo?.tableName) { toast.error("ํ…Œ์ด๋ธ” ์ •๋ณด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค."); return; } try { // ๐Ÿ†• ๊ทธ๋ฃน ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ: ๋ชจ๋“  ํ’ˆ๋ชฉ ์ผ๊ด„ ์ฒ˜๋ฆฌ (์ถ”๊ฐ€/์ˆ˜์ •/์‚ญ์ œ) if (groupData.length > 0 || originalGroupData.length > 0) { console.log("๐Ÿ”„ ๊ทธ๋ฃน ๋ฐ์ดํ„ฐ ์ผ๊ด„ ์ฒ˜๋ฆฌ ์‹œ์ž‘:", { groupDataLength: groupData.length, originalGroupDataLength: originalGroupData.length, groupData, originalGroupData, tableName: screenData.screenInfo.tableName, screenId: modalState.screenId, }); // ๐Ÿ†• ๋‚ ์งœ ํ•„๋“œ ์ •๊ทœํ™” ํ•จ์ˆ˜ (YYYY-MM-DD ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜) const normalizeDateField = (value: any): string | null => { if (!value) return null; // ISO 8601 ํ˜•์‹ (2025-11-26T00:00:00.000Z) ๋˜๋Š” Date ๊ฐ์ฒด if (value instanceof Date || typeof value === "string") { try { const date = new Date(value); if (isNaN(date.getTime())) return null; // YYYY-MM-DD ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, "0"); const day = String(date.getDate()).padStart(2, "0"); return `${year}-${month}-${day}`; } catch (error) { console.warn("๋‚ ์งœ ๋ณ€ํ™˜ ์‹คํŒจ:", value, error); return null; } } return null; }; // ๋‚ ์งœ ํ•„๋“œ ๋ชฉ๋ก const dateFields = ["item_due_date", "delivery_date", "due_date", "order_date"]; let insertedCount = 0; let updatedCount = 0; let deletedCount = 0; // 1๏ธโƒฃ ์‹ ๊ทœ ํ’ˆ๋ชฉ ์ถ”๊ฐ€ (id๊ฐ€ ์—†๋Š” ํ•ญ๋ชฉ) for (const currentData of groupData) { if (!currentData.id) { console.log("โž• ์‹ ๊ทœ ํ’ˆ๋ชฉ ์ถ”๊ฐ€:", currentData); console.log("๐Ÿ“‹ [์‹ ๊ทœ ํ’ˆ๋ชฉ] currentData ํ‚ค ๋ชฉ๋ก:", Object.keys(currentData)); // ๐Ÿ†• ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จ (id ์ œ์™ธ) const insertData: Record = { ...currentData }; console.log("๐Ÿ“ฆ [์‹ ๊ทœ ํ’ˆ๋ชฉ] ๋ณต์‚ฌ ์งํ›„ insertData:", insertData); console.log("๐Ÿ“‹ [์‹ ๊ทœ ํ’ˆ๋ชฉ] insertData ํ‚ค ๋ชฉ๋ก:", Object.keys(insertData)); delete insertData.id; // id๋Š” ์ž๋™ ์ƒ์„ฑ๋˜๋ฏ€๋กœ ์ œ๊ฑฐ // ๐Ÿ†• ๋‚ ์งœ ํ•„๋“œ ์ •๊ทœํ™” (YYYY-MM-DD ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜) dateFields.forEach((fieldName) => { if (insertData[fieldName]) { const normalizedDate = normalizeDateField(insertData[fieldName]); if (normalizedDate) { insertData[fieldName] = normalizedDate; console.log(`๐Ÿ“… [๋‚ ์งœ ์ •๊ทœํ™”] ${fieldName}: ${currentData[fieldName]} โ†’ ${normalizedDate}`); } } }); // ๐Ÿ†• groupByColumns์˜ ๊ฐ’์„ ๊ฐ•์ œ๋กœ ํฌํ•จ (order_no ๋“ฑ) if (modalState.groupByColumns && modalState.groupByColumns.length > 0) { modalState.groupByColumns.forEach((colName) => { // ๊ธฐ์กด ํ’ˆ๋ชฉ(originalGroupData[0])์—์„œ groupByColumns ๊ฐ’ ๊ฐ€์ ธ์˜ค๊ธฐ const referenceData = originalGroupData[0] || groupData.find((item) => item.id); if (referenceData && referenceData[colName]) { insertData[colName] = referenceData[colName]; console.log(`๐Ÿ”‘ [์‹ ๊ทœ ํ’ˆ๋ชฉ] ${colName} ๊ฐ’ ์ถ”๊ฐ€:`, referenceData[colName]); } }); } // ๐Ÿ†• ๊ณตํ†ต ํ•„๋“œ ์ถ”๊ฐ€ (๊ฑฐ๋ž˜์ฒ˜, ๋‹ด๋‹น์ž, ๋‚ฉํ’ˆ์ฒ˜, ๋ฉ”๋ชจ ๋“ฑ) // formData์—์„œ ํ’ˆ๋ชฉ๋ณ„ ํ•„๋“œ๊ฐ€ ์•„๋‹Œ ๊ณตํ†ต ํ•„๋“œ๋ฅผ ๋ณต์‚ฌ const commonFields = [ "partner_id", // ๊ฑฐ๋ž˜์ฒ˜ "manager_id", // ๋‹ด๋‹น์ž "delivery_partner_id", // ๋‚ฉํ’ˆ์ฒ˜ "delivery_address", // ๋‚ฉํ’ˆ์žฅ์†Œ "memo", // ๋ฉ”๋ชจ "order_date", // ์ฃผ๋ฌธ์ผ "due_date", // ๋‚ฉ๊ธฐ์ผ "shipping_method", // ๋ฐฐ์†ก๋ฐฉ๋ฒ• "status", // ์ƒํƒœ "sales_type", // ์˜์—…์œ ํ˜• ]; commonFields.forEach((fieldName) => { // formData์— ๊ฐ’์ด ์žˆ์œผ๋ฉด ์ถ”๊ฐ€ if (formData[fieldName] !== undefined && formData[fieldName] !== null) { // ๋‚ ์งœ ํ•„๋“œ์ธ ๊ฒฝ์šฐ ์ •๊ทœํ™” if (dateFields.includes(fieldName)) { const normalizedDate = normalizeDateField(formData[fieldName]); if (normalizedDate) { insertData[fieldName] = normalizedDate; console.log(`๐Ÿ”— [๊ณตํ†ต ํ•„๋“œ - ๋‚ ์งœ] ${fieldName} ๊ฐ’ ์ถ”๊ฐ€:`, normalizedDate); } } else { insertData[fieldName] = formData[fieldName]; console.log(`๐Ÿ”— [๊ณตํ†ต ํ•„๋“œ] ${fieldName} ๊ฐ’ ์ถ”๊ฐ€:`, formData[fieldName]); } } }); console.log("๐Ÿ“ฆ [์‹ ๊ทœ ํ’ˆ๋ชฉ] ์ตœ์ข… insertData:", insertData); console.log("๐Ÿ“‹ [์‹ ๊ทœ ํ’ˆ๋ชฉ] ์ตœ์ข… insertData ํ‚ค ๋ชฉ๋ก:", Object.keys(insertData)); try { const response = await dynamicFormApi.saveFormData({ screenId: modalState.screenId || 0, tableName: screenData.screenInfo.tableName, data: insertData, }); if (response.success) { insertedCount++; console.log("โœ… ์‹ ๊ทœ ํ’ˆ๋ชฉ ์ถ”๊ฐ€ ์„ฑ๊ณต:", response.data); } else { console.error("โŒ ์‹ ๊ทœ ํ’ˆ๋ชฉ ์ถ”๊ฐ€ ์‹คํŒจ:", response.message); } } catch (error: any) { console.error("โŒ ์‹ ๊ทœ ํ’ˆ๋ชฉ ์ถ”๊ฐ€ ์˜ค๋ฅ˜:", error); } } } // 2๏ธโƒฃ ๊ธฐ์กด ํ’ˆ๋ชฉ ์ˆ˜์ • (id๊ฐ€ ์žˆ๋Š” ํ•ญ๋ชฉ) for (const currentData of groupData) { if (currentData.id) { // id ๊ธฐ๋ฐ˜ ๋งค์นญ (์ธ๋ฑ์Šค ๊ธฐ๋ฐ˜ X) const originalItemData = originalGroupData.find((orig) => orig.id === currentData.id); if (!originalItemData) { console.warn(`์›๋ณธ ๋ฐ์ดํ„ฐ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค (id: ${currentData.id})`); continue; } // ๐Ÿ†• ๊ฐ’ ์ •๊ทœํ™” ํ•จ์ˆ˜ (ํƒ€์ž… ํ†ต์ผ) const normalizeValue = (val: any, fieldName?: string): any => { if (val === null || val === undefined || val === "") return null; // ๋‚ ์งœ ํ•„๋“œ์ธ ๊ฒฝ์šฐ YYYY-MM-DD ํ˜•์‹์œผ๋กœ ์ •๊ทœํ™” if (fieldName && dateFields.includes(fieldName)) { const normalizedDate = normalizeDateField(val); return normalizedDate; } if (typeof val === "string" && !isNaN(Number(val))) { // ์ˆซ์ž๋กœ ๋ณ€ํ™˜ ๊ฐ€๋Šฅํ•œ ๋ฌธ์ž์—ด์€ ์ˆซ์ž๋กœ return Number(val); } return val; }; // ๋ณ€๊ฒฝ๋œ ํ•„๋“œ๋งŒ ์ถ”์ถœ (id ์ œ์™ธ) const changedData: Record = {}; Object.keys(currentData).forEach((key) => { // id๋Š” ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€ if (key === "id") { return; } // ๐Ÿ†• ํƒ€์ž… ์ •๊ทœํ™” ํ›„ ๋น„๊ต const currentValue = normalizeValue(currentData[key], key); const originalValue = normalizeValue(originalItemData[key], key); // ๊ฐ’์ด ๋ณ€๊ฒฝ๋œ ๊ฒฝ์šฐ๋งŒ ํฌํ•จ if (currentValue !== originalValue) { console.log(`๐Ÿ” [ํ’ˆ๋ชฉ ์ˆ˜์ • ๊ฐ์ง€] ${key}: ${originalValue} โ†’ ${currentValue}`); // ๋‚ ์งœ ํ•„๋“œ๋Š” ์ •๊ทœํ™”๋œ ๊ฐ’ ์‚ฌ์šฉ, ๋‚˜๋จธ์ง€๋Š” ์›๋ณธ ๊ฐ’ ์‚ฌ์šฉ let finalValue = dateFields.includes(key) ? currentValue : currentData[key]; // ๐Ÿ”ง ๋ฐฐ์—ด์ด๋ฉด ์‰ผํ‘œ ๊ตฌ๋ถ„ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ (๋ฆฌํ”ผํ„ฐ ๋ฐ์ดํ„ฐ ์ œ์™ธ) if (Array.isArray(finalValue)) { const isRepeaterData = finalValue.length > 0 && typeof finalValue[0] === "object" && finalValue[0] !== null && ("_targetTable" in finalValue[0] || "_isNewItem" in finalValue[0] || "_existingRecord" in finalValue[0]); if (!isRepeaterData) { // ๐Ÿ”ง ์†์ƒ๋œ ๊ฐ’ ํ•„ํ„ฐ๋ง ํ—ฌํผ const isValidValue = (v: any): boolean => { if (typeof v === "number" && !isNaN(v)) return true; if (typeof v !== "string") return false; if (!v || v.trim() === "") return false; if (v.includes("{") || v.includes("}") || v.includes('"') || v.includes("\\")) return false; return true; }; const validValues = finalValue .map((v: any) => typeof v === "number" ? String(v) : v) .filter(isValidValue); const stringValue = validValues.join(","); console.log(`๐Ÿ”ง [EditModal ๊ทธ๋ฃนUPDATE] ๋ฐฐ์—ดโ†’๋ฌธ์ž์—ด ๋ณ€ํ™˜: ${key}`, { original: finalValue.length, valid: validValues.length, converted: stringValue }); finalValue = stringValue; } } changedData[key] = finalValue; } }); // ๋ณ€๊ฒฝ์‚ฌํ•ญ์ด ์—†์œผ๋ฉด ์Šคํ‚ต if (Object.keys(changedData).length === 0) { console.log(`๋ณ€๊ฒฝ์‚ฌํ•ญ ์—†์Œ (id: ${currentData.id})`); continue; } // UPDATE ์‹คํ–‰ try { const response = await dynamicFormApi.updateFormDataPartial( currentData.id, originalItemData, changedData, screenData.screenInfo.tableName, ); if (response.success) { updatedCount++; console.log(`โœ… ํ’ˆ๋ชฉ ์ˆ˜์ • ์„ฑ๊ณต (id: ${currentData.id})`); } else { console.error(`โŒ ํ’ˆ๋ชฉ ์ˆ˜์ • ์‹คํŒจ (id: ${currentData.id}):`, response.message); } } catch (error: any) { console.error(`โŒ ํ’ˆ๋ชฉ ์ˆ˜์ • ์˜ค๋ฅ˜ (id: ${currentData.id}):`, error); } } } // 3๏ธโƒฃ ์‚ญ์ œ๋œ ํ’ˆ๋ชฉ ์ œ๊ฑฐ (์›๋ณธ์—๋Š” ์žˆ์ง€๋งŒ ํ˜„์žฌ ๋ฐ์ดํ„ฐ์—๋Š” ์—†๋Š” ํ•ญ๋ชฉ) const currentIds = new Set(groupData.map((item) => item.id).filter(Boolean)); const deletedItems = originalGroupData.filter((orig) => orig.id && !currentIds.has(orig.id)); for (const deletedItem of deletedItems) { console.log("๐Ÿ—‘๏ธ ํ’ˆ๋ชฉ ์‚ญ์ œ:", deletedItem); try { // screenId ์ „๋‹ฌํ•˜์—ฌ ์ œ์–ด๊ด€๋ฆฌ ์‹คํ–‰ ๊ฐ€๋Šฅํ•˜๋„๋ก ํ•จ const response = await dynamicFormApi.deleteFormDataFromTable( deletedItem.id, screenData.screenInfo.tableName, modalState.screenId || screenData.screenInfo?.id, ); if (response.success) { deletedCount++; console.log(`โœ… ํ’ˆ๋ชฉ ์‚ญ์ œ ์„ฑ๊ณต (id: ${deletedItem.id})`); } else { console.error(`โŒ ํ’ˆ๋ชฉ ์‚ญ์ œ ์‹คํŒจ (id: ${deletedItem.id}):`, response.message); } } catch (error: any) { console.error(`โŒ ํ’ˆ๋ชฉ ์‚ญ์ œ ์˜ค๋ฅ˜ (id: ${deletedItem.id}):`, error); } } // ๊ฒฐ๊ณผ ๋ฉ”์‹œ์ง€ const messages: string[] = []; if (insertedCount > 0) messages.push(`${insertedCount}๊ฐœ ์ถ”๊ฐ€`); if (updatedCount > 0) messages.push(`${updatedCount}๊ฐœ ์ˆ˜์ •`); if (deletedCount > 0) messages.push(`${deletedCount}๊ฐœ ์‚ญ์ œ`); if (messages.length > 0) { toast.success(`ํ’ˆ๋ชฉ์ด ์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค (${messages.join(", ")})`); // ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์˜ onSave ์ฝœ๋ฐฑ ์‹คํ–‰ (ํ…Œ์ด๋ธ” ์ƒˆ๋กœ๊ณ ์นจ) if (modalState.onSave) { try { modalState.onSave(); } catch (callbackError) { console.error("โš ๏ธ onSave ์ฝœ๋ฐฑ ์—๋Ÿฌ:", callbackError); } } // ๐Ÿ†• ์ €์žฅ ํ›„ ์ œ์–ด๋กœ์ง ์‹คํ–‰ (๋ฒ„ํŠผ์˜ After ํƒ€์ด๋ฐ ์ œ์–ด) // ์šฐ์„ ์ˆœ์œ„: ๋ชจ๋‹ฌ ๋‚ด๋ถ€ ์ €์žฅ ๋ฒ„ํŠผ ์„ค์ •(saveButtonConfig) > ์ˆ˜์ • ๋ฒ„ํŠผ์—์„œ ์ „๋‹ฌ๋ฐ›์€ ์„ค์ •(buttonConfig) try { const controlConfig = modalState.saveButtonConfig || modalState.buttonConfig; console.log("[EditModal] ๊ทธ๋ฃน ์ €์žฅ ์™„๋ฃŒ ํ›„ ์ œ์–ด๋กœ์ง ์‹คํ–‰ ์‹œ๋„", { hasSaveButtonConfig: !!modalState.saveButtonConfig, hasButtonConfig: !!modalState.buttonConfig, controlConfig, }); // ๐Ÿ”ง executionTiming ์ฒดํฌ: dataflowTiming ๋˜๋Š” flowConfig.executionTiming ๋˜๋Š” flowControls ํ™•์ธ const flowTiming = controlConfig?.dataflowTiming || controlConfig?.dataflowConfig?.flowConfig?.executionTiming || (controlConfig?.dataflowConfig?.flowControls?.length > 0 ? "after" : null); if (controlConfig?.enableDataflowControl && flowTiming === "after") { console.log("๐ŸŽฏ [EditModal] ์ €์žฅ ํ›„ ์ œ์–ด๋กœ์ง ๋ฐœ๊ฒฌ:", controlConfig.dataflowConfig); // buttonActions์˜ executeAfterSaveControl ๋™์  import const { ButtonActionExecutor } = await import("@/lib/utils/buttonActions"); // ์ œ์–ด๋กœ์ง ์‹คํ–‰ await ButtonActionExecutor.executeAfterSaveControl( controlConfig, { formData: modalState.editData, screenId: modalState.buttonContext?.screenId || modalState.screenId, tableName: modalState.buttonContext?.tableName || screenData?.screenInfo?.tableName, userId: user?.userId, companyCode: user?.companyCode, onRefresh: modalState.onSave, } ); console.log("โœ… [EditModal] ์ œ์–ด๋กœ์ง ์‹คํ–‰ ์™„๋ฃŒ"); } else { console.log("โ„น๏ธ [EditModal] ์ €์žฅ ํ›„ ์‹คํ–‰ํ•  ์ œ์–ด๋กœ์ง ์—†์Œ"); } } catch (controlError) { console.error("โŒ [EditModal] ์ œ์–ด๋กœ์ง ์‹คํ–‰ ์˜ค๋ฅ˜:", controlError); // ์ œ์–ด๋กœ์ง ์˜ค๋ฅ˜๋Š” ์ €์žฅ ์„ฑ๊ณต์„ ๋ฐฉํ•ดํ•˜์ง€ ์•Š์Œ (๊ฒฝ๊ณ ๋งŒ ํ‘œ์‹œ) toast.warning("์ €์žฅ์€ ์™„๋ฃŒ๋˜์—ˆ์œผ๋‚˜ ์—ฐ๊ฒฐ๋œ ์ œ์–ด ์‹คํ–‰ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค."); } handleClose(); } else { toast.info("๋ณ€๊ฒฝ๋œ ๋‚ด์šฉ์ด ์—†์Šต๋‹ˆ๋‹ค."); handleClose(); } return; } // ======================================== // INSERT/UPDATE ํŒ๋‹จ (์žฌ์„ค๊ณ„) // ======================================== // ํŒ๋‹จ ๊ธฐ์ค€: // 1. isCreateModeFlag === true โ†’ ๋ฌด์กฐ๊ฑด INSERT (๋ณต์‚ฌ/๋“ฑ๋ก ๋ชจ๋“œ ๋ณดํ˜ธ) // 2. isCreateModeFlag === false โ†’ formData.id ์žˆ์œผ๋ฉด UPDATE, ์—†์œผ๋ฉด INSERT // originalData๋Š” INSERT/UPDATE ํŒ๋‹จ์— ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ (changedData ๊ณ„์‚ฐ์—๋งŒ ์‚ฌ์šฉ) // ======================================== let isCreateMode: boolean; if (isCreateModeFlag) { // ์ด๋ฒคํŠธ์—์„œ ๋ช…์‹œ์ ์œผ๋กœ INSERT ๋ชจ๋“œ๋กœ ์ง€์ •๋จ (๋“ฑ๋ก/๋ณต์‚ฌ) isCreateMode = true; } else { // ์ˆ˜์ • ๋ชจ๋“œ: formData์— id๊ฐ€ ์žˆ์œผ๋ฉด UPDATE, ์—†์œผ๋ฉด INSERT isCreateMode = !formData.id; } console.log("[EditModal] ์ €์žฅ ๋ชจ๋“œ ํŒ๋‹จ:", { isCreateMode, isCreateModeFlag, formDataId: formData.id, originalDataLength: Object.keys(originalData).length, tableName: screenData.screenInfo.tableName, }); if (isCreateMode) { // INSERT ๋ชจ๋“œ console.log("[EditModal] INSERT ๋ชจ๋“œ - ์ƒˆ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ:", formData); // ๐Ÿ†• ์ฑ„๋ฒˆ ๊ทœ์น™ ํ• ๋‹น ์ฒ˜๋ฆฌ (์ €์žฅ ์‹œ์ ์— ์‹ค์ œ ์ˆœ๋ฒˆ ์ฆ๊ฐ€) const dataToSave = { ...formData }; const fieldsWithNumbering: Record = {}; // formData์—์„œ ์ฑ„๋ฒˆ ๊ทœ์น™์ด ์„ค์ •๋œ ํ•„๋“œ ์ฐพ๊ธฐ for (const [key, value] of Object.entries(formData)) { if (key.endsWith("_numberingRuleId") && value) { const fieldName = key.replace("_numberingRuleId", ""); fieldsWithNumbering[fieldName] = value as string; console.log(`๐ŸŽฏ [EditModal] ์ฑ„๋ฒˆ ๊ทœ์น™ ๋ฐœ๊ฒฌ: ${fieldName} โ†’ ๊ทœ์น™ ${value}`); } } // ์ฑ„๋ฒˆ ๊ทœ์น™์ด ์žˆ๋Š” ํ•„๋“œ์— ๋Œ€ํ•ด allocateCode ํ˜ธ์ถœ (๐Ÿš€ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๋กœ ์ตœ์ ํ™”) if (Object.keys(fieldsWithNumbering).length > 0) { console.log("๐ŸŽฏ [EditModal] ์ฑ„๋ฒˆ ๊ทœ์น™ ํ• ๋‹น ์‹œ์ž‘, formData:", { material: formData.material, allKeys: Object.keys(formData), }); const { allocateNumberingCode } = await import("@/lib/api/numberingRule"); // ๐Ÿš€ Promise.all๋กœ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ (์—ฌ๋Ÿฌ ์ฑ„๋ฒˆ ํ•„๋“œ๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ ์„ฑ๋Šฅ ํ–ฅ์ƒ) const allocationPromises = Object.entries(fieldsWithNumbering).map( async ([fieldName, ruleId]) => { const userInputCode = dataToSave[fieldName] as string; console.log(`๐Ÿ”„ [EditModal] ${fieldName} ํ•„๋“œ์— ๋Œ€ํ•ด allocateCode ํ˜ธ์ถœ: ${ruleId}`); try { const allocateResult = await allocateNumberingCode(ruleId, userInputCode, formData); if (allocateResult.success && allocateResult.data?.generatedCode) { return { fieldName, success: true, code: allocateResult.data.generatedCode }; } else { console.warn(`โš ๏ธ [EditModal] ${fieldName} ์ฝ”๋“œ ํ• ๋‹น ์‹คํŒจ:`, allocateResult.error); return { fieldName, success: false, hasExistingValue: !!(dataToSave[fieldName]) }; } } catch (allocateError) { console.error(`โŒ [EditModal] ${fieldName} ์ฝ”๋“œ ํ• ๋‹น ์˜ค๋ฅ˜:`, allocateError); return { fieldName, success: false, hasExistingValue: !!(dataToSave[fieldName]) }; } } ); const allocationResults = await Promise.all(allocationPromises); // ๊ฒฐ๊ณผ ์ฒ˜๋ฆฌ const failedFields: string[] = []; for (const result of allocationResults) { if (result.success && result.code) { console.log(`โœ… [EditModal] ${result.fieldName} ์ƒˆ ์ฝ”๋“œ ํ• ๋‹น: ${result.code}`); dataToSave[result.fieldName] = result.code; } else if (!result.hasExistingValue) { failedFields.push(result.fieldName); } } // ์ฑ„๋ฒˆ ๊ทœ์น™ ํ• ๋‹น ์‹คํŒจ ์‹œ ์ €์žฅ ์ค‘๋‹จ if (failedFields.length > 0) { const fieldNames = failedFields.join(", "); toast.error(`์ฑ„๋ฒˆ ๊ทœ์น™ ํ• ๋‹น์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค (${fieldNames}). ํ™”๋ฉด ์„ค์ •์—์„œ ์ฑ„๋ฒˆ ๊ทœ์น™์„ ํ™•์ธํ•ด์ฃผ์„ธ์š”.`); console.error(`โŒ [EditModal] ์ฑ„๋ฒˆ ๊ทœ์น™ ํ• ๋‹น ์‹คํŒจ๋กœ ์ €์žฅ ์ค‘๋‹จ. ์‹คํŒจ ํ•„๋“œ: ${fieldNames}`); return; } // _numberingRuleId ํ•„๋“œ ์ œ๊ฑฐ (์‹ค์ œ ์ €์žฅํ•˜์ง€ ์•Š์Œ) for (const key of Object.keys(dataToSave)) { if (key.endsWith("_numberingRuleId")) { delete dataToSave[key]; } } } // ๐Ÿ†• ๋ฆฌํ”ผํ„ฐ ๋ฐ์ดํ„ฐ(๋ฐฐ์—ด)๋ฅผ ๋งˆ์Šคํ„ฐ ์ €์žฅ์—์„œ ์ œ์™ธ (V2Repeater๊ฐ€ ๋ณ„๋„๋กœ ์ €์žฅ) // ๐Ÿ”ง ๋‹จ, ๋‹ค์ค‘ ์„ ํƒ ๋ฐฐ์—ด์€ ์‰ผํ‘œ ๊ตฌ๋ถ„ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์ €์žฅ const masterDataToSave: Record = {}; Object.entries(dataToSave).forEach(([key, value]) => { if (!Array.isArray(value)) { masterDataToSave[key] = value; } else { // ๋ฆฌํ”ผํ„ฐ ๋ฐ์ดํ„ฐ์ธ์ง€ ํ™•์ธ (๊ฐ์ฒด ๋ฐฐ์—ด์ด๊ณ  _targetTable ๋˜๋Š” _isNewItem์ด ์žˆ์œผ๋ฉด ๋ฆฌํ”ผํ„ฐ) const isRepeaterData = value.length > 0 && typeof value[0] === "object" && value[0] !== null && ("_targetTable" in value[0] || "_isNewItem" in value[0] || "_existingRecord" in value[0]); if (isRepeaterData) { console.log(`๐Ÿ”„ [EditModal] ๋ฆฌํ”ผํ„ฐ ๋ฐ์ดํ„ฐ ์ œ์™ธ (๋ณ„๋„ ์ €์žฅ): ${key}, ${value.length}๊ฐœ ํ•ญ๋ชฉ`); } else { // ๐Ÿ”ง ์†์ƒ๋œ ๊ฐ’ ํ•„ํ„ฐ๋ง ํ—ฌํผ (์ค‘๊ด„ํ˜ธ, ๋”ฐ์˜ดํ‘œ, ๋ฐฑ์Šฌ๋ž˜์‹œ ํฌํ•จ ์‹œ ๋ฌดํšจ) const isValidValue = (v: any): boolean => { if (typeof v === "number" && !isNaN(v)) return true; if (typeof v !== "string") return false; if (!v || v.trim() === "") return false; if (v.includes("{") || v.includes("}") || v.includes('"') || v.includes("\\")) return false; return true; }; // ๐Ÿ”ง ๋‹ค์ค‘ ์„ ํƒ ๋ฐฐ์—ด โ†’ ์‰ผํ‘œ ๊ตฌ๋ถ„ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ (์†์ƒ๋œ ๊ฐ’ ํ•„ํ„ฐ๋ง) const validValues = value .map((v: any) => typeof v === "number" ? String(v) : v) .filter(isValidValue); const stringValue = validValues.join(","); console.log(`๐Ÿ”ง [EditModal CREATE] ๋ฐฐ์—ดโ†’๋ฌธ์ž์—ด ๋ณ€ํ™˜: ${key}`, { original: value.length, valid: validValues.length, converted: stringValue }); masterDataToSave[key] = stringValue; } } }); console.log("[EditModal] ์ตœ์ข… ์ €์žฅ ๋ฐ์ดํ„ฐ:", masterDataToSave); const response = await dynamicFormApi.saveFormData({ screenId: modalState.screenId!, tableName: screenData.screenInfo.tableName, data: masterDataToSave, }); if (response.success) { const masterRecordId = response.data?.id || formData.id; // ๐Ÿ†• ๋ฆฌํ”ผํ„ฐ ๋ฐ์ดํ„ฐ ์ €์žฅ ์ด๋ฒคํŠธ ๋ฐœ์ƒ (V2Repeater ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ์Šค๋‹) window.dispatchEvent( new CustomEvent("repeaterSave", { detail: { parentId: masterRecordId, masterRecordId, mainFormData: formData, tableName: screenData.screenInfo.tableName, }, }), ); console.log("๐Ÿ“‹ [EditModal] repeaterSave ์ด๋ฒคํŠธ ๋ฐœ์ƒ:", { masterRecordId, tableName: screenData.screenInfo.tableName }); toast.success("๋ฐ์ดํ„ฐ๊ฐ€ ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค."); // ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์˜ onSave ์ฝœ๋ฐฑ ์‹คํ–‰ (ํ…Œ์ด๋ธ” ์ƒˆ๋กœ๊ณ ์นจ) if (modalState.onSave) { try { modalState.onSave(); } catch (callbackError) { console.error("onSave ์ฝœ๋ฐฑ ์—๋Ÿฌ:", callbackError); } } // ๐Ÿ†• ์ €์žฅ ํ›„ ์ œ์–ด๋กœ์ง ์‹คํ–‰ (๋ฒ„ํŠผ์˜ After ํƒ€์ด๋ฐ ์ œ์–ด) // ์šฐ์„ ์ˆœ์œ„: ๋ชจ๋‹ฌ ๋‚ด๋ถ€ ์ €์žฅ ๋ฒ„ํŠผ ์„ค์ •(saveButtonConfig) > ์ˆ˜์ • ๋ฒ„ํŠผ์—์„œ ์ „๋‹ฌ๋ฐ›์€ ์„ค์ •(buttonConfig) try { const controlConfig = modalState.saveButtonConfig || modalState.buttonConfig; console.log("[EditModal] INSERT ์™„๋ฃŒ ํ›„ ์ œ์–ด๋กœ์ง ์‹คํ–‰ ์‹œ๋„", { controlConfig }); // ๐Ÿ”ง executionTiming ์ฒดํฌ: dataflowTiming ๋˜๋Š” flowConfig.executionTiming ๋˜๋Š” flowControls ํ™•์ธ const flowTimingInsert = controlConfig?.dataflowTiming || controlConfig?.dataflowConfig?.flowConfig?.executionTiming || (controlConfig?.dataflowConfig?.flowControls?.length > 0 ? "after" : null); if (controlConfig?.enableDataflowControl && flowTimingInsert === "after") { console.log("๐ŸŽฏ [EditModal] ์ €์žฅ ํ›„ ์ œ์–ด๋กœ์ง ๋ฐœ๊ฒฌ:", controlConfig.dataflowConfig); const { ButtonActionExecutor } = await import("@/lib/utils/buttonActions"); await ButtonActionExecutor.executeAfterSaveControl( controlConfig, { formData, screenId: modalState.buttonContext?.screenId || modalState.screenId, tableName: modalState.buttonContext?.tableName || screenData?.screenInfo?.tableName, userId: user?.userId, companyCode: user?.companyCode, onRefresh: modalState.onSave, } ); console.log("โœ… [EditModal] ์ œ์–ด๋กœ์ง ์‹คํ–‰ ์™„๋ฃŒ"); } } catch (controlError) { console.error("โŒ [EditModal] ์ œ์–ด๋กœ์ง ์‹คํ–‰ ์˜ค๋ฅ˜:", controlError); toast.warning("์ €์žฅ์€ ์™„๋ฃŒ๋˜์—ˆ์œผ๋‚˜ ์—ฐ๊ฒฐ๋œ ์ œ์–ด ์‹คํ–‰ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค."); } handleClose(); } else { throw new Error(response.message || "์ƒ์„ฑ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."); } } else { // UPDATE ๋ชจ๋“œ - PUT (์ „์ฒด ์—…๋ฐ์ดํŠธ) // originalData ๋น„๊ต ์—†์ด formData ์ „์ฒด๋ฅผ ๋ณด๋ƒ„ const recordId = formData.id; if (!recordId) { console.error("[EditModal] UPDATE ์‹คํŒจ: formData์— id๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.", { formDataKeys: Object.keys(formData), }); toast.error("์ˆ˜์ •ํ•  ๋ ˆ์ฝ”๋“œ์˜ ID๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); return; } // ๋ฐฐ์—ด ๊ฐ’ โ†’ ์‰ผํ‘œ ๊ตฌ๋ถ„ ๋ฌธ์ž์—ด ๋ณ€ํ™˜ (๋ฆฌํ”ผํ„ฐ ๋ฐ์ดํ„ฐ ์ œ์™ธ) const dataToSave: Record = {}; Object.entries(formData).forEach(([key, value]) => { if (Array.isArray(value)) { const isRepeaterData = value.length > 0 && typeof value[0] === "object" && value[0] !== null && ("_targetTable" in value[0] || "_isNewItem" in value[0] || "_existingRecord" in value[0]); if (isRepeaterData) { // ๋ฆฌํ”ผํ„ฐ ๋ฐ์ดํ„ฐ๋Š” ์ œ์™ธ (๋ณ„๋„ ์ €์žฅ) return; } // ๋‹ค์ค‘ ์„ ํƒ ๋ฐฐ์—ด โ†’ ์‰ผํ‘œ ๊ตฌ๋ถ„ ๋ฌธ์ž์—ด const validValues = value .map((v: any) => typeof v === "number" ? String(v) : v) .filter((v: any) => { if (typeof v === "number") return true; if (typeof v !== "string") return false; if (!v || v.trim() === "") return false; if (v.includes("{") || v.includes("}") || v.includes('"') || v.includes("\\")) return false; return true; }); dataToSave[key] = validValues.join(","); } else { dataToSave[key] = value; } }); console.log("[EditModal] UPDATE(PUT) ์‹คํ–‰:", { recordId, fieldCount: Object.keys(dataToSave).length, tableName: screenData.screenInfo.tableName, }); const response = await dynamicFormApi.updateFormData(recordId, { tableName: screenData.screenInfo.tableName, data: dataToSave, }); if (response.success) { toast.success("๋ฐ์ดํ„ฐ๊ฐ€ ์ˆ˜์ •๋˜์—ˆ์Šต๋‹ˆ๋‹ค."); // ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์˜ onSave ์ฝœ๋ฐฑ ์‹คํ–‰ (ํ…Œ์ด๋ธ” ์ƒˆ๋กœ๊ณ ์นจ) if (modalState.onSave) { try { modalState.onSave(); } catch (callbackError) { console.error("onSave ์ฝœ๋ฐฑ ์—๋Ÿฌ:", callbackError); } } // ๐Ÿ†• ์ €์žฅ ํ›„ ์ œ์–ด๋กœ์ง ์‹คํ–‰ (๋ฒ„ํŠผ์˜ After ํƒ€์ด๋ฐ ์ œ์–ด) // ์šฐ์„ ์ˆœ์œ„: ๋ชจ๋‹ฌ ๋‚ด๋ถ€ ์ €์žฅ ๋ฒ„ํŠผ ์„ค์ •(saveButtonConfig) > ์ˆ˜์ • ๋ฒ„ํŠผ์—์„œ ์ „๋‹ฌ๋ฐ›์€ ์„ค์ •(buttonConfig) try { const controlConfig = modalState.saveButtonConfig || modalState.buttonConfig; console.log("[EditModal] UPDATE ์™„๋ฃŒ ํ›„ ์ œ์–ด๋กœ์ง ์‹คํ–‰ ์‹œ๋„", { controlConfig }); // ๐Ÿ”ง executionTiming ์ฒดํฌ: dataflowTiming ๋˜๋Š” flowConfig.executionTiming ๋˜๋Š” flowControls ํ™•์ธ const flowTimingUpdate = controlConfig?.dataflowTiming || controlConfig?.dataflowConfig?.flowConfig?.executionTiming || (controlConfig?.dataflowConfig?.flowControls?.length > 0 ? "after" : null); if (controlConfig?.enableDataflowControl && flowTimingUpdate === "after") { console.log("๐ŸŽฏ [EditModal] ์ €์žฅ ํ›„ ์ œ์–ด๋กœ์ง ๋ฐœ๊ฒฌ:", controlConfig.dataflowConfig); const { ButtonActionExecutor } = await import("@/lib/utils/buttonActions"); await ButtonActionExecutor.executeAfterSaveControl( controlConfig, { formData, screenId: modalState.buttonContext?.screenId || modalState.screenId, tableName: modalState.buttonContext?.tableName || screenData?.screenInfo?.tableName, userId: user?.userId, companyCode: user?.companyCode, onRefresh: modalState.onSave, } ); console.log("โœ… [EditModal] ์ œ์–ด๋กœ์ง ์‹คํ–‰ ์™„๋ฃŒ"); } } catch (controlError) { console.error("โŒ [EditModal] ์ œ์–ด๋กœ์ง ์‹คํ–‰ ์˜ค๋ฅ˜:", controlError); toast.warning("์ €์žฅ์€ ์™„๋ฃŒ๋˜์—ˆ์œผ๋‚˜ ์—ฐ๊ฒฐ๋œ ์ œ์–ด ์‹คํ–‰ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค."); } handleClose(); } else { throw new Error(response.message || "์ˆ˜์ •์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."); } } } catch (error: any) { console.error("โŒ ์ˆ˜์ • ์‹คํŒจ:", error); toast.error(error.message || "๋ฐ์ดํ„ฐ ์ˆ˜์ • ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค."); } }; // ๋ชจ๋‹ฌ ํฌ๊ธฐ ์„ค์ • - ํ™”๋ฉด๊ด€๋ฆฌ ์„ค์ • ํฌ๊ธฐ + ํ—ค๋” const getModalStyle = () => { if (!screenDimensions) { return { className: "w-fit min-w-[400px] max-w-4xl max-h-[90vh] overflow-hidden p-0", style: undefined, // undefined๋กœ ๋ณ€๊ฒฝ - defaultWidth/defaultHeight ์‚ฌ์šฉ }; } // ํ™”๋ฉด๊ด€๋ฆฌ์—์„œ ์„ค์ •ํ•œ ํฌ๊ธฐ = ์ปจํ…์ธ  ์˜์—ญ ํฌ๊ธฐ // ์‹ค์ œ ๋ชจ๋‹ฌ ํฌ๊ธฐ = ์ปจํ…์ธ  + ํ—ค๋” + gap + padding + ๋ผ๋ฒจ ๊ณต๊ฐ„ const headerHeight = 52; // DialogHeader (ํƒ€์ดํ‹€ + border-b + py-3) const dialogGap = 16; // DialogContent gap-4 const extraPadding = 24; // ์ถ”๊ฐ€ ์—ฌ๋ฐฑ (์•ˆ์ „ ๋งˆ์ง„) const labelSpace = 30; // ์ž…๋ ฅ ํ•„๋“œ ์œ„ ๋ผ๋ฒจ ๊ณต๊ฐ„ (-top-6 = 24px + ์—ฌ์œ ) const totalHeight = screenDimensions.height + headerHeight + dialogGap + extraPadding + labelSpace; return { className: "overflow-hidden p-0", style: { width: `${Math.min(screenDimensions.width + 48, window.innerWidth * 0.98)}px`, // ์ขŒ์šฐ ํŒจ๋”ฉ ์ถ”๊ฐ€ height: `${Math.min(totalHeight, window.innerHeight * 0.95)}px`, maxWidth: "98vw", maxHeight: "95vh", }, }; }; const modalStyle = getModalStyle(); return (
{modalState.title || "๋ฐ์ดํ„ฐ ์ˆ˜์ •"} {modalState.description && !loading && ( {modalState.description} )} {loading && ( {loading ? "ํ™”๋ฉด์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค..." : ""} )}
{loading ? (

ํ™”๋ฉด์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘...

) : screenData ? (
{ const baseHeight = (screenDimensions?.height || 600) + 30; if (activeConditionalComponents.length > 0) { // ์กฐ๊ฑด๋ถ€ ๋ ˆ์ด์–ด ์ปดํฌ๋„ŒํŠธ ์ค‘ ๊ฐ€์žฅ ์•„๋ž˜ ์œ„์น˜ ๊ณ„์‚ฐ const offsetY = screenDimensions?.offsetY || 0; let maxBottom = 0; activeConditionalComponents.forEach((comp) => { const y = parseFloat(comp.position?.y?.toString() || "0") - offsetY + 30; const h = parseFloat(comp.size?.height?.toString() || "40"); maxBottom = Math.max(maxBottom, y + h); }); return Math.max(baseHeight, maxBottom + 20); // 20px ์—ฌ๋ฐฑ } return baseHeight; })(), transformOrigin: "center center", maxWidth: "100%", }} > {/* ๊ธฐ๋ณธ ๋ ˆ์ด์–ด ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง */} {screenData.components.map((component) => { // ์ปดํฌ๋„ŒํŠธ ์œ„์น˜๋ฅผ offset๋งŒํผ ์กฐ์ • const offsetX = screenDimensions?.offsetX || 0; const offsetY = screenDimensions?.offsetY || 0; const labelSpace = 30; // ๋ผ๋ฒจ ๊ณต๊ฐ„ (์ž…๋ ฅ ํ•„๋“œ ์œ„ -top-6 ๋ผ๋ฒจ์šฉ) const adjustedComponent = { ...component, position: { ...component.position, x: parseFloat(component.position?.x?.toString() || "0") - offsetX, y: parseFloat(component.position?.y?.toString() || "0") - offsetY + labelSpace, // ๋ผ๋ฒจ ๊ณต๊ฐ„ ์ถ”๊ฐ€ }, }; const groupedDataProp = groupData.length > 0 ? groupData : undefined; const hasUniversalFormModal = screenData.components.some( (c) => { if (c.componentType === "universal-form-modal") return true; return false; } ); const hasTableSectionData = Object.keys(formData).some(k => k.startsWith("_tableSection_") || k.startsWith("__tableSection_") ); const shouldUseEditModalSave = !hasTableSectionData && (groupData.length > 0 || !hasUniversalFormModal); const enrichedFormData = { ...(groupData.length > 0 ? groupData[0] : formData), tableName: screenData.screenInfo?.tableName, screenId: modalState.screenId, }; return ( { if (groupData.length > 0) { if (Array.isArray(value)) { setGroupData(value); } else { setGroupData((prev) => prev.map((item) => ({ ...item, [fieldName]: value, })), ); } } else { setFormData((prev) => ({ ...prev, [fieldName]: value, })); } }} screenInfo={{ id: modalState.screenId!, tableName: screenData.screenInfo?.tableName, }} menuObjid={modalState.menuObjid} onSave={shouldUseEditModalSave ? handleSave : undefined} isInModal={true} groupedData={groupedDataProp} disabledFields={["order_no", "partner_id"]} /> ); })} {/* ๐Ÿ†• ์กฐ๊ฑด๋ถ€ ๋ ˆ์ด์–ด ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง */} {activeConditionalComponents.map((component) => { const offsetX = screenDimensions?.offsetX || 0; const offsetY = screenDimensions?.offsetY || 0; const labelSpace = 30; const adjustedComponent = { ...component, position: { ...component.position, x: parseFloat(component.position?.x?.toString() || "0") - offsetX, y: parseFloat(component.position?.y?.toString() || "0") - offsetY + labelSpace, }, }; const enrichedFormData = { ...(groupData.length > 0 ? groupData[0] : formData), tableName: screenData.screenInfo?.tableName, screenId: modalState.screenId, }; const groupedDataProp = groupData.length > 0 ? groupData : undefined; return ( { if (groupData.length > 0) { if (Array.isArray(value)) { setGroupData(value); } else { setGroupData((prev) => prev.map((item) => ({ ...item, [fieldName]: value, })), ); } } else { setFormData((prev) => ({ ...prev, [fieldName]: value, })); } }} screenInfo={{ id: modalState.screenId!, tableName: screenData.screenInfo?.tableName, }} menuObjid={modalState.menuObjid} isInModal={true} groupedData={groupedDataProp} /> ); })}
) : (

ํ™”๋ฉด ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

)}
); }; export default EditModal;