"use client"; import React, { useEffect, useState } from "react"; import { useParams, useSearchParams } from "next/navigation"; import { Button } from "@/components/ui/button"; import { Loader2, ArrowLeft, Smartphone, Tablet, RotateCcw, RotateCw } from "lucide-react"; import { screenApi } from "@/lib/api/screen"; import { ScreenDefinition, LayoutData } from "@/types/screen"; import { useRouter } from "next/navigation"; import { toast } from "sonner"; import { initializeComponents } from "@/lib/registry/components"; import { DynamicComponentRenderer } from "@/lib/registry/DynamicComponentRenderer"; import { ScreenPreviewProvider } from "@/contexts/ScreenPreviewContext"; import { useAuth } from "@/hooks/useAuth"; import { TableOptionsProvider } from "@/contexts/TableOptionsContext"; import { TableSearchWidgetHeightProvider } from "@/contexts/TableSearchWidgetHeightContext"; import { ScreenContextProvider } from "@/contexts/ScreenContext"; import { SplitPanelProvider } from "@/lib/registry/components/split-panel-layout/SplitPanelContext"; import { ActiveTabProvider } from "@/contexts/ActiveTabContext"; import { ScreenMultiLangProvider } from "@/contexts/ScreenMultiLangContext"; import { PopLayoutDataV3, PopLayoutModeKey, ensureV3Layout, isV3Layout, } from "@/components/pop/designer/types/pop-layout"; import { PopLayoutRenderer, hasBaseLayout, getEffectiveModeLayout, } from "@/components/pop/designer/renderers"; import { useResponsiveMode, useResponsiveModeWithOverride, type DeviceType, } from "@/hooks/useDeviceOrientation"; // 디바이스별 크기 (프리뷰 모드용) const DEVICE_SIZES: Record> = { mobile: { landscape: { width: 667, height: 375, label: "모바일 가로" }, portrait: { width: 375, height: 667, label: "모바일 세로" }, }, tablet: { landscape: { width: 1024, height: 768, label: "태블릿 가로" }, portrait: { width: 768, height: 1024, label: "태블릿 세로" }, }, }; // ======================================== // 헬퍼 함수 // ======================================== const getModeKey = (device: DeviceType, isLandscape: boolean): PopLayoutModeKey => { if (device === "tablet") { return isLandscape ? "tablet_landscape" : "tablet_portrait"; } return isLandscape ? "mobile_landscape" : "mobile_portrait"; }; // v3.0 레이아웃인지 확인 const isPopLayoutV3 = (layout: any): layout is PopLayoutDataV3 => { return layout && layout.version === "pop-3.0" && layout.layouts && layout.components; }; // v1/v2 레이아웃인지 확인 (마이그레이션 대상) const isPopLayout = (layout: any): boolean => { return layout && ( layout.version === "pop-1.0" || layout.version === "pop-2.0" || layout.version === "pop-3.0" ); }; // ======================================== // 메인 컴포넌트 // ======================================== function PopScreenViewPage() { const params = useParams(); const searchParams = useSearchParams(); const router = useRouter(); const screenId = parseInt(params.screenId as string); const isPreviewMode = searchParams.get("preview") === "true"; // 반응형 모드 감지 (화면 크기에 따라 tablet/mobile, landscape/portrait 자동 전환) // 프리뷰 모드에서는 수동 전환 가능 const { mode, setDevice, setOrientation, isAutoDetect } = useResponsiveModeWithOverride( isPreviewMode ? "tablet" : undefined, isPreviewMode ? true : undefined ); // 현재 모드 정보 const deviceType = mode.device; const isLandscape = mode.isLandscape; const currentModeKey = mode.modeKey; const { user, userName, companyCode } = useAuth(); const [screen, setScreen] = useState(null); const [layout, setLayout] = useState(null); const [popLayoutV3, setPopLayoutV3] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [formData, setFormData] = useState>({}); const [selectedRowsData, setSelectedRowsData] = useState([]); const [tableRefreshKey, setTableRefreshKey] = useState(0); // 컴포넌트 초기화 useEffect(() => { const initComponents = async () => { try { await initializeComponents(); } catch (error) { console.error("POP 화면 컴포넌트 초기화 실패:", error); } }; initComponents(); }, []); // 화면 및 POP 레이아웃 로드 useEffect(() => { const loadScreen = async () => { try { setLoading(true); setError(null); const screenData = await screenApi.getScreen(screenId); setScreen(screenData); try { const popLayout = await screenApi.getLayoutPop(screenId); if (popLayout && isPopLayout(popLayout)) { // v1/v2/v3 → v3로 변환 const v3Layout = ensureV3Layout(popLayout); setPopLayoutV3(v3Layout); const componentCount = Object.keys(v3Layout.components).length; console.log(`[POP] v3 레이아웃 로드됨: ${componentCount}개 컴포넌트`); if (!isV3Layout(popLayout)) { console.log("[POP] v1/v2 → v3 자동 마이그레이션 완료"); } } else if (popLayout && popLayout.components && Array.isArray(popLayout.components) && popLayout.components.length > 0) { // 이전 형식 (레거시 components 구조) console.log("[POP] 레거시 레이아웃 로드:", popLayout.components.length, "개 컴포넌트"); setLayout(popLayout as LayoutData); } else { console.log("[POP] 레이아웃 없음"); setPopLayoutV3(null); setLayout(null); } } catch (layoutError) { console.warn("[POP] 레이아웃 로드 실패:", layoutError); setPopLayoutV3(null); setLayout(null); } } catch (error) { console.error("[POP] 화면 로드 실패:", error); setError("화면을 불러오는데 실패했습니다."); toast.error("화면을 불러오는데 실패했습니다."); } finally { setLoading(false); } }; if (screenId) { loadScreen(); } }, [screenId]); const currentDevice = DEVICE_SIZES[deviceType][isLandscape ? "landscape" : "portrait"]; if (loading) { return (

