"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 { 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"; // ํ™œ์„ฑ ํƒญ ๊ด€๋ฆฌ function ScreenViewPage() { 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 [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); // ๋ ˆ์ด์•„์›ƒ ๋กœ๋“œ try { 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]); // ๐Ÿ†• 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]); // ์บ”๋ฒ„์Šค ๋น„์œจ ์กฐ์ • (์‚ฌ์šฉ์ž ํ™”๋ฉด์— ๋งž๊ฒŒ ์ž๋™ ์Šค์ผ€์ผ) - ์ดˆ๊ธฐ ๋กœ๋”ฉ ์‹œ์—๋งŒ ๊ณ„์‚ฐ // ๋ธŒ๋ผ์šฐ์ € ๋ฐฐ์œจ ์กฐ์ • ์‹œ ๋ฉ”๋‰ด์™€ ํ™”๋ฉด์ด ํ•จ๊ป˜ ์ถ•์†Œ/ํ™•๋Œ€๋˜๋„๋ก 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", ); // TableSearchWidget ๋ฐ ์กฐ๊ฑด๋ถ€ ์ปจํ…Œ์ด๋„ˆ ๋†’์ด ์ฐจ์ด๋ฅผ ๊ณ„์‚ฐํ•˜์—ฌ Y ์œ„์น˜ ์กฐ์ • const adjustedComponents = regularComponents.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; console.log(`๐Ÿ” ๋†’์ด ์กฐ์ • ์ฒดํฌ:`, { componentId: component.id, componentY: component.position.y, containerY: container.position.y, isBelow, actualHeight, originalHeight, heightDiff, containerId: container.id, containerSize: container.size, }); if (isBelow && heightDiff > 0) { totalHeightAdjustment += heightDiff; console.log( `๐Ÿ“ ์ปดํฌ๋„ŒํŠธ ${component.id} ์œ„์น˜ ์กฐ์ •: ${heightDiff}px (์กฐ๊ฑด๋ถ€ ์ปจํ…Œ์ด๋„ˆ ${container.id})`, ); } } if (totalHeightAdjustment > 0) { return { ...component, position: { ...component.position, y: component.position.y + totalHeightAdjustment, }, }; } return component; }); return ( <> {/* ์ผ๋ฐ˜ ์ปดํฌ๋„ŒํŠธ๋“ค */} {adjustedComponents.map((component) => { // ํ™”๋ฉด ๊ด€๋ฆฌ ํ•ด์ƒ๋„๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ์œ„์น˜ ์กฐ์ • ๋ถˆํ•„์š” 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 })); }} />
); }} />
); })} ); })()}
) : ( // ๋นˆ ํ™”๋ฉด์ผ ๋•Œ
๐Ÿ“„

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

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

)} {/* ํŽธ์ง‘ ๋ชจ๋‹ฌ */} { 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; }); }} />
); } // ์‹ค์ œ ์ปดํฌ๋„ŒํŠธ๋ฅผ Provider๋กœ ๊ฐ์‹ธ๊ธฐ function ScreenViewPageWrapper() { return ( ); } export default ScreenViewPageWrapper;