You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

284 lines
12 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import dynamic from 'next/dynamic';
import Layout from '@/components/Layout';
import { TrendingUp, Database, BarChart3, Users, Calendar, DollarSign } from 'lucide-react';
import { stockApi, DatabaseStats, formatNumber } from '@/lib/api';
const Dashboard: React.FC = () => {
const [stats, setStats] = useState<DatabaseStats | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchStats = async () => {
try {
const data = await stockApi.getDatabaseStats();
setStats(data);
} catch (error) {
console.error('Failed to fetch database stats:', error);
} finally {
setLoading(false);
}
};
fetchStats();
}, []);
const quickStats = [
{
name: '등록된 회사',
value: stats?.companies.total || 0,
icon: Users,
color: 'text-blue-600',
bgColor: 'bg-blue-50',
description: '데이터베이스에 등록된 총 회사 수',
},
{
name: '재무 데이터',
value: stats?.financial_data.total_records || 0,
icon: BarChart3,
color: 'text-green-600',
bgColor: 'bg-green-50',
description: '저장된 재무 데이터 레코드 수',
},
{
name: '주가 데이터',
value: stats?.price_data.total_records || 0,
icon: TrendingUp,
color: 'text-purple-600',
bgColor: 'bg-purple-50',
description: '저장된 주가 데이터 포인트 수',
},
{
name: '계산된 지표',
value: stats?.calculated_metrics.total_records || 0,
icon: Calendar,
color: 'text-orange-600',
bgColor: 'bg-orange-50',
description: '계산된 재무 지표 수',
},
];
return (
<Layout title="Stock Oracle - 대시보드">
<div className="space-y-6">
{/* 환영 섹션 */}
<div className="bg-gradient-to-r from-blue-600 to-purple-600 rounded-lg shadow-sm p-8 text-white">
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold mb-2">Stock Oracle </h1>
<p className="text-blue-100 text-lg">
</p>
<p className="text-blue-200 mt-2">
SEC .
</p>
</div>
<div className="hidden md:block">
<TrendingUp className="h-24 w-24 text-blue-200" />
</div>
</div>
</div>
{/* 빠른 통계 */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{quickStats.map((stat) => {
const Icon = stat.icon;
return (
<div key={stat.name} className="bg-white rounded-lg shadow-sm border border-gray-200 p-6 hover:shadow-md transition-shadow">
<div className="flex items-center">
<div className={`flex-shrink-0 p-3 rounded-lg ${stat.bgColor}`}>
<Icon className={`h-6 w-6 ${stat.color}`} />
</div>
<div className="ml-4 flex-1">
<p className="text-sm font-medium text-gray-500">{stat.name}</p>
<div className="text-2xl font-bold text-gray-900">
{loading ? (
<span className="inline-block h-6 w-16 bg-gray-200 rounded animate-pulse"></span>
) : (
<span>{formatNumber(stat.value)}</span>
)}
</div>
</div>
</div>
<p className="text-xs text-gray-600 mt-2">{stat.description}</p>
</div>
);
})}
</div>
{/* 데이터 품질 개요 */}
{stats && (
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* 재무 데이터 품질 */}
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4 flex items-center">
<BarChart3 className="h-5 w-5 mr-2 text-green-600" />
</h3>
<div className="space-y-4">
<div>
<div className="flex justify-between items-center mb-2">
<span className="text-sm text-gray-600"> </span>
<span className="text-sm font-medium text-green-600">
{((stats.financial_data.real_data / stats.financial_data.total_records) * 100).toFixed(1)}%
</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-3">
<div
className="bg-green-500 h-3 rounded-full transition-all duration-500"
style={{ width: `${(stats.financial_data.real_data / stats.financial_data.total_records) * 100}%` }}
></div>
</div>
</div>
<div className="grid grid-cols-2 gap-4 mt-4">
<div className="text-center p-3 bg-green-50 rounded-lg">
<p className="text-2xl font-bold text-green-600">{formatNumber(stats.financial_data.real_data)}</p>
<p className="text-sm text-green-700"> </p>
</div>
<div className="text-center p-3 bg-yellow-50 rounded-lg">
<p className="text-2xl font-bold text-yellow-600">{formatNumber(stats.financial_data.estimated_data)}</p>
<p className="text-sm text-yellow-700"> </p>
</div>
</div>
</div>
</div>
{/* 커버리지 정보 */}
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4 flex items-center">
<Database className="h-5 w-5 mr-2 text-blue-600" />
</h3>
<div className="space-y-4">
<div className="flex justify-between items-center">
<span className="text-sm text-gray-600"> </span>
<span className="text-lg font-semibold text-gray-900">
{stats.companies.with_financial_data} / {stats.companies.total}
</span>
</div>
<div className="flex justify-between items-center">
<span className="text-sm text-gray-600"> </span>
<span className="text-lg font-semibold text-gray-900">
{stats.companies.with_price_data} / {stats.companies.total}
</span>
</div>
<div className="mt-4">
<p className="text-sm text-gray-600 mb-2"> </p>
<div className="flex flex-wrap gap-1">
{stats.price_data.tickers.slice(0, 8).map((ticker) => (
<span key={ticker} className="inline-flex px-2 py-1 text-xs font-medium bg-blue-100 text-blue-800 rounded">
{ticker}
</span>
))}
{stats.price_data.tickers.length > 8 && (
<span className="inline-flex px-2 py-1 text-xs font-medium bg-gray-100 text-gray-600 rounded">
+{stats.price_data.tickers.length - 8}
</span>
)}
</div>
</div>
</div>
</div>
</div>
)}
{/* 빠른 액션 */}
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4"> </h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<a
href="/stock"
className="flex items-center p-4 border border-gray-200 rounded-lg hover:border-orange-300 hover:bg-orange-50 transition-colors group"
>
<TrendingUp className="h-8 w-8 text-orange-600 group-hover:text-orange-700" />
<div className="ml-3">
<p className="text-sm font-medium text-gray-900"> </p>
<p className="text-xs text-gray-500"> </p>
</div>
</a>
<a
href="/query"
className="flex items-center p-4 border border-gray-200 rounded-lg hover:border-blue-300 hover:bg-blue-50 transition-colors group"
>
<BarChart3 className="h-8 w-8 text-blue-600 group-hover:text-blue-700" />
<div className="ml-3">
<p className="text-sm font-medium text-gray-900"> </p>
<p className="text-xs text-gray-500"> </p>
</div>
</a>
<a
href="/database"
className="flex items-center p-4 border border-gray-200 rounded-lg hover:border-green-300 hover:bg-green-50 transition-colors group"
>
<Database className="h-8 w-8 text-green-600 group-hover:text-green-700" />
<div className="ml-3">
<p className="text-sm font-medium text-gray-900">DB </p>
<p className="text-xs text-gray-500"> </p>
</div>
</a>
<a
href="/fred"
className="flex items-center p-4 border border-gray-200 rounded-lg hover:border-purple-300 hover:bg-purple-50 transition-colors group"
>
<DollarSign className="h-8 w-8 text-purple-600 group-hover:text-purple-700" />
<div className="ml-3">
<p className="text-sm font-medium text-gray-900">FRED </p>
<p className="text-xs text-gray-500"> </p>
</div>
</a>
</div>
</div>
{/* 시스템 상태 */}
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4"> </h3>
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<div className="flex items-center justify-between p-3 bg-green-50 rounded-lg">
<div className="flex items-center">
<div className="h-3 w-3 bg-green-400 rounded-full mr-2"></div>
<span className="text-sm font-medium text-green-800">API </span>
</div>
<span className="text-xs text-green-600"></span>
</div>
<div className="flex items-center justify-between p-3 bg-green-50 rounded-lg">
<div className="flex items-center">
<div className="h-3 w-3 bg-green-400 rounded-full mr-2"></div>
<span className="text-sm font-medium text-green-800"></span>
</div>
<span className="text-xs text-green-600"></span>
</div>
<div className="flex items-center justify-between p-3 bg-green-50 rounded-lg">
<div className="flex items-center">
<div className="h-3 w-3 bg-green-400 rounded-full mr-2"></div>
<span className="text-sm font-medium text-green-800"> API</span>
</div>
<span className="text-xs text-green-600"></span>
</div>
<div className="flex items-center justify-between p-3 bg-blue-50 rounded-lg">
<div className="flex items-center">
<div className="h-3 w-3 bg-blue-400 rounded-full mr-2"></div>
<span className="text-sm font-medium text-blue-800">FRED API</span>
</div>
<span className="text-xs text-blue-600"></span>
</div>
</div>
</div>
</div>
</Layout>
);
};
export default Dashboard;