ERP-node/frontend/components/pop/management/PopScreenPreview.tsx

182 lines
6.7 KiB
TypeScript

"use client";
import { useState, useEffect } from "react";
import { cn } from "@/lib/utils";
import { Smartphone, Tablet, Loader2, ExternalLink, RefreshCw } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { ScreenDefinition } from "@/types/screen";
import { screenApi } from "@/lib/api/screen";
// ============================================================
// 타입 정의
// ============================================================
type DeviceType = "mobile" | "tablet";
interface PopScreenPreviewProps {
screen: ScreenDefinition | null;
className?: string;
}
// 디바이스 프레임 크기
// 모바일: 세로(portrait), 태블릿: 가로(landscape) 디폴트
const DEVICE_SIZES = {
mobile: { width: 375, height: 667 }, // iPhone SE 기준 (세로)
tablet: { width: 1024, height: 768 }, // iPad 기준 (가로)
};
// ============================================================
// 메인 컴포넌트
// ============================================================
export function PopScreenPreview({ screen, className }: PopScreenPreviewProps) {
const [deviceType, setDeviceType] = useState<DeviceType>("tablet");
const [loading, setLoading] = useState(false);
const [hasLayout, setHasLayout] = useState(false);
const [key, setKey] = useState(0); // iframe 새로고침용
// 레이아웃 존재 여부 확인
useEffect(() => {
if (!screen) {
setHasLayout(false);
return;
}
const checkLayout = async () => {
try {
setLoading(true);
const layout = await screenApi.getLayoutPop(screen.screenId);
// v2 레이아웃: sections는 객체 (Record<string, PopSectionDefinition>)
// v1 레이아웃: sections는 배열
if (layout) {
const isV2 = layout.version === "pop-2.0";
const hasSections = isV2
? layout.sections && Object.keys(layout.sections).length > 0
: layout.sections && Array.isArray(layout.sections) && layout.sections.length > 0;
setHasLayout(hasSections);
} else {
setHasLayout(false);
}
} catch {
setHasLayout(false);
} finally {
setLoading(false);
}
};
checkLayout();
}, [screen]);
// 미리보기 URL
const previewUrl = screen ? `/pop/screens/${screen.screenId}?preview=true&device=${deviceType}` : null;
// 새 탭에서 열기
const openInNewTab = () => {
if (previewUrl) {
const size = DEVICE_SIZES[deviceType];
window.open(previewUrl, "_blank", `width=${size.width + 40},height=${size.height + 80}`);
}
};
// iframe 새로고침
const refreshPreview = () => {
setKey((prev) => prev + 1);
};
const deviceSize = DEVICE_SIZES[deviceType];
// 미리보기 컨테이너에 맞게 스케일 조정
const scale = deviceType === "tablet" ? 0.5 : 0.6;
return (
<div className={cn("bg-muted/30 flex h-full flex-col", className)}>
{/* 헤더 */}
<div className="bg-background flex shrink-0 items-center justify-between border-b p-3">
<div className="flex items-center gap-2">
<h3 className="text-sm font-medium"></h3>
{screen && <span className="text-muted-foreground max-w-[150px] truncate text-xs">{screen.screenName}</span>}
</div>
<div className="flex items-center gap-2">
{/* 디바이스 선택 */}
<Tabs value={deviceType} onValueChange={(v) => setDeviceType(v as DeviceType)}>
<TabsList className="h-8">
<TabsTrigger value="mobile" className="h-7 gap-1.5 px-3" title="모바일 (375x667)">
<Smartphone className="h-3.5 w-3.5" />
<span className="text-xs"></span>
</TabsTrigger>
<TabsTrigger value="tablet" className="h-7 gap-1.5 px-3" title="태블릿 (1024x768 가로)">
<Tablet className="h-3.5 w-3.5" />
<span className="text-xs">릿</span>
</TabsTrigger>
</TabsList>
</Tabs>
{screen && hasLayout && (
<>
<Button variant="ghost" size="icon" className="h-7 w-7" onClick={refreshPreview}>
<RefreshCw className="h-3.5 w-3.5" />
</Button>
<Button variant="ghost" size="icon" className="h-7 w-7" onClick={openInNewTab}>
<ExternalLink className="h-3.5 w-3.5" />
</Button>
</>
)}
</div>
</div>
{/* 미리보기 영역 */}
<div className="flex flex-1 items-center justify-center overflow-auto p-4">
{!screen ? (
// 화면 미선택
<div className="text-muted-foreground text-center">
<div className="bg-muted mx-auto mb-3 flex h-16 w-16 items-center justify-center rounded-full">
{deviceType === "mobile" ? <Smartphone className="h-8 w-8" /> : <Tablet className="h-8 w-8" />}
</div>
<p className="text-sm"> .</p>
</div>
) : loading ? (
// 로딩 중
<div className="text-muted-foreground text-center">
<Loader2 className="mx-auto mb-3 h-8 w-8 animate-spin" />
<p className="text-sm"> ...</p>
</div>
) : !hasLayout ? (
// 레이아웃 없음
<div className="text-muted-foreground text-center">
<div className="bg-muted mx-auto mb-3 flex h-16 w-16 items-center justify-center rounded-full">
{deviceType === "mobile" ? <Smartphone className="h-8 w-8" /> : <Tablet className="h-8 w-8" />}
</div>
<p className="mb-2 text-sm">POP .</p>
<p className="text-muted-foreground text-xs"> .</p>
</div>
) : (
// 디바이스 프레임 + iframe (심플한 테두리)
<div
className="relative overflow-hidden rounded-lg border-2 border-gray-300 shadow-lg"
style={{
width: deviceSize.width * scale,
height: deviceSize.height * scale,
}}
>
<iframe
key={key}
src={previewUrl || ""}
className="h-full w-full border-0"
style={{
width: deviceSize.width,
height: deviceSize.height,
transform: `scale(${scale})`,
transformOrigin: "top left",
}}
title="POP Screen Preview"
/>
</div>
)}
</div>
</div>
);
}