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.
188 lines
6.2 KiB
TypeScript
188 lines
6.2 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
|
import Head from 'next/head';
|
|
import Link from 'next/link';
|
|
import { useRouter } from 'next/router';
|
|
import { TrendingUp, Database, BarChart3, Settings, AlertTriangle, Activity, Search, DollarSign } from 'lucide-react';
|
|
|
|
interface LayoutProps {
|
|
children: React.ReactNode;
|
|
title?: string;
|
|
}
|
|
|
|
const ClientLayout: React.FC<LayoutProps> = ({ children, title = 'Stock Oracle' }) => {
|
|
const router = useRouter();
|
|
const [currentTime, setCurrentTime] = useState<string>('');
|
|
const [mounted, setMounted] = useState(false);
|
|
|
|
useEffect(() => {
|
|
setMounted(true);
|
|
setCurrentTime(new Date().toLocaleString('ko-KR'));
|
|
|
|
const timer = setInterval(() => {
|
|
setCurrentTime(new Date().toLocaleString('ko-KR'));
|
|
}, 1000);
|
|
|
|
return () => clearInterval(timer);
|
|
}, []);
|
|
|
|
const navigation = [
|
|
{
|
|
name: '대시보드',
|
|
href: '/',
|
|
icon: TrendingUp,
|
|
current: router.pathname === '/',
|
|
},
|
|
{
|
|
name: '종목 조회',
|
|
href: '/stock',
|
|
icon: Search,
|
|
current: router.pathname === '/stock',
|
|
},
|
|
{
|
|
name: '데이터 조회',
|
|
href: '/query',
|
|
icon: BarChart3,
|
|
current: router.pathname === '/query',
|
|
},
|
|
{
|
|
name: 'FRED 경제데이터',
|
|
href: '/fred',
|
|
icon: DollarSign,
|
|
current: router.pathname === '/fred',
|
|
},
|
|
{
|
|
name: 'DB 상태',
|
|
href: '/database',
|
|
icon: Database,
|
|
current: router.pathname === '/database',
|
|
},
|
|
{
|
|
name: '시스템 로그',
|
|
href: '/logs',
|
|
icon: Activity,
|
|
current: router.pathname === '/logs',
|
|
},
|
|
{
|
|
name: '설정',
|
|
href: '/settings',
|
|
icon: Settings,
|
|
current: router.pathname === '/settings',
|
|
},
|
|
];
|
|
|
|
return (
|
|
<>
|
|
<Head>
|
|
<title>{title}</title>
|
|
<meta name="description" content="Stock Oracle - 주식 데이터 분석 플랫폼" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
|
|
{/* Favicon and Icons */}
|
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
|
<link rel="alternate icon" href="/favicon.ico" />
|
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
|
|
|
{/* Web App Manifest */}
|
|
<link rel="manifest" href="/manifest.json" />
|
|
|
|
{/* Theme Colors */}
|
|
<meta name="theme-color" content="#3b82f6" />
|
|
<meta name="msapplication-TileColor" content="#3b82f6" />
|
|
|
|
{/* Open Graph */}
|
|
<meta property="og:title" content="Stock Oracle" />
|
|
<meta property="og:description" content="Investment Data Analysis Platform using SEC filings" />
|
|
<meta property="og:type" content="website" />
|
|
<meta property="og:image" content="/apple-touch-icon.png" />
|
|
|
|
{/* Twitter Card */}
|
|
<meta name="twitter:card" content="summary" />
|
|
<meta name="twitter:title" content="Stock Oracle" />
|
|
<meta name="twitter:description" content="Investment Data Analysis Platform using SEC filings" />
|
|
<meta name="twitter:image" content="/apple-touch-icon.png" />
|
|
</Head>
|
|
|
|
<div className="min-h-screen bg-gray-50">
|
|
{/* 사이드바 */}
|
|
<div className="fixed inset-y-0 left-0 z-50 w-64 bg-white shadow-lg">
|
|
<div className="flex h-16 items-center justify-center border-b border-gray-200">
|
|
<div className="flex items-center space-x-2">
|
|
<TrendingUp className="h-8 w-8 text-blue-600" />
|
|
<h1 className="text-xl font-bold text-gray-900">Stock Oracle</h1>
|
|
</div>
|
|
</div>
|
|
|
|
<nav className="mt-6 px-3">
|
|
<div className="space-y-1">
|
|
{navigation.map((item) => {
|
|
const Icon = item.icon;
|
|
return (
|
|
<Link
|
|
key={item.name}
|
|
href={item.href}
|
|
className={`group flex items-center px-3 py-2 text-sm font-medium rounded-md transition-colors ${
|
|
item.current
|
|
? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700'
|
|
: 'text-gray-700 hover:bg-gray-50 hover:text-gray-900'
|
|
}`}
|
|
>
|
|
<Icon
|
|
className={`mr-3 h-5 w-5 ${
|
|
item.current ? 'text-blue-700' : 'text-gray-400 group-hover:text-gray-500'
|
|
}`}
|
|
/>
|
|
{item.name}
|
|
</Link>
|
|
);
|
|
})}
|
|
</div>
|
|
</nav>
|
|
|
|
{/* 하단 정보 */}
|
|
<div className="absolute bottom-0 w-full p-4 border-t border-gray-200">
|
|
<div className="text-xs text-gray-500">
|
|
<p>Stock Oracle v1.0.0</p>
|
|
<p>Real-time Financial Data</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 메인 콘텐츠 */}
|
|
<div className="pl-64">
|
|
{/* 헤더 */}
|
|
<header className="bg-white shadow-sm border-b border-gray-200">
|
|
<div className="px-6 py-4">
|
|
<div className="flex items-center justify-between">
|
|
<h2 className="text-2xl font-semibold text-gray-900">
|
|
{navigation.find(item => item.current)?.name || 'Stock Oracle'}
|
|
</h2>
|
|
|
|
<div className="flex items-center space-x-4">
|
|
{/* API 상태 표시 */}
|
|
<div className="flex items-center space-x-2">
|
|
<div className="h-2 w-2 bg-green-400 rounded-full"></div>
|
|
<span className="text-sm text-gray-600">API 연결됨</span>
|
|
</div>
|
|
|
|
{/* 현재 시간 */}
|
|
<div className="text-sm text-gray-500">
|
|
{mounted ? currentTime : '--:--:--'}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
{/* 페이지 콘텐츠 */}
|
|
<main className="p-6">
|
|
<div className="max-w-7xl mx-auto">
|
|
{children}
|
|
</div>
|
|
</main>
|
|
</div>
|
|
</div>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default ClientLayout; |