ERP-node/frontend/app/(main)/dashboard/[dashboardId]/page.tsx

287 lines
9.1 KiB
TypeScript

'use client';
import React, { useState, useEffect } from 'react';
import { DashboardViewer } from '@/components/dashboard/DashboardViewer';
import { DashboardElement } from '@/components/admin/dashboard/types';
interface DashboardViewPageProps {
params: {
dashboardId: string;
};
}
/**
* 대시보드 뷰어 페이지
* - 저장된 대시보드를 읽기 전용으로 표시
* - 실시간 데이터 업데이트
* - 전체화면 모드 지원
*/
export default function DashboardViewPage({ params }: DashboardViewPageProps) {
const [dashboard, setDashboard] = useState<{
id: string;
title: string;
description?: string;
elements: DashboardElement[];
createdAt: string;
updatedAt: string;
} | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
// 대시보드 데이터 로딩
useEffect(() => {
loadDashboard();
}, [params.dashboardId]);
const loadDashboard = async () => {
setIsLoading(true);
setError(null);
try {
// 실제 API 호출 시도
const { dashboardApi } = await import('@/lib/api/dashboard');
try {
const dashboardData = await dashboardApi.getDashboard(params.dashboardId);
setDashboard(dashboardData);
} catch (apiError) {
console.warn('API 호출 실패, 로컬 스토리지 확인:', apiError);
// API 실패 시 로컬 스토리지에서 찾기
const savedDashboards = JSON.parse(localStorage.getItem('savedDashboards') || '[]');
const savedDashboard = savedDashboards.find((d: any) => d.id === params.dashboardId);
if (savedDashboard) {
setDashboard(savedDashboard);
} else {
// 로컬에도 없으면 샘플 데이터 사용
const sampleDashboard = generateSampleDashboard(params.dashboardId);
setDashboard(sampleDashboard);
}
}
} catch (err) {
setError('대시보드를 불러오는 중 오류가 발생했습니다.');
console.error('Dashboard loading error:', err);
} finally {
setIsLoading(false);
}
};
// 로딩 상태
if (isLoading) {
return (
<div className="h-screen flex items-center justify-center bg-gray-50">
<div className="text-center">
<div className="w-12 h-12 border-4 border-blue-500 border-t-transparent rounded-full animate-spin mx-auto mb-4" />
<div className="text-lg font-medium text-gray-700"> ...</div>
<div className="text-sm text-gray-500 mt-1"> </div>
</div>
</div>
);
}
// 에러 상태
if (error || !dashboard) {
return (
<div className="h-screen flex items-center justify-center bg-gray-50">
<div className="text-center">
<div className="text-6xl mb-4">😞</div>
<div className="text-xl font-medium text-gray-700 mb-2">
{error || '대시보드를 찾을 수 없습니다'}
</div>
<div className="text-sm text-gray-500 mb-4">
ID: {params.dashboardId}
</div>
<button
onClick={loadDashboard}
className="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600"
>
</button>
</div>
</div>
);
}
return (
<div className="h-screen bg-gray-50">
{/* 대시보드 헤더 */}
<div className="bg-white border-b border-gray-200 px-6 py-4">
<div className="flex justify-between items-center">
<div>
<h1 className="text-2xl font-bold text-gray-800">{dashboard.title}</h1>
{dashboard.description && (
<p className="text-sm text-gray-600 mt-1">{dashboard.description}</p>
)}
</div>
<div className="flex items-center gap-3">
{/* 새로고침 버튼 */}
<button
onClick={loadDashboard}
className="px-3 py-2 text-gray-600 hover:text-gray-800 border border-gray-300 rounded-lg hover:bg-gray-50"
title="새로고침"
>
🔄
</button>
{/* 전체화면 버튼 */}
<button
onClick={() => {
if (document.fullscreenElement) {
document.exitFullscreen();
} else {
document.documentElement.requestFullscreen();
}
}}
className="px-3 py-2 text-gray-600 hover:text-gray-800 border border-gray-300 rounded-lg hover:bg-gray-50"
title="전체화면"
>
</button>
{/* 편집 버튼 */}
<button
onClick={() => {
window.open(`/admin/dashboard?load=${params.dashboardId}`, '_blank');
}}
className="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600"
>
</button>
</div>
</div>
{/* 메타 정보 */}
<div className="flex items-center gap-4 mt-2 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>
{/* 대시보드 뷰어 */}
<div className="h-[calc(100vh-120px)]">
<DashboardViewer
elements={dashboard.elements}
dashboardId={dashboard.id}
/>
</div>
</div>
);
}
/**
* 샘플 대시보드 생성 함수
*/
function generateSampleDashboard(dashboardId: string) {
const dashboards: Record<string, any> = {
'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()
};
}