"use client"; import React, { useEffect, useState, useMemo } from "react"; import { useParams, useSearchParams } from "next/navigation"; import { Button } from "@/components/ui/button"; import { Loader2 } from "lucide-react"; import { screenApi } from "@/lib/api/screen"; import { ScreenDefinition, LayoutData, ComponentData } from "@/types/screen"; import { LayerDefinition } from "@/types/screen-management"; import { useRouter } from "next/navigation"; import { toast } from "sonner"; import { initializeComponents } from "@/lib/registry/components"; import { EditModal } from "@/components/screen/EditModal"; import { RealtimePreview } from "@/components/screen/RealtimePreviewDynamic"; import { FlowButtonGroup } from "@/components/screen/widgets/FlowButtonGroup"; import { FlowVisibilityConfig } from "@/types/control-management"; import { findAllButtonGroups } from "@/lib/utils/flowButtonGroupUtils"; import { DynamicComponentRenderer } from "@/lib/registry/DynamicComponentRenderer"; import { ScreenPreviewProvider } from "@/contexts/ScreenPreviewContext"; import { useAuth } from "@/hooks/useAuth"; // ๐Ÿ†• ์‚ฌ์šฉ์ž ์ •๋ณด import { useResponsive } from "@/lib/hooks/useResponsive"; // ๐Ÿ†• ๋ฐ˜์‘ํ˜• ๊ฐ์ง€ import { TableOptionsProvider } from "@/contexts/TableOptionsContext"; // ํ…Œ์ด๋ธ” ์˜ต์…˜ import { TableSearchWidgetHeightProvider, useTableSearchWidgetHeight } from "@/contexts/TableSearchWidgetHeightContext"; // ๋†’์ด ๊ด€๋ฆฌ import { ScreenContextProvider } from "@/contexts/ScreenContext"; // ์ปดํฌ๋„ŒํŠธ ๊ฐ„ ํ†ต์‹  import { SplitPanelProvider } from "@/lib/registry/components/split-panel-layout/SplitPanelContext"; // ๋ถ„ํ•  ํŒจ๋„ ๋ฆฌ์‚ฌ์ด์ฆˆ import { ActiveTabProvider } from "@/contexts/ActiveTabContext"; // ํ™œ์„ฑ ํƒญ ๊ด€๋ฆฌ import { evaluateConditional } from "@/lib/utils/conditionalEvaluator"; // ์กฐ๊ฑด๋ถ€ ํ‘œ์‹œ ํ‰๊ฐ€ import { ScreenMultiLangProvider } from "@/contexts/ScreenMultiLangContext"; // ํ™”๋ฉด ๋‹ค๊ตญ์–ด import { convertV2ToLegacy, isValidV2Layout } from "@/lib/utils/layoutV2Converter"; // V2 Zod ๊ธฐ๋ฐ˜ ๋ณ€ํ™˜ import { useScheduleGenerator, ScheduleConfirmDialog } from "@/lib/v2-core/services/ScheduleGeneratorService"; // ์Šค์ผ€์ค„ ์ž๋™ ์ƒ์„ฑ function ScreenViewPage() { // ์Šค์ผ€์ค„ ์ž๋™ ์ƒ์„ฑ ์„œ๋น„์Šค ํ™œ์„ฑํ™” const { showConfirmDialog, previewResult, handleConfirm, closeDialog, isLoading: scheduleLoading } = useScheduleGenerator(); const params = useParams(); const searchParams = useSearchParams(); const router = useRouter(); const screenId = parseInt(params.screenId as string); // URL ์ฟผ๋ฆฌ์—์„œ menuObjid ๊ฐ€์ ธ์˜ค๊ธฐ (๋ฉ”๋‰ด ์Šค์ฝ”ํ”„) const menuObjid = searchParams.get("menuObjid") ? parseInt(searchParams.get("menuObjid")!) : undefined; // URL ์ฟผ๋ฆฌ์—์„œ ํ”„๋ฆฌ๋ทฐ์šฉ company_code ๊ฐ€์ ธ์˜ค๊ธฐ const previewCompanyCode = searchParams.get("company_code"); // ํ”„๋ฆฌ๋ทฐ ๋ชจ๋“œ ๊ฐ์ง€ (iframe์—์„œ ๋กœ๋“œ๋  ๋•Œ) const isPreviewMode = searchParams.get("preview") === "true"; // ๐Ÿ†• ํ˜„์žฌ ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž ์ •๋ณด const { user, userName, companyCode: authCompanyCode } = useAuth(); // ํ”„๋ฆฌ๋ทฐ ๋ชจ๋“œ์—์„œ๋Š” URL ํŒŒ๋ผ๋ฏธํ„ฐ์˜ company_code ์šฐ์„  ์‚ฌ์šฉ const companyCode = previewCompanyCode || authCompanyCode; // ๐Ÿ†• ๋ชจ๋ฐ”์ผ ํ™˜๊ฒฝ ๊ฐ์ง€ const { isMobile } = useResponsive(); // ๐Ÿ†• TableSearchWidget ๋†’์ด ๊ด€๋ฆฌ const { getHeightDiff } = useTableSearchWidgetHeight(); const [screen, setScreen] = useState(null); const [layout, setLayout] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [formData, setFormData] = useState>({}); // ํ…Œ์ด๋ธ”์—์„œ ์„ ํƒ๋œ ํ–‰ ๋ฐ์ดํ„ฐ (๋ฒ„ํŠผ ์•ก์…˜์— ์ „๋‹ฌ) const [selectedRowsData, setSelectedRowsData] = useState([]); // ํ…Œ์ด๋ธ” ์ •๋ ฌ ์ •๋ณด (์—‘์…€ ๋‹ค์šด๋กœ๋“œ์šฉ) const [tableSortBy, setTableSortBy] = useState(); const [tableSortOrder, setTableSortOrder] = useState<"asc" | "desc">("asc"); const [tableColumnOrder, setTableColumnOrder] = useState(); const [tableDisplayData, setTableDisplayData] = useState([]); // ํ™”๋ฉด์— ํ‘œ์‹œ๋œ ๋ฐ์ดํ„ฐ (์ปฌ๋Ÿผ ์ˆœ์„œ ํฌํ•จ) // ํ”Œ๋กœ์šฐ์—์„œ ์„ ํƒ๋œ ๋ฐ์ดํ„ฐ (๋ฒ„ํŠผ ์•ก์…˜์— ์ „๋‹ฌ) const [flowSelectedData, setFlowSelectedData] = useState([]); const [flowSelectedStepId, setFlowSelectedStepId] = useState(null); // ํ…Œ์ด๋ธ” ์ƒˆ๋กœ๊ณ ์นจ์„ ์œ„ํ•œ ํ‚ค (๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋ฉด ํ…Œ์ด๋ธ”์ด ๋ฆฌ๋ Œ๋”๋ง๋จ) const [tableRefreshKey, setTableRefreshKey] = useState(0); // ํ”Œ๋กœ์šฐ ์ƒˆ๋กœ๊ณ ์นจ์„ ์œ„ํ•œ ํ‚ค (๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋ฉด ํ”Œ๋กœ์šฐ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง๋จ) const [flowRefreshKey, setFlowRefreshKey] = useState(0); // ๐Ÿ†• ์กฐ๊ฑด๋ถ€ ์ปจํ…Œ์ด๋„ˆ ๋†’์ด ์ถ”์  (์ปดํฌ๋„ŒํŠธ ID โ†’ ๋†’์ด) const [conditionalContainerHeights, setConditionalContainerHeights] = useState>({}); // ๐Ÿ†• ๋ ˆ์ด์–ด ์‹œ์Šคํ…œ ์ง€์› const [conditionalLayers, setConditionalLayers] = useState([]); // ๐Ÿ†• ์กฐ๊ฑด๋ถ€ ์˜์—ญ(Zone) ๋ชฉ๋ก const [zones, setZones] = useState([]); // ํŽธ์ง‘ ๋ชจ๋‹ฌ ์ƒํƒœ const [editModalOpen, setEditModalOpen] = useState(false); const [editModalConfig, setEditModalConfig] = useState<{ screenId?: number; modalSize?: "sm" | "md" | "lg" | "xl" | "full"; editData?: Record; onSave?: () => void; modalTitle?: string; modalDescription?: string; }>({}); // ๋ ˆ์ด์•„์›ƒ ์ค€๋น„ ์™„๋ฃŒ ์ƒํƒœ (๋ฒ„ํŠผ ์œ„์น˜ ๊ณ„์‚ฐ ์™„๋ฃŒ ํ›„ ํ™”๋ฉด ํ‘œ์‹œ) const [layoutReady, setLayoutReady] = useState(true); const containerRef = React.useRef(null); const [scale, setScale] = useState(1); const [containerWidth, setContainerWidth] = useState(0); useEffect(() => { const initComponents = async () => { try { await initializeComponents(); } catch (error) { console.error("โŒ ํ• ๋‹น๋œ ํ™”๋ฉด์—์„œ ์ปดํฌ๋„ŒํŠธ ์‹œ์Šคํ…œ ์ดˆ๊ธฐํ™” ์‹คํŒจ:", error); } }; initComponents(); }, []); // ํŽธ์ง‘ ๋ชจ๋‹ฌ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ๋“ฑ๋ก useEffect(() => { const handleOpenEditModal = (event: CustomEvent) => { // console.log("๐ŸŽญ ํŽธ์ง‘ ๋ชจ๋‹ฌ ์—ด๊ธฐ ์ด๋ฒคํŠธ ์ˆ˜์‹ :", event.detail); setEditModalConfig({ screenId: event.detail.screenId, modalSize: event.detail.modalSize, editData: event.detail.editData, onSave: event.detail.onSave, modalTitle: event.detail.modalTitle, modalDescription: event.detail.modalDescription, }); setEditModalOpen(true); }; // @ts-expect-error - CustomEvent type window.addEventListener("openEditModal", handleOpenEditModal); return () => { // @ts-expect-error - CustomEvent type window.removeEventListener("openEditModal", handleOpenEditModal); }; }, []); useEffect(() => { const loadScreen = async () => { try { setLoading(true); setLayoutReady(false); // ํ™”๋ฉด ๋กœ๋“œ ์‹œ ๋ ˆ์ด์•„์›ƒ ์ค€๋น„ ์ดˆ๊ธฐํ™” setError(null); // ํ™”๋ฉด ์ •๋ณด ๋กœ๋“œ const screenData = await screenApi.getScreen(screenId); setScreen(screenData); // ๋ ˆ์ด์•„์›ƒ ๋กœ๋“œ (V2 ์šฐ์„ , Zod ๊ธฐ๋ฐ˜ ๊ธฐ๋ณธ๊ฐ’ ๋ณ‘ํ•ฉ) try { // V2 API ๋จผ์ € ์‹œ๋„ const v2Response = await screenApi.getLayoutV2(screenId); if (v2Response && isValidV2Layout(v2Response)) { // V2 ๋ ˆ์ด์•„์›ƒ: Zod ๊ธฐ๋ฐ˜ ๋ณ€ํ™˜ (๊ธฐ๋ณธ๊ฐ’ ๋ณ‘ํ•ฉ) const convertedLayout = convertV2ToLegacy(v2Response); if (convertedLayout) { setLayout({ ...convertedLayout, screenResolution: v2Response.screenResolution || convertedLayout.screenResolution, } as LayoutData); } else { throw new Error("V2 ๋ ˆ์ด์•„์›ƒ ๋ณ€ํ™˜ ์‹คํŒจ"); } } else { // V1 ๋ ˆ์ด์•„์›ƒ ๋˜๋Š” ๋นˆ ๋ ˆ์ด์•„์›ƒ const layoutData = await screenApi.getLayout(screenId); setLayout(layoutData); } } catch (layoutError) { console.warn("๋ ˆ์ด์•„์›ƒ ๋กœ๋“œ ์‹คํŒจ, ๋นˆ ๋ ˆ์ด์•„์›ƒ ์‚ฌ์šฉ:", layoutError); setLayout({ screenId, components: [], gridSettings: { columns: 12, gap: 16, padding: 16, enabled: true, size: 8, color: "#e0e0e0", opacity: 0.5, snapToGrid: true, }, }); } } catch (error) { console.error("ํ™”๋ฉด ๋กœ๋“œ ์‹คํŒจ:", error); setError("ํ™”๋ฉด์„ ๋ถˆ๋Ÿฌ์˜ค๋Š”๋ฐ ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."); toast.error("ํ™”๋ฉด์„ ๋ถˆ๋Ÿฌ์˜ค๋Š”๋ฐ ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."); } finally { setLoading(false); } }; if (screenId) { loadScreen(); } }, [screenId]); // ๐Ÿ†• ์กฐ๊ฑด๋ถ€ ๋ ˆ์ด์–ด + Zone ๋กœ๋“œ useEffect(() => { const loadConditionalLayersAndZones = async () => { if (!screenId || !layout) return; try { // 1. Zone ๋กœ๋“œ const loadedZones = await screenApi.getScreenZones(screenId); setZones(loadedZones); // 2. ๋ชจ๋“  ๋ ˆ์ด์–ด ๋ชฉ๋ก ์กฐํšŒ const allLayers = await screenApi.getScreenLayers(screenId); const nonBaseLayers = allLayers.filter((l: any) => l.layer_id > 1); if (nonBaseLayers.length === 0) { setConditionalLayers([]); return; } // 3. ๊ฐ ๋ ˆ์ด์–ด์˜ ๋ ˆ์ด์•„์›ƒ ๋ฐ์ดํ„ฐ ๋กœ๋“œ const layerDefinitions: LayerDefinition[] = []; for (const layerInfo of nonBaseLayers) { try { const layerData = await screenApi.getLayerLayout(screenId, layerInfo.layer_id); const condConfig = layerInfo.condition_config || layerData?.conditionConfig || {}; // ๋ ˆ์ด์–ด ์ปดํฌ๋„ŒํŠธ ๋ณ€ํ™˜ (V2 โ†’ Legacy) let layerComponents: any[] = []; const rawComponents = layerData?.components; if (rawComponents && Array.isArray(rawComponents) && rawComponents.length > 0) { const tempV2 = { version: "2.0" as const, components: rawComponents, gridSettings: layerData.gridSettings, screenResolution: layerData.screenResolution, }; if (isValidV2Layout(tempV2)) { const converted = convertV2ToLegacy(tempV2); if (converted) { layerComponents = converted.components || []; } } } // Zone ๊ธฐ๋ฐ˜ condition_config ์ฒ˜๋ฆฌ const zoneId = condConfig.zone_id; const conditionValue = condConfig.condition_value; const zone = zoneId ? loadedZones.find((z: any) => z.zone_id === zoneId) : null; // LayerDefinition ์ƒ์„ฑ const layerDef: LayerDefinition = { id: String(layerInfo.layer_id), name: layerInfo.layer_name || `๋ ˆ์ด์–ด ${layerInfo.layer_id}`, type: "conditional", zIndex: layerInfo.layer_id * 10, isVisible: false, isLocked: false, // Zone ๊ธฐ๋ฐ˜ ์กฐ๊ฑด (Zone์—์„œ ํŠธ๋ฆฌ๊ฑฐ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ด) condition: zone ? { targetComponentId: zone.trigger_component_id || "", operator: (zone.trigger_operator as "eq" | "neq" | "in") || "eq", value: conditionValue, } : condConfig.targetComponentId ? { targetComponentId: condConfig.targetComponentId, operator: condConfig.operator || "eq", value: condConfig.value, } : undefined, // Zone ๊ธฐ๋ฐ˜: displayRegion์€ Zone์—์„œ ๊ฐ€์ ธ์˜ด zoneId: zoneId || undefined, conditionValue: conditionValue || undefined, displayRegion: zone ? { x: zone.x, y: zone.y, width: zone.width, height: zone.height } : condConfig.displayRegion || undefined, components: layerComponents, }; layerDefinitions.push(layerDef); } catch (layerError) { console.warn(`๋ ˆ์ด์–ด ${layerInfo.layer_id} ๋กœ๋“œ ์‹คํŒจ:`, layerError); } } console.log("๐Ÿ”„ ์กฐ๊ฑด๋ถ€ ๋ ˆ์ด์–ด ๋กœ๋“œ ์™„๋ฃŒ:", layerDefinitions.length, "๊ฐœ", layerDefinitions.map(l => ({ id: l.id, name: l.name, zoneId: l.zoneId, conditionValue: l.conditionValue, componentCount: l.components.length, condition: l.condition ? { targetComponentId: l.condition.targetComponentId, operator: l.condition.operator, value: l.condition.value, } : "์—†์Œ", }))); console.log("๐Ÿ—บ๏ธ Zone ์ •๋ณด:", loadedZones.map(z => ({ zone_id: z.zone_id, trigger_component_id: z.trigger_component_id, trigger_operator: z.trigger_operator, }))); setConditionalLayers(layerDefinitions); } catch (error) { console.error("๋ ˆ์ด์–ด/Zone ๋กœ๋“œ ์‹คํŒจ:", error); } }; loadConditionalLayersAndZones(); }, [screenId, layout]); // ๐Ÿ†• ์กฐ๊ฑด๋ถ€ ๋ ˆ์ด์–ด ์กฐ๊ฑด ํ‰๊ฐ€ (formData ๋ณ€๊ฒฝ ์‹œ ๋™๊ธฐ์ ์œผ๋กœ ์ฆ‰์‹œ ๊ณ„์‚ฐ) const activeLayerIds = useMemo(() => { if (conditionalLayers.length === 0 || !layout) return [] as string[]; const allComponents = layout.components || []; const newActiveIds: string[] = []; conditionalLayers.forEach((layer) => { if (layer.condition) { const { targetComponentId, operator, value } = layer.condition; // ๋นˆ targetComponentId๋Š” ๋ฌด์‹œ if (!targetComponentId) return; // ํŠธ๋ฆฌ๊ฑฐ ์ปดํฌ๋„ŒํŠธ ์ฐพ๊ธฐ (๊ธฐ๋ณธ ๋ ˆ์ด์–ด์—์„œ) const targetComponent = allComponents.find((c) => c.id === targetComponentId); // columnName์œผ๋กœ formData์—์„œ ๊ฐ’ ์กฐํšŒ const fieldKey = (targetComponent as any)?.columnName || (targetComponent as any)?.componentConfig?.columnName || targetComponentId; const targetValue = formData[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; } // ๋””๋ฒ„๊ทธ ๋กœ๊น… (๊ฐ’์ด ์กด์žฌํ•  ๋•Œ๋งŒ) if (targetValue !== undefined && targetValue !== "") { console.log("๐Ÿ” [๋ ˆ์ด์–ด ์กฐ๊ฑด ํ‰๊ฐ€]", { layerId: layer.id, layerName: layer.name, targetComponentId, fieldKey, targetValue: String(targetValue), conditionValue: String(value), operator, isMatch, }); } if (isMatch) { newActiveIds.push(layer.id); } } }); return newActiveIds; }, [formData, conditionalLayers, layout]); // ๐Ÿ†• ๋ฉ”์ธ ํ…Œ์ด๋ธ” ๋ฐ์ดํ„ฐ ์ž๋™ ๋กœ๋“œ (๋‹จ์ผ ๋ ˆ์ฝ”๋“œ ํผ) // ํ™”๋ฉด์˜ ๋ฉ”์ธ ํ…Œ์ด๋ธ”์—์„œ ์‚ฌ์šฉ์ž ํšŒ์‚ฌ ์ฝ”๋“œ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜์—ฌ ํผ์— ์ž๋™ ์ฑ„์›€ useEffect(() => { const loadMainTableData = async () => { if (!screen || !layout || !layout.components || !companyCode) { return; } const mainTableName = screen.tableName; if (!mainTableName) { return; } // ํ…Œ์ด๋ธ” ์œ„์ ฏ์ด ์—†๋Š” ๊ฒฝ์šฐ์—๋งŒ ์ž๋™ ๋กœ๋“œ (ํ…Œ์ด๋ธ”์ด ์žˆ์œผ๋ฉด ํ–‰ ์„ ํƒ์œผ๋กœ ๋ฐ์ดํ„ฐ ๋กœ๋“œ) const hasTableWidget = layout.components.some( (comp: any) => comp.componentType === "table-list" || comp.componentType === "v2-table-list" || comp.widgetType === "table" ); if (hasTableWidget) { return; } // ์ธํ’‹ ์ปดํฌ๋„ŒํŠธ๋“ค ์ค‘ ๋ฉ”์ธ ํ…Œ์ด๋ธ”์˜ ์ปฌ๋Ÿผ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋“ค ์ฐพ๊ธฐ const inputComponents = layout.components.filter((comp: any) => { const compType = comp.componentType || comp.widgetType; const isInputType = compType?.includes("input") || compType?.includes("select") || compType?.includes("textarea") || compType?.includes("v2-input") || compType?.includes("v2-select") || compType?.includes("v2-media") || compType?.includes("file-upload"); // ๐Ÿ†• ๋ ˆ๊ฑฐ์‹œ ํŒŒ์ผ ์—…๋กœ๋“œ ํฌํ•จ const hasColumnName = !!(comp as any).columnName; return isInputType && hasColumnName; }); if (inputComponents.length === 0) { return; } // ๋ฉ”์ธ ํ…Œ์ด๋ธ”์—์„œ ํ˜„์žฌ ํšŒ์‚ฌ์˜ ๋ฐ์ดํ„ฐ ์กฐํšŒ try { const { tableTypeApi } = await import("@/lib/api/screen"); // company_code๋กœ ํ•„ํ„ฐ๋งํ•˜์—ฌ ๋‹จ์ผ ๋ ˆ์ฝ”๋“œ ์กฐํšŒ const result = await tableTypeApi.getTableRecord( mainTableName, "company_code", companyCode, "*" // ๋ชจ๋“  ์ปฌ๋Ÿผ ); if (result && result.record) { console.log("๐Ÿ“ฆ ๋ฉ”์ธ ํ…Œ์ด๋ธ” ๋ฐ์ดํ„ฐ ์ž๋™ ๋กœ๋“œ:", mainTableName, result.record); // ๊ฐ ์ธํ’‹ ์ปดํฌ๋„ŒํŠธ์— ํ•ด๋‹นํ•˜๋Š” ๋ฐ์ดํ„ฐ ์ฑ„์šฐ๊ธฐ const newFormData: Record = {}; inputComponents.forEach((comp: any) => { const columnName = comp.columnName; if (columnName && result.record[columnName] !== undefined) { newFormData[columnName] = result.record[columnName]; } }); if (Object.keys(newFormData).length > 0) { setFormData((prev) => ({ ...prev, ...newFormData, })); } } } catch (error) { console.log("๋ฉ”์ธ ํ…Œ์ด๋ธ” ์ž๋™ ๋กœ๋“œ ์‹คํŒจ (์ •์ƒ์ผ ์ˆ˜ ์žˆ์Œ):", error); // ์—๋Ÿฌ๋Š” ๋ฌด์‹œ - ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๊ฑฐ๋‚˜ ๊ถŒํ•œ์ด ์—†์„ ์ˆ˜ ์žˆ์Œ } }; loadMainTableData(); }, [screen, layout, companyCode]); // ๐Ÿ†• ๊ฐœ๋ณ„ autoFill ์ฒ˜๋ฆฌ (๋ฉ”์ธ ํ…Œ์ด๋ธ”๊ณผ ๋‹ค๋ฅธ ํ…Œ์ด๋ธ”์—์„œ ์กฐํšŒํ•˜๋Š” ๊ฒฝ์šฐ) useEffect(() => { const initAutoFill = async () => { if (!layout || !layout.components || !user) { return; } for (const comp of layout.components) { // type: "component" ๋˜๋Š” type: "widget" ๋ชจ๋‘ ์ฒ˜๋ฆฌ if (comp.type === "widget" || comp.type === "component") { const widget = comp as any; const fieldName = widget.columnName || widget.id; // autoFill ์ฒ˜๋ฆฌ (๋ช…์‹œ์ ์œผ๋กœ ์„ค์ •๋œ ๊ฒฝ์šฐ๋งŒ) if (widget.autoFill?.enabled || (comp as any).autoFill?.enabled) { const autoFillConfig = widget.autoFill || (comp as any).autoFill; const currentValue = formData[fieldName]; if (currentValue === undefined || currentValue === "") { const { sourceTable, filterColumn, userField, displayColumn } = autoFillConfig; // ์‚ฌ์šฉ์ž ์ •๋ณด์—์„œ ํ•„ํ„ฐ ๊ฐ’ ๊ฐ€์ ธ์˜ค๊ธฐ const userValue = user?.[userField as keyof typeof user]; if (userValue && sourceTable && filterColumn && displayColumn) { try { const { tableTypeApi } = await import("@/lib/api/screen"); const result = await tableTypeApi.getTableRecord(sourceTable, filterColumn, userValue, displayColumn); setFormData((prev) => ({ ...prev, [fieldName]: result.value, })); } catch (error) { console.error(`autoFill ์กฐํšŒ ์‹คํŒจ: ${fieldName}`, error); } } } } } } }; initAutoFill(); }, [layout, user]); // ๐Ÿ†• ์กฐ๊ฑด๋ถ€ ๋น„ํ™œ์„ฑํ™”/์ˆจ๊น€ ์‹œ ํ•ด๋‹น ํ•„๋“œ ๊ฐ’ ์ดˆ๊ธฐํ™” // ์กฐ๊ฑด ํ•„๋“œ๋“ค์˜ ๊ฐ’์„ ์ถ”์ ํ•˜์—ฌ ๋ณ€๊ฒฝ ์‹œ์—๋งŒ ์‹คํ–‰ const conditionalFieldValues = useMemo(() => { if (!layout?.components) return ""; // ์กฐ๊ฑด๋ถ€ ์„ค์ •์— ์‚ฌ์šฉ๋˜๋Š” ํ•„๋“œ๋“ค์˜ ํ˜„์žฌ ๊ฐ’์„ JSON ๋ฌธ์ž์—ด๋กœ ๋งŒ๋“ค์–ด ๋น„๊ต const conditionFields = new Set(); layout.components.forEach((component) => { const conditional = (component as any).conditional; if (conditional?.enabled && conditional.field) { conditionFields.add(conditional.field); } }); const values: Record = {}; conditionFields.forEach((field) => { values[field] = (formData as Record)[field]; }); return JSON.stringify(values); }, [layout?.components, formData]); useEffect(() => { if (!layout?.components) return; const fieldsToReset: string[] = []; layout.components.forEach((component) => { const conditional = (component as any).conditional; if (!conditional?.enabled) return; const conditionalResult = evaluateConditional(conditional, formData as Record, layout.components); // ์ˆจ๊น€ ๋˜๋Š” ๋น„ํ™œ์„ฑํ™” ์ƒํƒœ์ธ ๊ฒฝ์šฐ if (!conditionalResult.visible || conditionalResult.disabled) { const fieldName = (component as any).columnName || component.id; const currentValue = (formData as Record)[fieldName]; // ๊ฐ’์ด ์žˆ์œผ๋ฉด ์ดˆ๊ธฐํ™” ๋Œ€์ƒ์— ์ถ”๊ฐ€ if (currentValue !== undefined && currentValue !== "" && currentValue !== null) { fieldsToReset.push(fieldName); } } }); // ์ดˆ๊ธฐํ™”ํ•  ํ•„๋“œ๊ฐ€ ์žˆ์œผ๋ฉด ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌ if (fieldsToReset.length > 0) { setFormData((prev) => { const updated = { ...prev }; fieldsToReset.forEach((fieldName) => { updated[fieldName] = ""; }); return updated; }); } }, [conditionalFieldValues, layout?.components]); // ์บ”๋ฒ„์Šค ๋น„์œจ ์กฐ์ • (์‚ฌ์šฉ์ž ํ™”๋ฉด์— ๋งž๊ฒŒ ์ž๋™ ์Šค์ผ€์ผ) - ์ดˆ๊ธฐ ๋กœ๋”ฉ ์‹œ์—๋งŒ ๊ณ„์‚ฐ // ๋ธŒ๋ผ์šฐ์ € ๋ฐฐ์œจ ์กฐ์ • ์‹œ ๋ฉ”๋‰ด์™€ ํ™”๋ฉด์ด ํ•จ๊ป˜ ์ถ•์†Œ/ํ™•๋Œ€๋˜๋„๋ก resize ์ด๋ฒคํŠธ๋Š” ๊ฐ์ง€ํ•˜์ง€ ์•Š์Œ useEffect(() => { // ๋ชจ๋ฐ”์ผ ํ™˜๊ฒฝ์—์„œ๋Š” ์Šค์ผ€์ผ ์กฐ์ • ๋น„ํ™œ์„ฑํ™” (๋ฐ˜์‘ํ˜•๋งŒ ์ž‘๋™) if (isMobile) { setScale(1); setLayoutReady(true); // ๋ชจ๋ฐ”์ผ์—์„œ๋„ ๋ ˆ์ด์•„์›ƒ ์ค€๋น„ ์™„๋ฃŒ ํ‘œ์‹œ return; } const updateScale = () => { if (containerRef.current && layout) { const designWidth = layout?.screenResolution?.width || 1200; const designHeight = layout?.screenResolution?.height || 800; // ์ปจํ…Œ์ด๋„ˆ์˜ ์‹ค์ œ ํฌ๊ธฐ (ํ”„๋ฆฌ๋ทฐ ๋ชจ๋“œ์—์„œ๋Š” window ํฌ๊ธฐ ์‚ฌ์šฉ) let containerWidth: number; let containerHeight: number; if (isPreviewMode) { // iframe์—์„œ๋Š” window ํฌ๊ธฐ๋ฅผ ์ง์ ‘ ์‚ฌ์šฉ containerWidth = window.innerWidth; containerHeight = window.innerHeight; } else { containerWidth = containerRef.current.offsetWidth; containerHeight = containerRef.current.offsetHeight; } let newScale: number; if (isPreviewMode) { // ํ”„๋ฆฌ๋ทฐ ๋ชจ๋“œ: ๊ฐ€๋กœ/์„ธ๋กœ ๋ชจ๋‘ fitํ•˜๋„๋ก (์—ฌ๋ฐฑ ์—†์ด) const scaleX = containerWidth / designWidth; const scaleY = containerHeight / designHeight; newScale = Math.min(scaleX, scaleY, 1); // ์ตœ๋Œ€ 1๋ฐฐ์œจ } else { // ์ผ๋ฐ˜ ๋ชจ๋“œ: ๊ฐ€๋กœ ๊ธฐ์ค€ ์Šค์ผ€์ผ (์ขŒ์šฐ ์—ฌ๋ฐฑ 16px์”ฉ ๊ณ ์ •) const MARGIN_X = 32; const availableWidth = containerWidth - MARGIN_X; newScale = availableWidth / designWidth; } // console.log("๐Ÿ“ ์Šค์ผ€์ผ ๊ณ„์‚ฐ:", { // containerWidth, // containerHeight, // designWidth, // designHeight, // finalScale: newScale, // isPreviewMode, // }); setScale(newScale); // ์ปจํ…Œ์ด๋„ˆ ๋„ˆ๋น„ ์—…๋ฐ์ดํŠธ setContainerWidth(containerWidth); // ์Šค์ผ€์ผ ๊ณ„์‚ฐ ์™„๋ฃŒ ํ›„ ๋ ˆ์ด์•„์›ƒ ์ค€๋น„ ์™„๋ฃŒ ํ‘œ์‹œ setLayoutReady(true); } }; // ์ดˆ๊ธฐ ์ธก์ • (ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰) const timer = setTimeout(updateScale, 100); // resize ์ด๋ฒคํŠธ๋Š” ๊ฐ์ง€ํ•˜์ง€ ์•Š์Œ - ๋ธŒ๋ผ์šฐ์ € ๋ฐฐ์œจ ์กฐ์ • ์‹œ ๋ฉ”๋‰ด์™€ ํ™”๋ฉด์ด ํ•จ๊ป˜ ๋ณ€๊ฒฝ๋˜๋„๋ก return () => { clearTimeout(timer); }; }, [layout, isMobile, isPreviewMode]); if (loading) { return (

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

); } if (error || !screen) { return (
โš ๏ธ

ํ™”๋ฉด์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค

{error || "์š”์ฒญํ•˜์‹  ํ™”๋ฉด์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค."}

); } // ํ™”๋ฉด ํ•ด์ƒ๋„ ์ •๋ณด๊ฐ€ ์žˆ์œผ๋ฉด ํ•ด๋‹น ํฌ๊ธฐ๋กœ, ์—†์œผ๋ฉด ๊ธฐ๋ณธ ํฌ๊ธฐ ์‚ฌ์šฉ const screenWidth = layout?.screenResolution?.width || 1200; const screenHeight = layout?.screenResolution?.height || 800; return (
{/* ๋ ˆ์ด์•„์›ƒ ์ค€๋น„ ์ค‘ ๋กœ๋”ฉ ํ‘œ์‹œ */} {!layoutReady && (

ํ™”๋ฉด ์ค€๋น„ ์ค‘...

)} {/* ์ ˆ๋Œ€ ์œ„์น˜ ๊ธฐ๋ฐ˜ ๋ Œ๋”๋ง (ํ™”๋ฉด๊ด€๋ฆฌ์™€ ๋™์ผํ•œ ๋ฐฉ์‹) */} {layoutReady && layout && layout.components.length > 0 ? (
{/* ์ตœ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ๋“ค ๋ Œ๋”๋ง */} {(() => { // ๐Ÿ†• ํ”Œ๋กœ์šฐ ๋ฒ„ํŠผ ๊ทธ๋ฃน ๊ฐ์ง€ ๋ฐ ์ฒ˜๋ฆฌ const topLevelComponents = layout.components.filter((component) => !component.parentId); // ํ™”๋ฉด ๊ด€๋ฆฌ์—์„œ ์„ค์ •ํ•œ ํ•ด์ƒ๋„๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ widthOffset ๊ณ„์‚ฐ ๋ถˆํ•„์š” // ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋Š” ์›๋ณธ ์œ„์น˜ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉ const widthOffset = 0; const buttonGroups: Record = {}; const processedButtonIds = new Set(); // ๐Ÿ” ์ „์ฒด ๋ฒ„ํŠผ ๋ชฉ๋ก ํ™•์ธ const allButtons = topLevelComponents.filter((component) => { const isButton = (component.type === "component" && ["button-primary", "button-secondary"].includes((component as any).componentType)) || (component.type === "widget" && (component as any).widgetType === "button"); return isButton; }); topLevelComponents.forEach((component) => { const isButton = (component.type === "component" && ["button-primary", "button-secondary"].includes((component as any).componentType)) || (component.type === "widget" && (component as any).widgetType === "button"); if (isButton) { const flowConfig = (component as any).webTypeConfig?.flowVisibilityConfig as | FlowVisibilityConfig | undefined; // ๐Ÿ”ง ์ž„์‹œ: ๋ฒ„ํŠผ ๊ทธ๋ฃน ๊ธฐ๋Šฅ ์™„์ „ ๋น„ํ™œ์„ฑํ™” // TODO: ์‚ฌ์šฉ์ž๊ฐ€ ๋ช…์‹œ์ ์œผ๋กœ ๊ทธ๋ฃน์„ ์›ํ•˜๋Š” ๊ฒฝ์šฐ์—๋งŒ ํ™œ์„ฑํ™”ํ•˜๋„๋ก UI ๊ฐœ์„  ํ•„์š” const DISABLE_BUTTON_GROUPS = false; if ( !DISABLE_BUTTON_GROUPS && flowConfig?.enabled && flowConfig.layoutBehavior === "auto-compact" && flowConfig.groupId ) { if (!buttonGroups[flowConfig.groupId]) { buttonGroups[flowConfig.groupId] = []; } buttonGroups[flowConfig.groupId].push(component); processedButtonIds.add(component.id); } // else: ๋ชจ๋“  ๋ฒ„ํŠผ์„ ๊ฐœ๋ณ„ ๋ Œ๋”๋ง } }); const regularComponents = topLevelComponents.filter((c) => !processedButtonIds.has(c.id)); // TableSearchWidget๋“ค์„ ๋จผ์ € ์ฐพ๊ธฐ const tableSearchWidgets = regularComponents.filter( (c) => (c as any).componentId === "table-search-widget", ); // ์กฐ๊ฑด๋ถ€ ์ปจํ…Œ์ด๋„ˆ๋“ค์„ ์ฐพ๊ธฐ const conditionalContainers = regularComponents.filter( (c) => (c as any).componentId === "conditional-container" || (c as any).componentType === "conditional-container", ); // ๐Ÿ†• ๊ฐ™์€ X ์˜์—ญ(์„น์…˜)์—์„œ ์ปดํฌ๋„ŒํŠธ๋“ค์ด ๊ฒน์น˜์ง€ ์•Š๋„๋ก ์ž๋™ ์ˆ˜์ง ์ •๋ ฌ // โš ๏ธ V2 ๋ ˆ์ด์•„์›ƒ์—์„œ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ๋ฐฐ์น˜ํ•œ ์œ„์น˜๋ฅผ ์กด์ค‘ํ•˜๋ฏ€๋กœ ์ž๋™ ์ •๋ ฌ ๋น„ํ™œ์„ฑํ™” const autoLayoutComponents = regularComponents; // TableSearchWidget ๋ฐ ์กฐ๊ฑด๋ถ€ ์ปจํ…Œ์ด๋„ˆ ๋†’์ด ์ฐจ์ด๋ฅผ ๊ณ„์‚ฐํ•˜์—ฌ Y ์œ„์น˜ ์ถ”๊ฐ€ ์กฐ์ • const adjustedComponents = autoLayoutComponents.map((component) => { const isTableSearchWidget = (component as any).componentId === "table-search-widget"; const isConditionalContainer = (component as any).componentId === "conditional-container"; if (isTableSearchWidget || isConditionalContainer) { // ์ž๊ธฐ ์ž์‹ ์€ ์กฐ์ •ํ•˜์ง€ ์•Š์Œ return component; } let totalHeightAdjustment = 0; // TableSearchWidget ๋†’์ด ์กฐ์ • for (const widget of tableSearchWidgets) { const isBelow = component.position.y > widget.position.y; const heightDiff = getHeightDiff(screenId, widget.id); if (isBelow && heightDiff > 0) { totalHeightAdjustment += heightDiff; } } // ์กฐ๊ฑด๋ถ€ ์ปจํ…Œ์ด๋„ˆ ๋†’์ด ์กฐ์ • for (const container of conditionalContainers) { const isBelow = component.position.y > container.position.y; const actualHeight = conditionalContainerHeights[container.id]; const originalHeight = container.size?.height || 200; const heightDiff = actualHeight ? actualHeight - originalHeight : 0; if (isBelow && heightDiff > 0) { totalHeightAdjustment += heightDiff; } } // ๐Ÿ†• Zone ๊ธฐ๋ฐ˜ ๋†’์ด ์กฐ์ • // Zone ๋‹จ์œ„๋กœ ํ™œ์„ฑ ์—ฌ๋ถ€๋ฅผ ํŒ๋‹จํ•˜์—ฌ Y ์˜คํ”„์…‹ ๊ณ„์‚ฐ // Zone์€ ๊ฒน์น˜์ง€ ์•Š์œผ๋ฏ€๋กœ merge ๋กœ์ง์ด ๋ถˆํ•„์š” (๋‹จ์ˆœ boolean ํŒ๋‹จ) for (const zone of zones) { const zoneBottom = zone.y + zone.height; // ์ปดํฌ๋„ŒํŠธ๊ฐ€ Zone ํ•˜๋‹จ๋ณด๋‹ค ์•„๋ž˜์— ์žˆ๋Š” ๊ฒฝ์šฐ if (component.position.y >= zoneBottom) { // Zone์— ๋งค์นญ๋˜๋Š” ํ™œ์„ฑ ๋ ˆ์ด์–ด๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ const hasActiveLayer = conditionalLayers.some( l => l.zoneId === zone.zone_id && activeLayerIds.includes(l.id) ); if (!hasActiveLayer) { // Zone์— ํ™œ์„ฑ ๋ ˆ์ด์–ด ์—†์Œ: Zone ๋†’์ด๋งŒํผ ์œ„๋กœ ๋‹น๊น€ (๋นˆ ๊ณต๊ฐ„ ์ œ๊ฑฐ) totalHeightAdjustment -= zone.height; } } } if (totalHeightAdjustment !== 0) { return { ...component, position: { ...component.position, y: component.position.y + totalHeightAdjustment, }, }; } return component; }); return ( <> {/* ์ผ๋ฐ˜ ์ปดํฌ๋„ŒํŠธ๋“ค */} {adjustedComponents.map((component) => { // ์กฐ๊ฑด๋ถ€ ํ‘œ์‹œ ์„ค์ •์ด ์žˆ๋Š” ๊ฒฝ์šฐ์—๋งŒ ํ‰๊ฐ€ const conditional = (component as any).conditional; let conditionalDisabled = false; if (conditional?.enabled) { const conditionalResult = evaluateConditional( conditional, formData as Record, layout?.components || [], ); // ์กฐ๊ฑด์— ๋”ฐ๋ผ ์ˆจ๊น€ ์ฒ˜๋ฆฌ if (!conditionalResult.visible) { return null; } // ์กฐ๊ฑด์— ๋”ฐ๋ผ ๋น„ํ™œ์„ฑํ™” ์ฒ˜๋ฆฌ conditionalDisabled = conditionalResult.disabled; } // ํ™”๋ฉด ๊ด€๋ฆฌ ํ•ด์ƒ๋„๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ์œ„์น˜ ์กฐ์ • ๋ถˆํ•„์š” return ( {}} menuObjid={menuObjid} screenId={screenId} tableName={screen?.tableName} userId={user?.userId} userName={userName} companyCode={companyCode} menuObjid={menuObjid} selectedRowsData={selectedRowsData} sortBy={tableSortBy} sortOrder={tableSortOrder} columnOrder={tableColumnOrder} tableDisplayData={tableDisplayData} onSelectedRowsChange={( _, selectedData, sortBy, sortOrder, columnOrder, tableDisplayData, ) => { setSelectedRowsData(selectedData); setTableSortBy(sortBy); setTableSortOrder(sortOrder || "asc"); setTableColumnOrder(columnOrder); setTableDisplayData(tableDisplayData || []); }} flowSelectedData={flowSelectedData} flowSelectedStepId={flowSelectedStepId} onFlowSelectedDataChange={(selectedData: any[], stepId: number | null) => { setFlowSelectedData(selectedData); setFlowSelectedStepId(stepId); }} refreshKey={tableRefreshKey} onRefresh={() => { setTableRefreshKey((prev) => prev + 1); setSelectedRowsData([]); // ์„ ํƒ ํ•ด์ œ }} flowRefreshKey={flowRefreshKey} onFlowRefresh={() => { setFlowRefreshKey((prev) => prev + 1); setFlowSelectedData([]); // ์„ ํƒ ํ•ด์ œ setFlowSelectedStepId(null); }} formData={formData} onFormDataChange={(fieldName, value) => { setFormData((prev) => ({ ...prev, [fieldName]: value })); }} onHeightChange={(componentId, newHeight) => { setConditionalContainerHeights((prev) => ({ ...prev, [componentId]: newHeight, })); }} > {/* ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋“ค */} {(component.type === "group" || component.type === "container" || component.type === "area") && layout.components .filter((child) => child.parentId === component.id) .map((child) => { // ์ž์‹ ์ปดํฌ๋„ŒํŠธ์˜ ์œ„์น˜๋ฅผ ๋ถ€๋ชจ ๊ธฐ์ค€ ์ƒ๋Œ€ ์ขŒํ‘œ๋กœ ์กฐ์ • const relativeChildComponent = { ...child, position: { x: child.position.x - component.position.x, y: child.position.y - component.position.y, z: child.position.z || 1, }, }; return ( {}} menuObjid={menuObjid} screenId={screenId} tableName={screen?.tableName} userId={user?.userId} userName={userName} companyCode={companyCode} menuObjid={menuObjid} selectedRowsData={selectedRowsData} sortBy={tableSortBy} sortOrder={tableSortOrder} columnOrder={tableColumnOrder} tableDisplayData={tableDisplayData} onSelectedRowsChange={( _, selectedData, sortBy, sortOrder, columnOrder, tableDisplayData, ) => { setSelectedRowsData(selectedData); setTableSortBy(sortBy); setTableSortOrder(sortOrder || "asc"); setTableColumnOrder(columnOrder); setTableDisplayData(tableDisplayData || []); }} refreshKey={tableRefreshKey} onRefresh={() => { setTableRefreshKey((prev) => prev + 1); setSelectedRowsData([]); // ์„ ํƒ ํ•ด์ œ }} formData={formData} onFormDataChange={(fieldName, value) => { setFormData((prev) => ({ ...prev, [fieldName]: value })); }} /> ); })} ); })} {/* ๐Ÿ†• ํ”Œ๋กœ์šฐ ๋ฒ„ํŠผ ๊ทธ๋ฃน๋“ค */} {Object.entries(buttonGroups).map(([groupId, buttons]) => { if (buttons.length === 0) return null; const firstButton = buttons[0]; const groupConfig = (firstButton as any).webTypeConfig ?.flowVisibilityConfig as FlowVisibilityConfig; // ๐Ÿ” ๋ฒ„ํŠผ ๊ทธ๋ฃน ์„ค์ • ํ™•์ธ console.log("๐Ÿ” ๋ฒ„ํŠผ ๊ทธ๋ฃน ์„ค์ •:", { groupId, buttonCount: buttons.length, buttons: buttons.map((b) => ({ id: b.id, label: b.label, x: b.position.x, y: b.position.y, })), groupConfig: { layoutBehavior: groupConfig.layoutBehavior, groupDirection: groupConfig.groupDirection, groupAlign: groupConfig.groupAlign, groupGap: groupConfig.groupGap, }, }); // ๐Ÿ”ง ์ˆ˜์ •: ๊ทธ๋ฃน ์ปจํ…Œ์ด๋„ˆ๋Š” ์ฒซ ๋ฒˆ์งธ ๋ฒ„ํŠผ ์œ„์น˜๋ฅผ ๊ธฐ์ค€์œผ๋กœ ํ•˜๋˜, // ๊ฐ ๋ฒ„ํŠผ์˜ ์ƒ๋Œ€ ์œ„์น˜๋Š” ์›๋ž˜ ์œ„์น˜๋ฅผ ์œ ์ง€ const firstButtonPosition = { x: buttons[0].position.x, y: buttons[0].position.y, z: buttons[0].position.z || 2, }; // ๋ฒ„ํŠผ ๊ทธ๋ฃน ์œ„์น˜์—๋„ widthOffset ์ ์šฉ const adjustedGroupPosition = { ...firstButtonPosition, x: firstButtonPosition.x + widthOffset, }; // ๊ทธ๋ฃน์˜ ํฌ๊ธฐ ๊ณ„์‚ฐ: ๋ฒ„ํŠผ๋“ค์˜ ์‹ค์ œ ํฌ๊ธฐ + ๊ฐ„๊ฒฉ์„ ๊ธฐ์ค€์œผ๋กœ ๊ณ„์‚ฐ const direction = groupConfig.groupDirection || "horizontal"; const gap = groupConfig.groupGap ?? 8; let groupWidth = 0; let groupHeight = 0; if (direction === "horizontal") { groupWidth = buttons.reduce((total, button, index) => { const buttonWidth = button.size?.width || 100; const gapWidth = index < buttons.length - 1 ? gap : 0; return total + buttonWidth + gapWidth; }, 0); groupHeight = Math.max(...buttons.map((b) => b.size?.height || 40)); } else { groupWidth = Math.max(...buttons.map((b) => b.size?.width || 100)); groupHeight = buttons.reduce((total, button, index) => { const buttonHeight = button.size?.height || 40; const gapHeight = index < buttons.length - 1 ? gap : 0; return total + buttonHeight + gapHeight; }, 0); } return (
{ // ๐Ÿ”ง ๊ฐ ๋ฒ„ํŠผ์˜ ์ƒ๋Œ€ ์œ„์น˜ = ๋ฒ„ํŠผ์˜ ์›๋ž˜ ์œ„์น˜ - ์ฒซ ๋ฒˆ์งธ ๋ฒ„ํŠผ ์œ„์น˜ const relativeButton = { ...button, position: { x: button.position.x - firstButtonPosition.x, y: button.position.y - firstButtonPosition.y, z: button.position.z || 1, }, }; return (
{}} screenId={screenId} tableName={screen?.tableName} userId={user?.userId} userName={userName} companyCode={companyCode} tableDisplayData={tableDisplayData} selectedRowsData={selectedRowsData} sortBy={tableSortBy} sortOrder={tableSortOrder} columnOrder={tableColumnOrder} onSelectedRowsChange={(_, selectedData, sortBy, sortOrder, columnOrder) => { setSelectedRowsData(selectedData); setTableSortBy(sortBy); setTableSortOrder(sortOrder || "asc"); setTableColumnOrder(columnOrder); }} flowSelectedData={flowSelectedData} flowSelectedStepId={flowSelectedStepId} onFlowSelectedDataChange={(selectedData: any[], stepId: number | null) => { setFlowSelectedData(selectedData); setFlowSelectedStepId(stepId); }} refreshKey={tableRefreshKey} onRefresh={() => { setTableRefreshKey((prev) => prev + 1); setSelectedRowsData([]); }} flowRefreshKey={flowRefreshKey} onFlowRefresh={() => { setFlowRefreshKey((prev) => prev + 1); setFlowSelectedData([]); setFlowSelectedStepId(null); }} onFormDataChange={(fieldName, value) => { setFormData((prev) => ({ ...prev, [fieldName]: value })); }} />
); }} />
); })} {/* ๐Ÿ†• ์กฐ๊ฑด๋ถ€ ๋ ˆ์ด์–ด ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง (Zone ๊ธฐ๋ฐ˜) */} {conditionalLayers.map((layer) => { const isActive = activeLayerIds.includes(layer.id); if (!isActive || !layer.components || layer.components.length === 0) return null; // Zone ๊ธฐ๋ฐ˜: zoneId๋กœ Zone ์ฐพ์•„์„œ ์œ„์น˜/ํฌ๊ธฐ ๊ฒฐ์ • const zone = layer.zoneId ? zones.find(z => z.zone_id === layer.zoneId) : null; const region = zone ? { x: zone.x, y: zone.y, width: zone.width, height: zone.height } : layer.displayRegion; return (
{layer.components .filter((comp) => !comp.parentId) .map((comp) => ( {}} menuObjid={menuObjid} screenId={screenId} tableName={screen?.tableName} userId={user?.userId} userName={userName} companyCode={companyCode} selectedRowsData={selectedRowsData} sortBy={tableSortBy} sortOrder={tableSortOrder} columnOrder={tableColumnOrder} tableDisplayData={tableDisplayData} onSelectedRowsChange={( _, selectedData, sortBy, sortOrder, columnOrder, tableDisplayData, ) => { setSelectedRowsData(selectedData); setTableSortBy(sortBy); setTableSortOrder(sortOrder || "asc"); setTableColumnOrder(columnOrder); setTableDisplayData(tableDisplayData || []); }} refreshKey={tableRefreshKey} onRefresh={() => { setTableRefreshKey((prev) => prev + 1); setSelectedRowsData([]); }} formData={formData} onFormDataChange={(fieldName, value) => { setFormData((prev) => ({ ...prev, [fieldName]: value })); }} /> ))}
); })} ); })()}
) : ( // ๋นˆ ํ™”๋ฉด์ผ ๋•Œ
๐Ÿ“„

ํ™”๋ฉด์ด ๋น„์–ด์žˆ์Šต๋‹ˆ๋‹ค

์ด ํ™”๋ฉด์—๋Š” ์•„์ง ์„ค๊ณ„๋œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

)} {/* ํŽธ์ง‘ ๋ชจ๋‹ฌ */} { setEditModalOpen(false); setEditModalConfig({}); }} screenId={editModalConfig.screenId} modalSize={editModalConfig.modalSize} editData={editModalConfig.editData} onSave={editModalConfig.onSave} modalTitle={editModalConfig.modalTitle} modalDescription={editModalConfig.modalDescription} onDataChange={(changedFormData) => { console.log("๐Ÿ“ EditModal์—์„œ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์ˆ˜์‹ :", changedFormData); // ๋ณ€๊ฒฝ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฉ”์ธ ํผ์— ๋ฐ˜์˜ setFormData((prev) => { const updatedFormData = { ...prev, ...changedFormData, // ๋ณ€๊ฒฝ๋œ ํ•„๋“œ๋“ค๋งŒ ์—…๋ฐ์ดํŠธ }; console.log("๐Ÿ“Š ๋ฉ”์ธ ํผ ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ:", updatedFormData); return updatedFormData; }); }} /> {/* ์Šค์ผ€์ค„ ์ƒ์„ฑ ํ™•์ธ ๋‹ค์ด์–ผ๋กœ๊ทธ */} !open && closeDialog()} preview={previewResult} onConfirm={() => handleConfirm(true)} onCancel={closeDialog} isLoading={scheduleLoading} />
); } // ์‹ค์ œ ์ปดํฌ๋„ŒํŠธ๋ฅผ Provider๋กœ ๊ฐ์‹ธ๊ธฐ function ScreenViewPageWrapper() { return ( ); } export default ScreenViewPageWrapper;