goldenChat base source add
This commit is contained in:
@@ -0,0 +1,523 @@
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
ThemeProvider,
|
||||
CssBaseline,
|
||||
Box,
|
||||
AppBar,
|
||||
Toolbar,
|
||||
Typography,
|
||||
Button,
|
||||
Container,
|
||||
IconButton,
|
||||
Tooltip,
|
||||
Chip,
|
||||
Menu,
|
||||
MenuItem,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
Divider,
|
||||
Badge,
|
||||
} from '@mui/material';
|
||||
import {
|
||||
Dashboard as DashboardIcon,
|
||||
ShowChart,
|
||||
CloudDownload,
|
||||
Schedule,
|
||||
Settings as SettingsIcon,
|
||||
Analytics,
|
||||
LightMode,
|
||||
DarkMode,
|
||||
Login as LoginIcon,
|
||||
Logout as LogoutIcon,
|
||||
AccountCircle,
|
||||
Person,
|
||||
Rule as RuleIcon,
|
||||
AccountBalance as SimulationIcon,
|
||||
TrendingUp as TrendingUpIcon,
|
||||
Fullscreen,
|
||||
FullscreenExit,
|
||||
Notifications as NotificationsIcon,
|
||||
ListAlt as OrderbookIcon,
|
||||
FormatListBulleted as AlertListIcon,
|
||||
AutoAwesome as AutoTradingIcon,
|
||||
GridView as MultiChartIcon,
|
||||
ClearAll as ClearAllIcon,
|
||||
CompareArrows as CompareIcon,
|
||||
} from '@mui/icons-material';
|
||||
|
||||
import { useAppTheme } from './contexts/ThemeContext';
|
||||
import { useIndicatorPopup } from './contexts/IndicatorPopupContext';
|
||||
import { useAuth } from './contexts/AuthContext';
|
||||
import { useAlertNotification } from './contexts/AlertNotificationContext';
|
||||
import { useNavigation } from './contexts/NavigationContext';
|
||||
import { useIsMobile } from './hooks/useIsMobile';
|
||||
import { MobileAppShell } from './components/MobileAppShell';
|
||||
import DashboardPage from './pages/DashboardPage';
|
||||
import PatternSearchPage from './pages/PatternSearchPage';
|
||||
import RisingStocksPage from './pages/RisingStocksPage';
|
||||
import StrategyTreeBuilderPage from './pages/StrategyTreeBuilderPage';
|
||||
import SimulationPage from './pages/SimulationPage';
|
||||
import DataCollectionPage from './pages/DataCollectionPage';
|
||||
import SchedulerPage from './pages/SchedulerPage';
|
||||
import SettingsPage from './pages/SettingsPage';
|
||||
import CandlestickTestPage from './pages/CandlestickTestPage';
|
||||
import CandlestickTestPage3 from './pages/CandlestickTestPage3';
|
||||
import AlertPage from './pages/AlertPage';
|
||||
import NotificationListPage from './pages/NotificationListPage';
|
||||
import OrderbookPage from './pages/OrderbookPage';
|
||||
import AutoTradingPage from './pages/AutoTradingPage';
|
||||
import MultiChartPage from './pages/MultiChartPage';
|
||||
import MultiChartPage2 from './pages/MultiChartPage2';
|
||||
import AlertListPage from './pages/AlertListPage';
|
||||
import ChartValidationPage from './pages/admin/ChartValidationPage';
|
||||
import LoginDialog from './components/LoginDialog';
|
||||
import SignUpDialog from './components/SignUpDialog';
|
||||
import IndicatorConfigPanel from './components/IndicatorConfigPanel';
|
||||
import IndicatorPopupWindow from './components/IndicatorPopupWindow';
|
||||
import AlertSnackbar from './components/alert/AlertSnackbar';
|
||||
|
||||
interface MenuItemType {
|
||||
shortText: string;
|
||||
fullText: string;
|
||||
icon: React.ReactNode;
|
||||
page: string;
|
||||
}
|
||||
|
||||
const menuItems: MenuItemType[] = [
|
||||
{ shortText: '대시보드', fullText: '대시보드', icon: <DashboardIcon />, page: 'dashboard' },
|
||||
{ shortText: '멀티차트', fullText: '멀티차트', icon: <MultiChartIcon />, page: 'multi-chart' },
|
||||
{ shortText: '멀티2', fullText: '멀티차트2', icon: <MultiChartIcon />, page: 'multi-chart-2' },
|
||||
{ shortText: '추세', fullText: '상승종목 추세분석', icon: <TrendingUpIcon />, page: 'rising-stocks' },
|
||||
{ shortText: '패턴', fullText: '패턴검색', icon: <ShowChart />, page: 'pattern-search' },
|
||||
{ shortText: '투자', fullText: '모의투자', icon: <SimulationIcon />, page: 'simulation' },
|
||||
{ shortText: '전략', fullText: '전략설정', icon: <RuleIcon />, page: 'strategy-tree' },
|
||||
{ shortText: '분석', fullText: '투자분석', icon: <Analytics />, page: 'candlestick-test' },
|
||||
{ shortText: '분석3', fullText: '투자분석3 (프론트엔드 지표)', icon: <Analytics />, page: 'candlestick-test-3' },
|
||||
{ shortText: '알림', fullText: '알림 설정', icon: <NotificationsIcon />, page: 'alerts' },
|
||||
{ shortText: '알림목록', fullText: '알림목록', icon: <AlertListIcon />, page: 'alert-list' },
|
||||
{ shortText: '호가', fullText: '실시간 호가', icon: <OrderbookIcon />, page: 'orderbook' },
|
||||
{ shortText: '자동매매', fullText: '자동매매', icon: <AutoTradingIcon />, page: 'auto-trading' },
|
||||
{ shortText: '검증', fullText: '차트 비교검증', icon: <CompareIcon />, page: 'chart-validation' },
|
||||
{ shortText: '데이터', fullText: '데이터 수집', icon: <CloudDownload />, page: 'data-collection' },
|
||||
{ shortText: '스케쥴', fullText: '스케쥴러', icon: <Schedule />, page: 'scheduler' },
|
||||
{ shortText: '설정', fullText: '설정', icon: <SettingsIcon />, page: 'settings' },
|
||||
];
|
||||
|
||||
function MainContent() {
|
||||
const isMobile = useIsMobile();
|
||||
const { currentPage, navigateToPage } = useNavigation();
|
||||
const { themeMode, setThemeMode, theme } = useAppTheme();
|
||||
const { isAuthenticated, user, logout } = useAuth();
|
||||
const { isPopupOpen, closePopup, chartData, chartMargin } = useIndicatorPopup();
|
||||
const { unreadCount, notifications, dismissAllNotifications } = useAlertNotification();
|
||||
|
||||
// 로그인/회원가입 다이얼로그 상태
|
||||
const [loginDialogOpen, setLoginDialogOpen] = useState(false);
|
||||
const [signUpDialogOpen, setSignUpDialogOpen] = useState(false);
|
||||
|
||||
// 사용자 메뉴 상태
|
||||
const [userMenuAnchor, setUserMenuAnchor] = useState<null | HTMLElement>(null);
|
||||
|
||||
// 전체화면 상태
|
||||
const [isFullscreen, setIsFullscreen] = useState(false);
|
||||
|
||||
const handleUserMenuOpen = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setUserMenuAnchor(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleUserMenuClose = () => {
|
||||
setUserMenuAnchor(null);
|
||||
};
|
||||
|
||||
const handleLogout = () => {
|
||||
logout();
|
||||
handleUserMenuClose();
|
||||
};
|
||||
|
||||
const renderPage = () => {
|
||||
// 투자분석/투자분석3: 상태 유지를 위해 항상 마운트하되 css display로 노출 여부 제어
|
||||
if (currentPage === 'candlestick-test') return null;
|
||||
if (currentPage === 'candlestick-test-3') return null;
|
||||
|
||||
switch (currentPage) {
|
||||
case 'dashboard':
|
||||
return <DashboardPage />;
|
||||
case 'multi-chart':
|
||||
return <MultiChartPage />;
|
||||
case 'multi-chart-2':
|
||||
return <MultiChartPage2 />;
|
||||
case 'rising-stocks':
|
||||
return <RisingStocksPage />;
|
||||
case 'pattern-search':
|
||||
return <PatternSearchPage />;
|
||||
case 'simulation':
|
||||
return <SimulationPage />;
|
||||
case 'strategy-tree':
|
||||
return <StrategyTreeBuilderPage />;
|
||||
case 'data-collection':
|
||||
return <DataCollectionPage />;
|
||||
case 'scheduler':
|
||||
return <SchedulerPage />;
|
||||
case 'alerts':
|
||||
return <AlertPage />;
|
||||
case 'alert-list':
|
||||
return <AlertListPage />;
|
||||
case 'notifications':
|
||||
return <NotificationListPage />;
|
||||
case 'orderbook':
|
||||
return <OrderbookPage />;
|
||||
case 'auto-trading':
|
||||
return <AutoTradingPage />;
|
||||
case 'chart-validation':
|
||||
return <ChartValidationPage />;
|
||||
case 'settings':
|
||||
return <SettingsPage />;
|
||||
default:
|
||||
return <DashboardPage />;
|
||||
}
|
||||
};
|
||||
|
||||
const handleThemeToggle = () => {
|
||||
setThemeMode(themeMode === 'dark' ? 'light' : 'dark');
|
||||
};
|
||||
|
||||
// 전체화면 토글 함수
|
||||
const handleFullscreenToggle = () => {
|
||||
if (!document.fullscreenElement) {
|
||||
// 전체화면으로 전환
|
||||
document.documentElement.requestFullscreen().catch((err) => {
|
||||
console.error('전체화면 전환 실패:', err);
|
||||
});
|
||||
} else {
|
||||
// 전체화면 해제
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 전체화면 상태 변경 감지
|
||||
React.useEffect(() => {
|
||||
const handleFullscreenChange = () => {
|
||||
setIsFullscreen(!!document.fullscreenElement);
|
||||
};
|
||||
|
||||
document.addEventListener('fullscreenchange', handleFullscreenChange);
|
||||
return () => {
|
||||
document.removeEventListener('fullscreenchange', handleFullscreenChange);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// 다크 모드 AppBar 스타일
|
||||
const darkAppBarStyle = {
|
||||
background: 'linear-gradient(90deg, #0a0a0f 0%, #12121a 50%, #1a1a2e 100%)',
|
||||
borderBottom: '1px solid rgba(100, 149, 237, 0.2)',
|
||||
boxShadow: '0 2px 10px rgba(0, 0, 0, 0.3)',
|
||||
};
|
||||
|
||||
// 라이트 모드 AppBar 스타일
|
||||
const lightAppBarStyle = {
|
||||
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
||||
boxShadow: '0 2px 10px rgba(0, 0, 0, 0.15)',
|
||||
};
|
||||
|
||||
// 모바일 접속 시 모바일 앱 UI (전체 화면 모바일 최적화)
|
||||
if (isMobile) {
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<MobileAppShell />
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
|
||||
// 데스크톱: 기존 레이아웃 유지
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', minHeight: '100vh' }}>
|
||||
{/* Top Navigation Bar */}
|
||||
<AppBar
|
||||
position="fixed"
|
||||
sx={{
|
||||
...(themeMode === 'dark' ? darkAppBarStyle : lightAppBarStyle),
|
||||
zIndex: (theme: any) => theme.zIndex.drawer + 100,
|
||||
}}
|
||||
>
|
||||
<Toolbar sx={{ justifyContent: 'space-between' }}>
|
||||
{/* Logo & Title */}
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Analytics sx={{ fontSize: 32, color: themeMode === 'dark' ? 'primary.main' : '#ffffff', mr: 1.5 }} />
|
||||
<Typography
|
||||
variant="h6"
|
||||
noWrap
|
||||
component="div"
|
||||
sx={{
|
||||
color: themeMode === 'dark' ? 'primary.main' : '#ffffff',
|
||||
fontWeight: 700,
|
||||
letterSpacing: '0.5px',
|
||||
}}
|
||||
>
|
||||
Golden Analysis
|
||||
</Typography>
|
||||
{/* <Typography
|
||||
variant="body2"
|
||||
sx={{
|
||||
ml: 2,
|
||||
color: themeMode === 'dark' ? 'text.secondary' : 'rgba(255, 255, 255, 0.8)',
|
||||
display: { xs: 'none', sm: 'block' },
|
||||
}}
|
||||
>
|
||||
투자전략 성능 분석 시스템
|
||||
</Typography> */}
|
||||
</Box>
|
||||
|
||||
{/* Navigation Menu */}
|
||||
<Box sx={{ display: 'flex', gap: 0.5, alignItems: 'center' }}>
|
||||
{menuItems.map((item) => (
|
||||
<Tooltip
|
||||
key={item.page}
|
||||
title={item.fullText}
|
||||
arrow
|
||||
placement="bottom"
|
||||
enterDelay={200}
|
||||
leaveDelay={0}
|
||||
>
|
||||
<IconButton
|
||||
onClick={() => navigateToPage(item.page)}
|
||||
sx={{
|
||||
color: themeMode === 'dark'
|
||||
? (currentPage === item.page ? 'primary.main' : 'text.secondary')
|
||||
: (currentPage === item.page ? '#ffffff' : 'rgba(255, 255, 255, 0.7)'),
|
||||
backgroundColor: currentPage === item.page
|
||||
? (themeMode === 'dark' ? 'rgba(100, 149, 237, 0.15)' : 'rgba(255, 255, 255, 0.2)')
|
||||
: 'transparent',
|
||||
borderBottom: currentPage === item.page ? '2px solid' : '2px solid transparent',
|
||||
borderBottomColor: currentPage === item.page
|
||||
? (themeMode === 'dark' ? 'primary.main' : '#ffffff')
|
||||
: 'transparent',
|
||||
borderRadius: 0,
|
||||
'&:hover': {
|
||||
backgroundColor: themeMode === 'dark' ? 'rgba(100, 149, 237, 0.1)' : 'rgba(255, 255, 255, 0.15)',
|
||||
color: themeMode === 'dark' ? 'primary.light' : '#ffffff',
|
||||
},
|
||||
transition: 'all 0.2s ease-in-out',
|
||||
}}
|
||||
>
|
||||
{item.page === 'alerts' && unreadCount > 0 ? (
|
||||
<Badge badgeContent={unreadCount} color="error">
|
||||
{item.icon}
|
||||
</Badge>
|
||||
) : (
|
||||
item.icon
|
||||
)}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
))}
|
||||
|
||||
{/* Theme Toggle Button */}
|
||||
<Tooltip title={themeMode === 'dark' ? '라이트 모드로 전환' : '다크 모드로 전환'}>
|
||||
<IconButton
|
||||
onClick={handleThemeToggle}
|
||||
sx={{
|
||||
ml: 2,
|
||||
color: themeMode === 'dark' ? 'primary.main' : '#ffffff',
|
||||
'&:hover': {
|
||||
backgroundColor: themeMode === 'dark' ? 'rgba(100, 149, 237, 0.1)' : 'rgba(255, 255, 255, 0.15)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{themeMode === 'dark' ? <LightMode /> : <DarkMode />}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
|
||||
{/* Fullscreen Toggle Button */}
|
||||
<Tooltip title={isFullscreen ? '전체화면 해제' : '전체화면으로 전환'}>
|
||||
<IconButton
|
||||
onClick={handleFullscreenToggle}
|
||||
sx={{
|
||||
ml: 1,
|
||||
color: themeMode === 'dark' ? 'primary.main' : '#ffffff',
|
||||
'&:hover': {
|
||||
backgroundColor: themeMode === 'dark' ? 'rgba(100, 149, 237, 0.1)' : 'rgba(255, 255, 255, 0.15)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{isFullscreen ? <FullscreenExit /> : <Fullscreen />}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
|
||||
{/* 알림팝업 전체 닫기 버튼 */}
|
||||
<Tooltip title="알림팝업 전체 닫기">
|
||||
<span>
|
||||
<IconButton
|
||||
onClick={dismissAllNotifications}
|
||||
disabled={notifications.length === 0}
|
||||
sx={{
|
||||
ml: 1,
|
||||
color: themeMode === 'dark' ? 'primary.main' : '#ffffff',
|
||||
'&:hover': {
|
||||
backgroundColor: themeMode === 'dark' ? 'rgba(100, 149, 237, 0.1)' : 'rgba(255, 255, 255, 0.15)',
|
||||
},
|
||||
'&.Mui-disabled': {
|
||||
color: themeMode === 'dark' ? 'text.disabled' : 'rgba(255, 255, 255, 0.4)',
|
||||
},
|
||||
}}
|
||||
aria-label="알림팝업 전체 닫기"
|
||||
>
|
||||
<ClearAllIcon />
|
||||
</IconButton>
|
||||
</span>
|
||||
</Tooltip>
|
||||
|
||||
{/* 로그인/사용자 버튼 */}
|
||||
{isAuthenticated ? (
|
||||
<>
|
||||
<Chip
|
||||
icon={<AccountCircle />}
|
||||
label={user?.name || user?.username || '사용자'}
|
||||
onClick={handleUserMenuOpen}
|
||||
sx={{
|
||||
ml: 2,
|
||||
backgroundColor: themeMode === 'dark' ? 'rgba(100, 149, 237, 0.2)' : 'rgba(255, 255, 255, 0.2)',
|
||||
color: themeMode === 'dark' ? 'primary.main' : '#ffffff',
|
||||
cursor: 'pointer',
|
||||
'&:hover': {
|
||||
backgroundColor: themeMode === 'dark' ? 'rgba(100, 149, 237, 0.3)' : 'rgba(255, 255, 255, 0.3)',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Menu
|
||||
anchorEl={userMenuAnchor}
|
||||
open={Boolean(userMenuAnchor)}
|
||||
onClose={handleUserMenuClose}
|
||||
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
|
||||
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
|
||||
>
|
||||
<MenuItem disabled>
|
||||
<ListItemIcon>
|
||||
<Person fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={user?.username}
|
||||
secondary={user?.email}
|
||||
/>
|
||||
</MenuItem>
|
||||
<Divider />
|
||||
<MenuItem onClick={handleLogout}>
|
||||
<ListItemIcon>
|
||||
<LogoutIcon fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="로그아웃" />
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</>
|
||||
) : (
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<LoginIcon />}
|
||||
onClick={() => setLoginDialogOpen(true)}
|
||||
sx={{
|
||||
ml: 2,
|
||||
borderColor: themeMode === 'dark' ? 'primary.main' : '#ffffff',
|
||||
color: themeMode === 'dark' ? 'primary.main' : '#ffffff',
|
||||
'&:hover': {
|
||||
borderColor: themeMode === 'dark' ? 'primary.light' : '#ffffff',
|
||||
backgroundColor: themeMode === 'dark' ? 'rgba(100, 149, 237, 0.1)' : 'rgba(255, 255, 255, 0.15)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
로그인
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* Version */}
|
||||
<Typography
|
||||
variant="caption"
|
||||
sx={{
|
||||
color: themeMode === 'dark' ? 'text.secondary' : 'rgba(255, 255, 255, 0.7)',
|
||||
display: { xs: 'none', md: 'block' },
|
||||
}}
|
||||
>
|
||||
v1.1.0
|
||||
</Typography>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
|
||||
{/* Main Content - Full Width */}
|
||||
<Box
|
||||
component="main"
|
||||
sx={{
|
||||
flexGrow: 1,
|
||||
bgcolor: 'background.default',
|
||||
mt: '64px', // AppBar height offset
|
||||
minHeight: 'calc(100vh - 64px)',
|
||||
}}
|
||||
>
|
||||
<Container
|
||||
maxWidth={false}
|
||||
sx={{
|
||||
py: 2,
|
||||
px: { xs: 2, sm: 3 },
|
||||
height: 'calc(100vh - 64px)',
|
||||
overflow: 'auto',
|
||||
}}
|
||||
>
|
||||
{/* 상태 유지가 필요한 투자분석 화면 */}
|
||||
<Box sx={{ display: currentPage === 'candlestick-test' ? 'block' : 'none', height: '100%' }}>
|
||||
<CandlestickTestPage />
|
||||
</Box>
|
||||
{/* 투자분석3 (프론트엔드 지표 계산 버전) — 상태 유지 */}
|
||||
<Box sx={{ display: currentPage === 'candlestick-test-3' ? 'block' : 'none', height: '100%' }}>
|
||||
<CandlestickTestPage3 />
|
||||
</Box>
|
||||
|
||||
{renderPage()}
|
||||
</Container>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* 로그인 다이얼로그 */}
|
||||
<LoginDialog
|
||||
open={loginDialogOpen}
|
||||
onClose={() => setLoginDialogOpen(false)}
|
||||
onSwitchToSignUp={() => {
|
||||
setLoginDialogOpen(false);
|
||||
setSignUpDialogOpen(true);
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 회원가입 다이얼로그 */}
|
||||
<SignUpDialog
|
||||
open={signUpDialogOpen}
|
||||
onClose={() => setSignUpDialogOpen(false)}
|
||||
onSwitchToLogin={() => {
|
||||
setSignUpDialogOpen(false);
|
||||
setLoginDialogOpen(true);
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 지표 설정 패널 (우측 하단 FAB) */}
|
||||
<IndicatorConfigPanel />
|
||||
|
||||
{/* 보조지표 팝업 윈도우 */}
|
||||
<IndicatorPopupWindow
|
||||
open={isPopupOpen}
|
||||
onClose={closePopup}
|
||||
chartData={chartData}
|
||||
chartMargin={chartMargin}
|
||||
/>
|
||||
|
||||
{/* 알림 스낵바 (모든 화면에서 표시) */}
|
||||
<AlertSnackbar />
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
|
||||
function App() {
|
||||
// Context providers are now in AppRouter.tsx
|
||||
return <MainContent />;
|
||||
}
|
||||
|
||||
export default App;
|
||||
Reference in New Issue
Block a user