POP 화면 로딩 중...

); } if (error || !screen) { return (
!

화면을 찾을 수 없습니다

{error || "요청하신 POP 화면이 존재하지 않습니다."}

); } return (
{/* 상단 툴바 (프리뷰 모드에서만) */} {isPreviewMode && (
{screen.screenName} ({currentModeKey.replace("_", " ")})
{/* 자동 감지 모드 버튼 */}
)} {/* POP 화면 컨텐츠 */}
{/* 현재 모드 표시 (일반 모드) */} {!isPreviewMode && (
{currentModeKey.replace("_", " ")}
)}
{/* POP 레이아웃 v3.0 렌더링 */} {popLayoutV3 ? ( ) : layout && layout.components && layout.components.length > 0 ? ( // 레거시 형식 (components 구조) - 호환성 유지
{layout.components .filter((component) => !component.parentId) .map((component) => (
{ }} screenId={screenId} tableName={screen?.tableName} userId={user?.userId} userName={userName} companyCode={companyCode} selectedRowsData={selectedRowsData} onSelectedRowsChange={(_, selectedData) => { setSelectedRowsData(selectedData); }} refreshKey={tableRefreshKey} onRefresh={() => { setTableRefreshKey((prev) => prev + 1); setSelectedRowsData([]); }} onFormDataChange={(fieldName, value) => { setFormData((prev) => ({ ...prev, [fieldName]: value })); }} />
))}
) : ( // 빈 화면

화면이 비어있습니다

POP 화면 디자이너에서 컴포넌트를 추가하여 화면을 구성하세요.

)}
); } // ======================================== // POP 레이아웃 v3.0 렌더러 // ======================================== interface PopLayoutV3RendererProps { layout: PopLayoutDataV3; modeKey: PopLayoutModeKey; } function PopLayoutV3Renderer({ layout, modeKey }: PopLayoutV3RendererProps) { // 태블릿 가로 모드가 기준으로 설정되어 있는지 확인 if (!hasBaseLayout(layout)) { return (
!

화면이 설정되지 않았습니다

POP 화면 디자이너에서 태블릿 가로 모드 레이아웃을 먼저 설정해주세요.

); } // 현재 모드에 맞는 레이아웃 가져오기 const { modeLayout, isConverted, sourceModeKey } = getEffectiveModeLayout(layout, modeKey); return (
{isConverted && (
{sourceModeKey} 기준 자동 변환됨
)}
); } // Provider 래퍼 export default function PopScreenViewPageWrapper() { return ( ); }