ERP-node/frontend/lib/registry/components/conditional-container/ConditionalSectionViewer.tsx

200 lines
7.0 KiB
TypeScript
Raw Normal View History

"use client";
import React, { useState, useEffect } from "react";
import { ConditionalSectionViewerProps } from "./types";
2025-11-17 10:09:02 +09:00
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";
2025-11-20 15:30:00 +09:00
import { useAuth } from "@/hooks/useAuth";
/**
*
*
*/
export function ConditionalSectionViewer({
sectionId,
condition,
label,
screenId,
screenName,
isActive,
isDesignMode,
showBorder = true,
formData,
onFormDataChange,
groupedData, // 🆕 그룹 데이터
onSave, // 🆕 EditModal의 handleSave 콜백
2025-11-28 14:56:11 +09:00
controlField, // 🆕 조건부 컨테이너의 제어 필드명
selectedCondition, // 🆕 현재 선택된 조건 값
}: ConditionalSectionViewerProps) {
2025-11-20 15:30:00 +09:00
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);
2025-11-17 10:09:02 +09:00
const [screenResolution, setScreenResolution] = useState<{ width: number; height: number } | null>(null);
2025-11-28 14:56:11 +09:00
// 🆕 조건 값을 포함한 formData 생성
const enhancedFormData = React.useMemo(() => {
const base = formData || {};
// 조건부 컨테이너의 현재 선택 값을 formData에 포함
if (controlField && selectedCondition) {
return {
...base,
[controlField]: selectedCondition,
__conditionalContainerValue: selectedCondition,
__conditionalContainerLabel: label,
__conditionalContainerControlField: controlField, // 🆕 제어 필드명도 포함
};
}
return base;
}, [formData, controlField, selectedCondition, label]);
// 화면 로드
useEffect(() => {
if (!screenId) {
setComponents([]);
setScreenInfo(null);
2025-11-17 10:09:02 +09:00
setScreenResolution(null);
return;
}
const loadScreen = async () => {
setIsLoading(true);
try {
2025-11-17 10:09:02 +09:00
const [layout, screen] = await Promise.all([screenApi.getLayout(screenId), screenApi.getScreen(screenId)]);
setComponents(layout.components || []);
setScreenInfo({
id: screenId,
tableName: screen.tableName,
});
2025-11-17 10:09:02 +09:00
setScreenResolution(layout.screenResolution || null);
} catch (error) {
console.error("화면 로드 실패:", error);
setComponents([]);
setScreenInfo(null);
2025-11-17 10:09:02 +09:00
setScreenResolution(null);
} finally {
setIsLoading(false);
}
};
loadScreen();
}, [screenId]);
// 디자인 모드가 아니고 비활성 섹션이면 렌더링하지 않음
if (!isDesignMode && !isActive) {
return null;
}
return (
<div
className={cn(
2025-11-17 10:09:02 +09:00
"relative w-full transition-all",
isDesignMode && showBorder && "border-muted-foreground/30 bg-muted/20 rounded-lg border-2 border-dashed",
!isDesignMode && !isActive && "hidden",
)}
2025-11-17 10:09:02 +09:00
style={{
minHeight: isDesignMode ? "200px" : undefined,
}}
data-section-id={sectionId}
>
{/* 섹션 라벨 (디자인 모드에서만 표시) */}
{isDesignMode && (
2025-11-17 10:09:02 +09:00
<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">
2025-11-17 10:09:02 +09:00
<div className="text-muted-foreground text-center">
<p className="text-sm"> </p>
2025-11-17 10:09:02 +09:00
<p className="mt-1 text-xs">: {condition}</p>
</div>
</div>
)}
{/* 로딩 중 */}
{isLoading && (
2025-11-17 10:09:02 +09:00
<div className="bg-background/50 absolute inset-0 z-20 flex items-center justify-center">
<div className="flex flex-col items-center gap-2">
2025-11-17 10:09:02 +09:00
<Loader2 className="text-primary h-6 w-6 animate-spin" />
<p className="text-muted-foreground text-xs"> ...</p>
</div>
</div>
)}
{/* 화면 렌더링 */}
{screenId && components.length > 0 && (
2025-11-17 10:09:02 +09:00
<>
{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
2025-11-28 14:56:11 +09:00
component={component}
isInteractive={true}
2025-11-28 14:56:11 +09:00
screenId={screenInfo?.id}
tableName={screenInfo?.tableName}
userId={userId}
userName={userName}
companyCode={user?.companyCode}
formData={enhancedFormData}
onFormDataChange={onFormDataChange}
groupedData={groupedData}
onSave={onSave}
2025-11-28 14:56:11 +09:00
/>
</div>
);
})}
2025-11-17 10:09:02 +09:00
</div>
</div>
)}
</>
)}
</div>
);
}