import React, { useState } from 'react'; import { AppBar, Box, CssBaseline, Drawer, IconButton, List, ListItem, ListItemButton, ListItemIcon, ListItemText, Toolbar, Typography, Avatar, Menu, MenuItem, Divider, useTheme as useMuiTheme, useMediaQuery, Badge, } from '@mui/material'; import { Menu as MenuIcon, Dashboard as DashboardIcon, CloudUpload as UploadIcon, Search as SearchIcon, Folder as FolderIcon, Settings as SettingsIcon, Notifications as NotificationsIcon, AccountCircle as AccountIcon, Logout as LogoutIcon, Description as DocumentIcon, Storage as StorageIcon, Error as ErrorIcon, Label as LabelIcon, Block as BlockIcon, Api as ApiIcon, ManageAccounts as ManageIcon, BugReport as BugReportIcon, } from '@mui/icons-material'; import { useNavigate, useLocation } from 'react-router-dom'; import { useAuth } from '../../contexts/AuthContext'; import { useNotifications } from '../../contexts/NotificationContext'; import GlobalSearchBar from '../GlobalSearchBar'; import ThemeToggle from '../ThemeToggle/ThemeToggle'; import NotificationPanel from '../Notifications/NotificationPanel'; import LanguageSwitcher from '../LanguageSwitcher'; import BottomNavigation from './BottomNavigation'; import { usePWA } from '../../hooks/usePWA'; import { useTranslation } from 'react-i18next'; const drawerWidth = 280; interface NavigationItem { textKey: string; icon: React.ComponentType; path: string; } interface AppLayoutProps { children: React.ReactNode; } interface User { username?: string; email?: string; } const getNavigationItems = (t: (key: string) => string): NavigationItem[] => [ { textKey: 'navigation.dashboard', icon: DashboardIcon, path: '/dashboard' }, { textKey: 'navigation.upload', icon: UploadIcon, path: '/upload' }, { textKey: 'navigation.documents', icon: DocumentIcon, path: '/documents' }, { textKey: 'navigation.search', icon: SearchIcon, path: '/search' }, { textKey: 'navigation.labels', icon: LabelIcon, path: '/labels' }, { textKey: 'navigation.sources', icon: StorageIcon, path: '/sources' }, { textKey: 'navigation.watchFolder', icon: FolderIcon, path: '/watch' }, { textKey: 'navigation.documentManagement', icon: ManageIcon, path: '/documents/management' }, { textKey: 'navigation.ignoredFiles', icon: BlockIcon, path: '/ignored-files' }, ]; const AppLayout: React.FC = ({ children }) => { const theme = useMuiTheme(); const isMobile = useMediaQuery(theme.breakpoints.down('md')); const isPWA = usePWA(); const [mobileOpen, setMobileOpen] = useState(false); const [anchorEl, setAnchorEl] = useState(null); const [notificationAnchorEl, setNotificationAnchorEl] = useState(null); const navigate = useNavigate(); const location = useLocation(); const { user, logout } = useAuth(); const { unreadCount } = useNotifications(); const { t } = useTranslation(); const navigationItems = getNavigationItems(t); const handleDrawerToggle = (): void => { setMobileOpen(!mobileOpen); }; const handleProfileMenuOpen = (event: React.MouseEvent): void => { setAnchorEl(event.currentTarget); }; const handleProfileMenuClose = (): void => { setAnchorEl(null); }; const handleLogout = (): void => { logout(); handleProfileMenuClose(); navigate('/login'); }; const handleNotificationClick = (event: React.MouseEvent): void => { setNotificationAnchorEl(notificationAnchorEl ? null : event.currentTarget); }; const handleNotificationClose = (): void => { setNotificationAnchorEl(null); }; const drawer = ( {/* Logo Section */} Readur Logo { // Fallback to "R" if image fails to load e.currentTarget.style.display = 'none'; e.currentTarget.parentElement!.innerHTML = 'R'; }} /> {t('common.appName')} {t('common.appTagline')} {/* Navigation */} {navigationItems.map((item) => { const isActive = location.pathname === item.path; const Icon = item.icon; return ( navigate(item.path)} sx={{ borderRadius: 3, minHeight: 52, px: 2.5, py: 1.5, background: isActive ? 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)' : 'transparent', color: isActive ? 'white' : 'text.primary', position: 'relative', overflow: 'hidden', transition: 'all 0.2s ease-in-out', '&:hover': { backgroundColor: isActive ? 'transparent' : 'rgba(99,102,241,0.08)', transform: isActive ? 'none' : 'translateX(4px)', '&::before': isActive ? {} : { content: '""', position: 'absolute', left: 0, top: 0, bottom: 0, width: '3px', background: 'linear-gradient(180deg, #6366f1 0%, #8b5cf6 100%)', borderRadius: '0 2px 2px 0', }, }, '&::after': isActive ? { content: '""', position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, background: 'linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.05) 100%)', backdropFilter: 'blur(10px)', } : {}, '& .MuiListItemIcon-root': { color: isActive ? 'white' : 'text.secondary', minWidth: 36, position: 'relative', zIndex: 1, }, '& .MuiListItemText-root': { position: 'relative', zIndex: 1, }, ...(isActive && { boxShadow: '0 8px 32px rgba(99,102,241,0.3)', }), }} > ); })} {/* User Info */} {user?.username?.charAt(0).toUpperCase()} {user?.username} {user?.email} ); return ( {/* App Bar */} {navigationItems.find(item => item.path === location.pathname)?.textKey ? t(navigationItems.find(item => item.path === location.pathname)!.textKey) : t('navigation.dashboard')} {/* Global Search Bar */} {/* Notifications */} {/* Language Switcher */} {/* Theme Toggle */} {/* Profile Menu */} navigate('/profile')}> {t('auth.profile')} navigate('/settings')}> {t('settings.title')} navigate('/debug')}> {t('settings.debug')} window.open('/swagger-ui', '_blank')}> {t('settings.apiDocumentation')} {t('auth.logout')} {/* Navigation Drawer */} {drawer} {drawer} {/* Main Content */} {children} {/* Notification Panel */} {/* Bottom Navigation (PWA only) */} ); }; export default AppLayout;