app/src/pages/Header.tsx
Agent Zero 51c0bfaeaf fix: correct MUI icon usage in Header and NotFoundPublic
- Header.tsx: Use <MenuIcon /> instead of <Menu /> component
- NotFoundPublic.tsx: Use <ErrorOutlineIcon /> instead of <ErrorOutline />

Resolves: Menu open prop error and ErrorOutline undefined error
2026-02-26 07:38:05 +00:00

384 lines
13 KiB
TypeScript

import React, { useEffect, useState } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
import { useAuth } from '../contexts/AuthContext';
import {
AppBar,
Toolbar,
Typography,
IconButton,
Box,
useTheme,
Menu,
MenuItem,
ListItemIcon,
ListItemText,
Divider
} from '@mui/material';
import MenuIcon from '@mui/icons-material/Menu';
import Login from '@mui/icons-material/Login';
import Logout from '@mui/icons-material/Logout';
import Teacher from '@mui/icons-material/School';
import Student from '@mui/icons-material/Person';
import TLDrawDev from '@mui/icons-material/Dashboard';
import DevTools from '@mui/icons-material/Build';
import Multiplayer from '@mui/icons-material/Groups';
import Calendar from '@mui/icons-material/CalendarToday';
import TeacherPlanner from '@mui/icons-material/Assignment';
import ExamMarker from '@mui/icons-material/AssignmentTurnedIn';
import Settings from '@mui/icons-material/Settings';
import Search from '@mui/icons-material/Search';
import Admin from '@mui/icons-material/AdminPanelSettings';
import Home from '@mui/icons-material/Home';
import Schedule from '@mui/icons-material/Schedule';
import Class from '@mui/icons-material/Class';
import Book from '@mui/icons-material/Book';
import Enrollment from '@mui/icons-material/HowToReg';
import { HEADER_HEIGHT } from './Layout';
import { logger } from '../debugConfig';
import { GraphNavigator } from '../components/navigation/GraphNavigator';
const Header: React.FC = () => {
const theme = useTheme();
const navigate = useNavigate();
const location = useLocation();
const { user, signOut } = useAuth();
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const [isAuthenticated, setIsAuthenticated] = useState(!!user);
const isAdmin = user?.email === import.meta.env.VITE_SUPER_ADMIN_EMAIL;
const showGraphNavigation = location.pathname === '/single-player';
// Update authentication state whenever user changes
useEffect(() => {
const newAuthState = !!user;
setIsAuthenticated(newAuthState);
logger.debug('user-context', '🔄 User state changed in header', {
hasUser: newAuthState,
userId: user?.id,
userEmail: user?.email,
userState: newAuthState ? 'logged-in' : 'logged-out',
isAdmin
});
}, [user, isAdmin]);
const handleMenuOpen = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};
const handleMenuClose = () => {
setAnchorEl(null);
};
const handleNavigation = (path: string) => {
navigate(path);
handleMenuClose();
};
const handleSignupNavigation = (role: 'teacher' | 'student') => {
navigate('/signup', { state: { role } });
handleMenuClose();
};
const handleSignOut = async () => {
try {
logger.debug('auth-service', '🔄 Signing out user', { userId: user?.id });
await signOut();
setIsAuthenticated(false);
setAnchorEl(null);
logger.debug('auth-service', '✅ User signed out');
} catch (error) {
logger.error('auth-service', '❌ Error signing out:', error);
console.error('Error signing out:', error);
}
};
return (
<AppBar
position="fixed"
sx={{
height: `${HEADER_HEIGHT}px`,
bgcolor: theme.palette.background.paper,
color: theme.palette.text.primary,
boxShadow: 1
}}
>
<Toolbar sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
height: '100%'
}}>
{showGraphNavigation && (
<GraphNavigator />
)}
<Typography
variant="h6"
component="div"
sx={{
flexGrow: showGraphNavigation ? 0 : 1,
fontWeight: 600,
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
gap: 1
}}
onClick={() => handleNavigation('/')}
>
<Home sx={{ color: theme.palette.primary.main }} />
Classroom Copilot
</Typography>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
{isAuthenticated && (
<Typography
variant="body2"
sx={{
color: theme.palette.text.secondary,
display: { xs: 'none', sm: 'block' }
}}
>
{user?.email}
</Typography>
)}
<IconButton
color="inherit"
aria-label="menu"
onClick={handleMenuOpen}
sx={{
border: `1px solid ${theme.palette.divider}`,
borderRadius: 1,
'&:hover': {
bgcolor: theme.palette.action.hover
}
}}
>
<MenuIcon />
</IconButton>
<Menu
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={handleMenuClose}
PaperProps={{
sx: {
width: 280,
maxHeight: '80vh'
}
}}
>
<Typography
variant="subtitle2"
sx={{
px: 2,
py: 1.5,
color: theme.palette.text.secondary,
fontWeight: 600,
letterSpacing: '0.5px',
textTransform: 'uppercase',
fontSize: '0.75rem'
}}
>
Navigation
</Typography>
{isAuthenticated ? [
// Home
<MenuItem key="home" onClick={() => handleNavigation('/')}>
<ListItemIcon>
<Home />
</ListItemIcon>
<ListItemText primary="Home" />
</MenuItem>,
<Divider key="home-divider" />,
// Timetable Section
<Typography
key="timetable-header"
variant="subtitle2"
sx={{
px: 2,
py: 1,
color: theme.palette.primary.main,
fontWeight: 600,
letterSpacing: '0.5px',
textTransform: 'uppercase',
fontSize: '0.75rem'
}}
>
Timetable & Classes
</Typography>,
<MenuItem key="timetable" onClick={() => handleNavigation('/timetable')}>
<ListItemIcon>
<Schedule />
</ListItemIcon>
<ListItemText
primary="My Timetable"
secondary="View your schedule"
/>
</MenuItem>,
<MenuItem key="classes" onClick={() => handleNavigation('/classes')}>
<ListItemIcon>
<Class />
</ListItemIcon>
<ListItemText
primary="Browse Classes"
secondary="Find and enroll in classes"
/>
</MenuItem>,
<MenuItem key="my-classes" onClick={() => handleNavigation('/my-classes')}>
<ListItemIcon>
<Book />
</ListItemIcon>
<ListItemText
primary="My Classes"
secondary="Manage your classes"
/>
</MenuItem>,
<MenuItem key="enrollment-requests" onClick={() => handleNavigation('/enrollment-requests')}>
<ListItemIcon>
<Enrollment />
</ListItemIcon>
<ListItemText
primary="Enrollment Requests"
secondary="Review pending enrollments"
/>
</MenuItem>,
<Divider key="timetable-divider" />,
// Features Section
<Typography
key="features-header"
variant="subtitle2"
sx={{
px: 2,
py: 1,
color: theme.palette.text.secondary,
fontWeight: 600,
letterSpacing: '0.5px',
textTransform: 'uppercase',
fontSize: '0.75rem'
}}
>
Features
</Typography>,
<MenuItem key="calendar" onClick={() => handleNavigation('/calendar')}>
<ListItemIcon>
<Calendar />
</ListItemIcon>
<ListItemText primary="Calendar" />
</MenuItem>,
<MenuItem key="teacher-planner" onClick={() => handleNavigation('/teacher-planner')}>
<ListItemIcon>
<TeacherPlanner />
</ListItemIcon>
<ListItemText primary="Teacher Planner" />
</MenuItem>,
<MenuItem key="exam-marker" onClick={() => handleNavigation('/exam-marker')}>
<ListItemIcon>
<ExamMarker />
</ListItemIcon>
<ListItemText primary="Exam Marker" />
</MenuItem>,
<Divider key="features-divider" />,
// Utilities Section
<Typography
key="utilities-header"
variant="subtitle2"
sx={{
px: 2,
py: 1,
color: theme.palette.text.secondary,
fontWeight: 600,
letterSpacing: '0.5px',
textTransform: 'uppercase',
fontSize: '0.75rem'
}}
>
Utilities
</Typography>,
<MenuItem key="settings" onClick={() => handleNavigation('/settings')}>
<ListItemIcon>
<Settings />
</ListItemIcon>
<ListItemText primary="Settings" />
</MenuItem>,
<MenuItem key="search" onClick={() => handleNavigation('/search')}>
<ListItemIcon>
<Search />
</ListItemIcon>
<ListItemText primary="Search" />
</MenuItem>,
// Admin Section
...(isAdmin ? [
<Divider key="admin-divider" />,
<Typography
key="admin-header"
variant="subtitle2"
sx={{
px: 2,
py: 1,
color: theme.palette.error.main,
fontWeight: 600,
letterSpacing: '0.5px',
textTransform: 'uppercase',
fontSize: '0.75rem'
}}
>
Administration
</Typography>,
<MenuItem key="admin" onClick={() => handleNavigation('/admin')}>
<ListItemIcon>
<Admin />
</ListItemIcon>
<ListItemText primary="Admin Dashboard" />
</MenuItem>
] : []),
// Authentication Section
<Divider key="auth-divider" />,
<MenuItem key="signout" onClick={handleSignOut}>
<ListItemIcon>
<Logout />
</ListItemIcon>
<ListItemText primary="Sign Out" />
</MenuItem>
] : [
// Authentication Section for Non-authenticated Users
<MenuItem key="signin" onClick={() => handleNavigation('/login')}>
<ListItemIcon>
<Login />
</ListItemIcon>
<ListItemText primary="Sign In" />
</MenuItem>,
<Divider key="signup-divider" />,
<MenuItem key="teacher-signup" onClick={() => handleSignupNavigation('teacher')}>
<ListItemIcon>
<Teacher />
</ListItemIcon>
<ListItemText
primary="Sign up as Teacher"
secondary="Create a teacher account"
/>
</MenuItem>,
<MenuItem key="student-signup" onClick={() => handleSignupNavigation('student')}>
<ListItemIcon>
<Student />
</ListItemIcon>
<ListItemText
primary="Sign up as Student"
secondary="Create a student account"
/>
</MenuItem>
]}
</Menu>
</Box>
</Toolbar>
</AppBar>
);
};
export default Header;