314 lines
9.9 KiB
TypeScript
314 lines
9.9 KiB
TypeScript
"use client";
|
|
|
|
import React, { useState, useEffect, use } from "react";
|
|
import { DashboardViewer } from "@/components/dashboard/DashboardViewer";
|
|
import { DashboardElement } from "@/components/admin/dashboard/types";
|
|
|
|
interface DashboardViewPageProps {
|
|
params: Promise<{
|
|
dashboardId: string;
|
|
}>;
|
|
}
|
|
|
|
/**
|
|
* 대시보드 뷰어 페이지
|
|
* - 저장된 대시보드를 읽기 전용으로 표시
|
|
* - 실시간 데이터 업데이트
|
|
* - 전체화면 모드 지원
|
|
*/
|
|
export default function DashboardViewPage({ params }: DashboardViewPageProps) {
|
|
const resolvedParams = use(params);
|
|
const [dashboard, setDashboard] = useState<{
|
|
id: string;
|
|
title: string;
|
|
description?: string;
|
|
elements: DashboardElement[];
|
|
settings?: {
|
|
backgroundColor?: string;
|
|
resolution?: string;
|
|
};
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
} | null>(null);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const loadDashboard = React.useCallback(async () => {
|
|
setIsLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
// 실제 API 호출 시도
|
|
const { dashboardApi } = await import("@/lib/api/dashboard");
|
|
|
|
try {
|
|
const dashboardData = await dashboardApi.getDashboard(resolvedParams.dashboardId);
|
|
setDashboard({
|
|
...dashboardData,
|
|
elements: dashboardData.elements || [],
|
|
});
|
|
} catch (apiError) {
|
|
console.warn("API 호출 실패, 로컬 스토리지 확인:", apiError);
|
|
|
|
// API 실패 시 로컬 스토리지에서 찾기
|
|
const savedDashboards = JSON.parse(localStorage.getItem("savedDashboards") || "[]");
|
|
const savedDashboard = savedDashboards.find((d: { id: string }) => d.id === resolvedParams.dashboardId);
|
|
|
|
if (savedDashboard) {
|
|
setDashboard(savedDashboard);
|
|
} else {
|
|
// 로컬에도 없으면 샘플 데이터 사용
|
|
const sampleDashboard = generateSampleDashboard(resolvedParams.dashboardId);
|
|
setDashboard(sampleDashboard);
|
|
}
|
|
}
|
|
} catch (err) {
|
|
setError("대시보드를 불러오는 중 오류가 발생했습니다.");
|
|
console.error("Dashboard loading error:", err);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
}, [resolvedParams.dashboardId]);
|
|
|
|
// 대시보드 데이터 로딩
|
|
useEffect(() => {
|
|
loadDashboard();
|
|
}, [loadDashboard]);
|
|
|
|
// 로딩 상태
|
|
if (isLoading) {
|
|
return (
|
|
<div className="flex h-screen items-center justify-center bg-background">
|
|
<div className="text-center">
|
|
<div className="mx-auto mb-4 h-12 w-12 animate-spin rounded-full border-4 border-ring border-t-transparent" />
|
|
<div className="text-lg font-medium text-foreground">대시보드 로딩 중...</div>
|
|
<div className="mt-1 text-sm text-muted-foreground">잠시만 기다려주세요</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// 에러 상태
|
|
if (error || !dashboard) {
|
|
return (
|
|
<div className="flex h-screen items-center justify-center bg-background">
|
|
<div className="text-center">
|
|
<div className="mb-4 text-6xl">😞</div>
|
|
<div className="mb-2 text-xl font-medium text-foreground">{error || "대시보드를 찾을 수 없습니다"}</div>
|
|
<div className="mb-4 text-sm text-muted-foreground">대시보드 ID: {resolvedParams.dashboardId}</div>
|
|
<button onClick={loadDashboard} className="rounded-lg bg-primary px-4 py-2 text-primary-foreground hover:bg-primary/90">
|
|
다시 시도
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="h-screen">
|
|
{/* 대시보드 헤더 - 보기 모드에서는 숨김 */}
|
|
{/* <div className="border-b border-gray-200 bg-white px-6 py-4">
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h1 className="text-2xl font-bold text-gray-800">{dashboard.title}</h1>
|
|
{dashboard.description && <p className="mt-1 text-sm text-gray-600">{dashboard.description}</p>}
|
|
</div>
|
|
|
|
<div className="flex items-center gap-3">
|
|
{/* 새로고침 버튼 *\/}
|
|
<button
|
|
onClick={loadDashboard}
|
|
className="rounded-lg border border-gray-300 px-3 py-2 text-gray-600 hover:bg-gray-50 hover:text-gray-800"
|
|
title="새로고침"
|
|
>
|
|
🔄
|
|
</button>
|
|
|
|
{/* 전체화면 버튼 *\/}
|
|
<button
|
|
onClick={() => {
|
|
if (document.fullscreenElement) {
|
|
document.exitFullscreen();
|
|
} else {
|
|
document.documentElement.requestFullscreen();
|
|
}
|
|
}}
|
|
className="rounded-lg border border-gray-300 px-3 py-2 text-gray-600 hover:bg-gray-50 hover:text-gray-800"
|
|
title="전체화면"
|
|
>
|
|
⛶
|
|
</button>
|
|
|
|
{/* 편집 버튼 *\/}
|
|
<button
|
|
onClick={() => {
|
|
router.push(`/admin/screenMng/dashboardList?load=${resolvedParams.dashboardId}`);
|
|
}}
|
|
className="rounded-lg bg-blue-500 px-4 py-2 text-white hover:bg-blue-600"
|
|
>
|
|
편집
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 메타 정보 *\/}
|
|
<div className="mt-2 flex items-center gap-4 text-xs text-gray-500">
|
|
<span>생성: {new Date(dashboard.createdAt).toLocaleString()}</span>
|
|
<span>수정: {new Date(dashboard.updatedAt).toLocaleString()}</span>
|
|
<span>요소: {dashboard.elements.length}개</span>
|
|
</div>
|
|
</div> */}
|
|
|
|
{/* 대시보드 뷰어 */}
|
|
<DashboardViewer
|
|
elements={dashboard.elements}
|
|
dashboardId={dashboard.id}
|
|
dashboardTitle={dashboard.title}
|
|
backgroundColor={dashboard.settings?.backgroundColor}
|
|
resolution={dashboard.settings?.resolution}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 샘플 대시보드 생성 함수
|
|
*/
|
|
function generateSampleDashboard(dashboardId: string): {
|
|
id: string;
|
|
title: string;
|
|
description?: string;
|
|
elements: DashboardElement[];
|
|
settings?: {
|
|
backgroundColor?: string;
|
|
resolution?: string;
|
|
};
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
} {
|
|
const dashboards: Record<
|
|
string,
|
|
{
|
|
id: string;
|
|
title: string;
|
|
description?: string;
|
|
elements: DashboardElement[];
|
|
settings?: {
|
|
backgroundColor?: string;
|
|
resolution?: string;
|
|
};
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
> = {
|
|
"sales-overview": {
|
|
id: "sales-overview",
|
|
title: "📊 매출 현황 대시보드",
|
|
description: "월별 매출 추이 및 상품별 판매 현황을 한눈에 확인할 수 있습니다.",
|
|
elements: [
|
|
{
|
|
id: "chart-1",
|
|
type: "chart",
|
|
subtype: "bar",
|
|
position: { x: 20, y: 20 },
|
|
size: { width: 400, height: 300 },
|
|
title: "📊 월별 매출 추이",
|
|
content: "월별 매출 데이터",
|
|
dataSource: {
|
|
type: "database",
|
|
query: "SELECT month, sales FROM monthly_sales",
|
|
refreshInterval: 30000,
|
|
},
|
|
chartConfig: {
|
|
xAxis: "month",
|
|
yAxis: "sales",
|
|
title: "월별 매출 추이",
|
|
colors: ["#3B82F6", "#EF4444", "#10B981"],
|
|
},
|
|
},
|
|
{
|
|
id: "chart-2",
|
|
type: "chart",
|
|
subtype: "pie",
|
|
position: { x: 450, y: 20 },
|
|
size: { width: 350, height: 300 },
|
|
title: "🥧 상품별 판매 비율",
|
|
content: "상품별 판매 데이터",
|
|
dataSource: {
|
|
type: "database",
|
|
query: "SELECT product_name, total_sold FROM product_sales",
|
|
refreshInterval: 60000,
|
|
},
|
|
chartConfig: {
|
|
xAxis: "product_name",
|
|
yAxis: "total_sold",
|
|
title: "상품별 판매 비율",
|
|
colors: ["#8B5CF6", "#EC4899", "#06B6D4", "#84CC16"],
|
|
},
|
|
},
|
|
{
|
|
id: "chart-3",
|
|
type: "chart",
|
|
subtype: "line",
|
|
position: { x: 20, y: 350 },
|
|
size: { width: 780, height: 250 },
|
|
title: "📈 사용자 가입 추이",
|
|
content: "사용자 가입 데이터",
|
|
dataSource: {
|
|
type: "database",
|
|
query: "SELECT week, new_users FROM user_growth",
|
|
refreshInterval: 300000,
|
|
},
|
|
chartConfig: {
|
|
xAxis: "week",
|
|
yAxis: "new_users",
|
|
title: "주간 신규 사용자 가입 추이",
|
|
colors: ["#10B981"],
|
|
},
|
|
},
|
|
],
|
|
createdAt: "2024-09-30T10:00:00Z",
|
|
updatedAt: "2024-09-30T14:30:00Z",
|
|
},
|
|
"user-analytics": {
|
|
id: "user-analytics",
|
|
title: "👥 사용자 분석 대시보드",
|
|
description: "사용자 행동 패턴 및 가입 추이 분석",
|
|
elements: [
|
|
{
|
|
id: "chart-4",
|
|
type: "chart",
|
|
subtype: "line",
|
|
position: { x: 20, y: 20 },
|
|
size: { width: 500, height: 300 },
|
|
title: "📈 일일 활성 사용자",
|
|
content: "사용자 활동 데이터",
|
|
dataSource: {
|
|
type: "database",
|
|
query: "SELECT date, active_users FROM daily_active_users",
|
|
refreshInterval: 60000,
|
|
},
|
|
chartConfig: {
|
|
xAxis: "date",
|
|
yAxis: "active_users",
|
|
title: "일일 활성 사용자 추이",
|
|
},
|
|
},
|
|
],
|
|
createdAt: "2024-09-29T15:00:00Z",
|
|
updatedAt: "2024-09-30T09:15:00Z",
|
|
},
|
|
};
|
|
|
|
return (
|
|
dashboards[dashboardId] || {
|
|
id: dashboardId,
|
|
title: `대시보드 ${dashboardId}`,
|
|
description: "샘플 대시보드입니다.",
|
|
elements: [],
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString(),
|
|
}
|
|
);
|
|
}
|