178 lines
6.2 KiB
TypeScript
178 lines
6.2 KiB
TypeScript
"use client";
|
|
|
|
import React, { useState, useEffect } from "react";
|
|
import { ConditionalSectionViewerProps } from "./types";
|
|
import { RealtimePreview } from "@/components/screen/RealtimePreviewDynamic";
|
|
import { DynamicComponentRenderer } from "@/lib/registry/DynamicComponentRenderer";
|
|
import { cn } from "@/lib/utils";
|
|
import { Loader2 } from "lucide-react";
|
|
import { screenApi } from "@/lib/api/screen";
|
|
import { ComponentData } from "@/types/screen";
|
|
import { useAuth } from "@/hooks/useAuth";
|
|
|
|
/**
|
|
* 조건부 섹션 뷰어 컴포넌트
|
|
* 각 조건에 해당하는 화면을 표시
|
|
*/
|
|
export function ConditionalSectionViewer({
|
|
sectionId,
|
|
condition,
|
|
label,
|
|
screenId,
|
|
screenName,
|
|
isActive,
|
|
isDesignMode,
|
|
showBorder = true,
|
|
formData,
|
|
onFormDataChange,
|
|
groupedData, // 🆕 그룹 데이터
|
|
}: ConditionalSectionViewerProps) {
|
|
const { userId, userName, user } = useAuth();
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [components, setComponents] = useState<ComponentData[]>([]);
|
|
const [screenInfo, setScreenInfo] = useState<{ id: number; tableName?: string } | null>(null);
|
|
const [screenResolution, setScreenResolution] = useState<{ width: number; height: number } | null>(null);
|
|
|
|
// 화면 로드
|
|
useEffect(() => {
|
|
if (!screenId) {
|
|
setComponents([]);
|
|
setScreenInfo(null);
|
|
setScreenResolution(null);
|
|
return;
|
|
}
|
|
|
|
const loadScreen = async () => {
|
|
setIsLoading(true);
|
|
try {
|
|
const [layout, screen] = await Promise.all([screenApi.getLayout(screenId), screenApi.getScreen(screenId)]);
|
|
|
|
setComponents(layout.components || []);
|
|
setScreenInfo({
|
|
id: screenId,
|
|
tableName: screen.tableName,
|
|
});
|
|
setScreenResolution(layout.screenResolution || null);
|
|
} catch (error) {
|
|
console.error("화면 로드 실패:", error);
|
|
setComponents([]);
|
|
setScreenInfo(null);
|
|
setScreenResolution(null);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
loadScreen();
|
|
}, [screenId]);
|
|
|
|
// 디자인 모드가 아니고 비활성 섹션이면 렌더링하지 않음
|
|
if (!isDesignMode && !isActive) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
"relative w-full transition-all",
|
|
isDesignMode && showBorder && "border-muted-foreground/30 bg-muted/20 rounded-lg border-2 border-dashed",
|
|
!isDesignMode && !isActive && "hidden",
|
|
)}
|
|
style={{
|
|
minHeight: isDesignMode ? "200px" : undefined,
|
|
}}
|
|
data-section-id={sectionId}
|
|
>
|
|
{/* 섹션 라벨 (디자인 모드에서만 표시) */}
|
|
{isDesignMode && (
|
|
<div className="bg-background text-muted-foreground absolute -top-3 left-4 z-10 px-2 text-xs font-medium">
|
|
{label} {isActive && "(활성)"}
|
|
{screenId && ` - 화면 ID: ${screenId}`}
|
|
</div>
|
|
)}
|
|
|
|
{/* 화면 미선택 안내 (디자인 모드 + 화면 없을 때) */}
|
|
{isDesignMode && !screenId && (
|
|
<div className="absolute inset-0 flex items-center justify-center">
|
|
<div className="text-muted-foreground text-center">
|
|
<p className="text-sm">설정 패널에서 화면을 선택하세요</p>
|
|
<p className="mt-1 text-xs">조건: {condition}</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* 로딩 중 */}
|
|
{isLoading && (
|
|
<div className="bg-background/50 absolute inset-0 z-20 flex items-center justify-center">
|
|
<div className="flex flex-col items-center gap-2">
|
|
<Loader2 className="text-primary h-6 w-6 animate-spin" />
|
|
<p className="text-muted-foreground text-xs">화면 로드 중...</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* 화면 렌더링 */}
|
|
{screenId && components.length > 0 && (
|
|
<>
|
|
{isDesignMode ? (
|
|
/* 디자인 모드: 화면 정보만 표시 */
|
|
<div className="absolute inset-0 flex items-center justify-center">
|
|
<div className="text-center">
|
|
<p className="text-foreground mb-2 text-sm font-medium">{screenName || `화면 ID: ${screenId}`}</p>
|
|
<p className="text-muted-foreground text-xs">
|
|
{screenResolution?.width} x {screenResolution?.height}
|
|
</p>
|
|
<p className="text-muted-foreground mt-1 text-xs">컴포넌트 {components.length}개</p>
|
|
</div>
|
|
</div>
|
|
) : (
|
|
/* 실행 모드: 실제 화면 렌더링 */
|
|
<div className="w-full">
|
|
{/* 화면 크기만큼의 절대 위치 캔버스 */}
|
|
<div
|
|
className="relative mx-auto"
|
|
style={{
|
|
width: screenResolution?.width ? `${screenResolution.width}px` : "100%",
|
|
height: screenResolution?.height ? `${screenResolution.height}px` : "auto",
|
|
minHeight: "200px",
|
|
}}
|
|
>
|
|
{components.map((component) => {
|
|
const { position = { x: 0, y: 0, z: 1 }, size = { width: 200, height: 40 } } = component;
|
|
|
|
return (
|
|
<div
|
|
key={component.id}
|
|
className="absolute"
|
|
style={{
|
|
left: position.x || 0,
|
|
top: position.y || 0,
|
|
width: size.width || 200,
|
|
height: size.height || 40,
|
|
zIndex: position.z || 1,
|
|
}}
|
|
>
|
|
<DynamicComponentRenderer
|
|
component={component}
|
|
isInteractive={true}
|
|
screenId={screenInfo?.id}
|
|
tableName={screenInfo?.tableName}
|
|
userId={userId}
|
|
userName={userName}
|
|
companyCode={user?.companyCode}
|
|
formData={formData}
|
|
onFormDataChange={onFormDataChange}
|
|
groupedData={groupedData}
|
|
/>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|