211 lines
6.9 KiB
TypeScript
211 lines
6.9 KiB
TypeScript
"use client";
|
|
|
|
import React, { useState, useEffect } from "react";
|
|
import { TabsComponent, TabItem, ScreenDefinition } from "@/types";
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|
import { Card } from "@/components/ui/card";
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { Loader2, FileQuestion } from "lucide-react";
|
|
import { screenApi } from "@/lib/api/screen";
|
|
import { InteractiveScreenViewerDynamic } from "@/components/screen/InteractiveScreenViewerDynamic";
|
|
|
|
interface TabsWidgetProps {
|
|
component: TabsComponent;
|
|
isPreview?: boolean;
|
|
}
|
|
|
|
/**
|
|
* 탭 위젯 컴포넌트
|
|
* 각 탭에 다른 화면을 표시할 수 있습니다
|
|
*/
|
|
export const TabsWidget: React.FC<TabsWidgetProps> = ({ component, isPreview = false }) => {
|
|
// componentConfig에서 설정 읽기 (새 컴포넌트 시스템)
|
|
const config = (component as any).componentConfig || component;
|
|
const { tabs = [], defaultTab, orientation = "horizontal", variant = "default" } = config;
|
|
|
|
// console.log("🔍 TabsWidget 렌더링:", {
|
|
// component,
|
|
// componentConfig: (component as any).componentConfig,
|
|
// tabs,
|
|
// tabsLength: tabs.length
|
|
// });
|
|
|
|
const [activeTab, setActiveTab] = useState<string>(defaultTab || tabs[0]?.id || "");
|
|
const [loadedScreens, setLoadedScreens] = useState<Record<string, any>>({});
|
|
const [loadingScreens, setLoadingScreens] = useState<Record<string, boolean>>({});
|
|
const [screenErrors, setScreenErrors] = useState<Record<string, string>>({});
|
|
|
|
// 탭 변경 시 화면 로드
|
|
useEffect(() => {
|
|
if (!activeTab) return;
|
|
|
|
const currentTab = tabs.find((tab) => tab.id === activeTab);
|
|
if (!currentTab || !currentTab.screenId) return;
|
|
|
|
// 이미 로드된 화면이면 스킵
|
|
if (loadedScreens[activeTab]) return;
|
|
|
|
// 이미 로딩 중이면 스킵
|
|
if (loadingScreens[activeTab]) return;
|
|
|
|
// 화면 로드 시작
|
|
loadScreen(activeTab, currentTab.screenId);
|
|
}, [activeTab, tabs]);
|
|
|
|
const loadScreen = async (tabId: string, screenId: number) => {
|
|
setLoadingScreens((prev) => ({ ...prev, [tabId]: true }));
|
|
setScreenErrors((prev) => ({ ...prev, [tabId]: "" }));
|
|
|
|
try {
|
|
const layoutData = await screenApi.getLayout(screenId);
|
|
|
|
if (layoutData) {
|
|
setLoadedScreens((prev) => ({
|
|
...prev,
|
|
[tabId]: {
|
|
screenId,
|
|
layout: layoutData,
|
|
},
|
|
}));
|
|
} else {
|
|
setScreenErrors((prev) => ({
|
|
...prev,
|
|
[tabId]: "화면을 불러올 수 없습니다",
|
|
}));
|
|
}
|
|
} catch (error: any) {
|
|
setScreenErrors((prev) => ({
|
|
...prev,
|
|
[tabId]: error.message || "화면 로드 중 오류가 발생했습니다",
|
|
}));
|
|
} finally {
|
|
setLoadingScreens((prev) => ({ ...prev, [tabId]: false }));
|
|
}
|
|
};
|
|
|
|
// 탭 콘텐츠 렌더링
|
|
const renderTabContent = (tab: TabItem) => {
|
|
const isLoading = loadingScreens[tab.id];
|
|
const error = screenErrors[tab.id];
|
|
const screenData = loadedScreens[tab.id];
|
|
|
|
// 로딩 중
|
|
if (isLoading) {
|
|
return (
|
|
<div className="flex h-full flex-col items-center justify-center space-y-4">
|
|
<Loader2 className="h-8 w-8 animate-spin text-primary" />
|
|
<p className="text-muted-foreground text-sm">화면을 불러오는 중...</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// 에러 발생
|
|
if (error) {
|
|
return (
|
|
<div className="flex h-full flex-col items-center justify-center space-y-4">
|
|
<FileQuestion className="h-12 w-12 text-destructive" />
|
|
<div className="text-center">
|
|
<p className="mb-2 font-medium text-destructive">화면 로드 실패</p>
|
|
<p className="text-muted-foreground text-sm">{error}</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// 화면 ID가 없는 경우
|
|
if (!tab.screenId) {
|
|
return (
|
|
<div className="flex h-full flex-col items-center justify-center space-y-4">
|
|
<FileQuestion className="text-muted-foreground h-12 w-12" />
|
|
<div className="text-center">
|
|
<p className="text-muted-foreground mb-2 text-sm">화면이 할당되지 않았습니다</p>
|
|
<p className="text-xs text-gray-400">상세설정에서 화면을 선택하세요</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// 화면 렌더링 - 원본 화면의 모든 컴포넌트를 그대로 렌더링
|
|
if (screenData && screenData.layout && screenData.layout.components) {
|
|
const components = screenData.layout.components;
|
|
const screenResolution = screenData.layout.screenResolution || { width: 1920, height: 1080 };
|
|
|
|
return (
|
|
<div className="bg-white" style={{ width: `${screenResolution.width}px`, height: '100%' }}>
|
|
<div className="relative h-full">
|
|
{components.map((comp) => (
|
|
<InteractiveScreenViewerDynamic
|
|
key={comp.id}
|
|
component={comp}
|
|
allComponents={components}
|
|
screenInfo={{ id: tab.screenId }}
|
|
/>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="flex h-full flex-col items-center justify-center space-y-4">
|
|
<FileQuestion className="text-muted-foreground h-12 w-12" />
|
|
<div className="text-center">
|
|
<p className="text-muted-foreground text-sm">화면 데이터를 불러올 수 없습니다</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
// 빈 탭 목록
|
|
if (tabs.length === 0) {
|
|
return (
|
|
<Card className="flex h-full w-full items-center justify-center">
|
|
<div className="text-center">
|
|
<p className="text-muted-foreground text-sm">탭이 없습니다</p>
|
|
<p className="text-xs text-gray-400">상세설정에서 탭을 추가하세요</p>
|
|
</div>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="h-full w-full overflow-auto">
|
|
<Tabs
|
|
value={activeTab}
|
|
onValueChange={setActiveTab}
|
|
orientation={orientation}
|
|
className="flex h-full w-full flex-col"
|
|
>
|
|
<TabsList className={orientation === "horizontal" ? "justify-start shrink-0" : "flex-col shrink-0"}>
|
|
{tabs.map((tab) => (
|
|
<TabsTrigger
|
|
key={tab.id}
|
|
value={tab.id}
|
|
disabled={tab.disabled}
|
|
className={orientation === "horizontal" ? "" : "w-full justify-start"}
|
|
>
|
|
<span>{tab.label}</span>
|
|
{tab.screenName && (
|
|
<Badge variant="secondary" className="ml-2 text-[10px]">
|
|
{tab.screenName}
|
|
</Badge>
|
|
)}
|
|
</TabsTrigger>
|
|
))}
|
|
</TabsList>
|
|
|
|
{tabs.map((tab) => (
|
|
<TabsContent
|
|
key={tab.id}
|
|
value={tab.id}
|
|
className="flex-1 mt-0 data-[state=inactive]:hidden"
|
|
>
|
|
{renderTabContent(tab)}
|
|
</TabsContent>
|
|
))}
|
|
</Tabs>
|
|
</div>
|
|
);
|
|
};
|
|
|