/** * 최상단 글로벌 메뉴바 * * 레이아웃: * [로고] | [그룹메뉴 ▾] … | [테마토글] [로그인] */ import React, { memo, useEffect, useState } from 'react'; import type { Theme } from '../types'; import type { AuthSession } from '../utils/auth'; import type { TradeAlertPopupLayout, TradeAlertPopupPosition } from '../utils/tradeAlertPopupLayout'; import { canAccessMenu } from '../utils/permissions'; import { isDesktop } from '../utils/platform'; import { useAppTitleBarVersion } from '../hooks/useDesktopAppTitle'; import DesktopUpdateModal from './DesktopUpdateModal'; import TopMenuBarNavGroups from './TopMenuBarNavGroups'; export type MenuPage = 'dashboard' | 'chart' | 'paper' | 'virtual' | 'trend-search' | 'verification-board' | 'strategy' | 'strategy-editor' | 'strategy-evaluation' | 'backtest' | 'analysis-report' | 'notifications' | 'settings' | 'widget-dashboard'; interface TopMenuBarProps { activePage: MenuPage; theme: Theme; onPage: (page: MenuPage) => void; onTheme: () => void; /** 미확인 매매 시그널 알림 수 — 알림목록 메뉴 뱃지로 표시 */ tradeNotifyUnread?: number; /** @deprecated 알림 아이콘 버튼 제거됨, 하위 호환 유지 */ onOpenNotifications?: () => void; /** 모바일 앱 다운로드 모달 */ onOpenAppDownload?: () => void; /** 우측에 떠 있는 알림 팝업(토스트) 개수 */ tradeNotifyToastCount?: number; /** 모든 알림 팝업 닫기 */ onDismissAllNotifyPopups?: () => void; /** 알림 팝업 표시 위치·방식 */ tradeAlertPopupPosition?: string; tradeAlertPopupLayout?: string; tradeAlertPopupGridCols?: number; onTradeAlertPopupPosition?: (v: TradeAlertPopupPosition) => void; onTradeAlertPopupLayout?: (v: TradeAlertPopupLayout) => void; onTradeAlertPopupGridCols?: (v: number) => void; /** 로그인 사용자 (null = 비로그인·기기별 설정) */ authUser?: AuthSession | null; /** 게스트 모드(스플래시에서 게스트 진입) */ guestMode?: boolean; onLoginClick?: () => void; onLogout?: () => void; /** 브라우저 전체화면 (모든 화면에서 사용) */ onFullscreen?: () => void; /** 플로팅 위젯 레이아웃 선택 열기 */ onOpenFloatingWidgets?: () => void; /** 실행 중인 플로팅 위젯 창 수 */ floatingWidgetCount?: number; /** 데스크톱 — 현재 화면 데이터 새로고침 */ onRefresh?: () => void; /** 메뉴별 접근 허용 (없으면 전체 표시) */ menuPermissions?: Record; } // ── SVG 아이콘 ──────────────────────────────────────────────────────────── const IcDashboard = () => ( ); const IcChart = () => ( ); const IcAnalysisReport = () => ( ); const IcBacktest = () => ( ); const IcSettings = () => ( ); const IcAppDownload = () => ( ); const IcDesktopUpdate = () => ( ); const IcRefresh = () => ( ); const IcNotify = () => ( ); const IcFullscreen = () => ( ); const IcFullscreenExit = () => ( ); const IcLogin = () => ( ); // ── 테마 아이콘 ─────────────────────────────────────────────────────────── const THEME_CONFIG: Record = { dark: { icon: '🌙', label: '다크', next: 'blue' }, blue: { icon: '🌊', label: '블루', next: 'light' }, light: { icon: '☀️', label: '라이트', next: 'dark' }, }; const IcStrategyEditor = () => ( ); const IcStrategyEvaluation = () => ( ); const IcPaper = () => ( ); const IcVirtual = () => ( ); const IcTrendSearch = () => ( ); const IcVerificationBoard = () => ( ); const IcWidget = () => ( ); const IcFloatingWidgetAdd = () => ( ); const MENU_ITEMS: { page: MenuPage; label: string; icon: React.ReactNode }[] = [ { page: 'dashboard', label: '대시보드', icon: }, { page: 'widget-dashboard', label: '위젯', icon: }, { page: 'chart', label: '실시간차트', icon: }, { page: 'paper', label: '투자관리', icon: }, { page: 'virtual', label: '가상매매', icon: }, { page: 'trend-search', label: '추세검색', icon: }, { page: 'strategy-editor', label: '전략편집기', icon: }, { page: 'strategy-evaluation', label: '전략 평가', icon: }, { page: 'backtest', label: '백테스팅', icon: }, { page: 'analysis-report', label: '분석레포트', icon: }, { page: 'notifications', label: '알림목록', icon: }, { page: 'settings', label: '설정', icon: }, { page: 'verification-board', label: '검증게시판', icon: }, ]; /** 기능별 그룹 — 클릭 시 하단 드롭다운 */ const MENU_GROUPS = [ { id: 'home', label: '홈·분석', icon: , pages: ['dashboard', 'widget-dashboard', 'analysis-report'] as MenuPage[], }, { id: 'trading', label: '차트·매매', icon: , pages: ['chart', 'paper', 'virtual', 'trend-search'] as MenuPage[], }, { id: 'strategy', label: '전략', icon: , pages: ['strategy-editor', 'strategy-evaluation', 'backtest'] as MenuPage[], }, { id: 'system', label: '알림·설정', icon: , pages: ['notifications', 'verification-board', 'settings'] as MenuPage[], }, ]; export const TopMenuBar = memo(function TopMenuBar({ activePage, theme, onPage, onTheme, tradeNotifyUnread = 0, onOpenAppDownload, tradeNotifyToastCount = 0, onDismissAllNotifyPopups, tradeAlertPopupPosition = 'right', tradeAlertPopupLayout = 'stack', tradeAlertPopupGridCols = 2, onTradeAlertPopupPosition, onTradeAlertPopupLayout, onTradeAlertPopupGridCols, authUser = null, guestMode = false, onLoginClick, onLogout, onFullscreen, onOpenFloatingWidgets, floatingWidgetCount = 0, onRefresh, menuPermissions, }: TopMenuBarProps) { const [isFullscreen, setIsFullscreen] = useState(() => !!document.fullscreenElement); const [desktopUpdateOpen, setDesktopUpdateOpen] = useState(false); const [refreshBusy, setRefreshBusy] = useState(false); const desktopClient = isDesktop(); const appVersion = useAppTitleBarVersion(); useEffect(() => { const sync = () => setIsFullscreen(!!document.fullscreenElement); document.addEventListener('fullscreenchange', sync); return () => document.removeEventListener('fullscreenchange', sync); }, []); const tc = THEME_CONFIG[theme]; const visibleItems = menuPermissions ? MENU_ITEMS.filter(({ page }) => canAccessMenu(menuPermissions, page)) : MENU_ITEMS; return ( <>
{/* 로고 */}
GoldenChart {appVersion ? ( (v{appVersion}) ) : null}
{/* 기능별 그룹 메뉴 */}
{desktopClient && ( setDesktopUpdateOpen(false)} autoStart /> )} ); }); export default TopMenuBar;