feat(phase-b): student enrollment UI, teacher/student management pages, lesson views

- ClassDetailPage: full rewrite with MUI tabs (students, enrollment requests, teachers),
  add/remove students, approve/reject enrollment requests, AddStudentDialog
- StudentLessonsPage: new — student's weekly lesson view with week navigation
- TaughtLessonsPage: teacher's taught lesson week view
- SchoolSettingsPage, StaffManagerPage, StudentManagerPage: school admin management pages
- PlatformAdminPage: platform admin reset/seed controls
- Header: expanded nav menu (student lessons, school management, platform admin items)
- AppRoutes: routes for all new pages
- SchoolCalendarWizard, TeacherTimetableWizard: week_cycle support and improvements
- CCGraphNavPanel: updated navigation integration
- index.ts: export all new timetable pages

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
2026-05-27 02:56:04 +01:00
co-authored by Claude Sonnet 4.6
parent 138dfb1531
commit 6ac5ab7b5c
13 changed files with 2663 additions and 384 deletions
+98 -25
View File
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect, useState, useCallback } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
import { useAuth } from '../contexts/AuthContext';
import {
@@ -19,9 +19,6 @@ 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';
@@ -33,32 +30,61 @@ 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 Lessons from '@mui/icons-material/EventNote';
import People from '@mui/icons-material/People';
import SchoolSettings from '@mui/icons-material/Tune';
import { HEADER_HEIGHT } from './Layout';
import { logger } from '../debugConfig';
import { GraphNavigator } from '../components/navigation/GraphNavigator';
const API_BASE = import.meta.env.VITE_API_URL || 'http://localhost:8000';
const Header: React.FC = () => {
const theme = useTheme();
const navigate = useNavigate();
const location = useLocation();
const { user, signOut } = useAuth();
const { user, signOut, accessToken } = useAuth();
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const [isPlatformAdmin, setIsPlatformAdmin] = useState(false);
const [schoolRole, setSchoolRole] = useState<string | null>(null);
const [isAuthenticated, setIsAuthenticated] = useState(!!user);
const isAdmin = user?.email === import.meta.env.VITE_SUPER_ADMIN_EMAIL;
const showGraphNavigation = location.pathname === '/single-player';
const isSchoolAdmin = schoolRole === 'school_admin' || schoolRole === 'department_head';
// 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]);
setIsAuthenticated(!!user);
}, [user]);
// Check platform admin status and school role once on login
const checkAdminStatus = useCallback(async () => {
if (!accessToken) return;
// Platform admin check
try {
const res = await fetch(`${API_BASE}/admin/stats`, {
headers: { Authorization: `Bearer ${accessToken}` },
});
setIsPlatformAdmin(res.ok);
} catch {
setIsPlatformAdmin(false);
}
// School role check
try {
const res = await fetch(`${API_BASE}/school/status`, {
headers: { Authorization: `Bearer ${accessToken}` },
});
if (res.ok) {
const data = await res.json();
setSchoolRole(data.user_role || null);
}
} catch {
setSchoolRole(null);
}
}, [accessToken]);
useEffect(() => {
if (accessToken) checkAdminStatus();
else { setIsPlatformAdmin(false); setSchoolRole(null); }
}, [accessToken, checkAdminStatus]);
const handleMenuOpen = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
@@ -239,13 +265,62 @@ const Header: React.FC = () => {
<ListItemIcon>
<Enrollment />
</ListItemIcon>
<ListItemText
primary="Enrollment Requests"
<ListItemText
primary="Enrollment Requests"
secondary="Review pending enrollments"
/>
</MenuItem>,
<MenuItem key="my-lessons" onClick={() => handleNavigation('/my-lessons')}>
<ListItemIcon>
<Lessons />
</ListItemIcon>
<ListItemText
primary="My Lessons"
secondary="Weekly lesson view"
/>
</MenuItem>,
<MenuItem key="student-lessons" onClick={() => handleNavigation('/student-lessons')}>
<ListItemIcon>
<Student />
</ListItemIcon>
<ListItemText
primary="Student Lessons"
secondary="Your class timetable"
/>
</MenuItem>,
<Divider key="timetable-divider" />,
// School Admin Section
...(isSchoolAdmin ? [
<Typography
key="school-admin-header"
variant="subtitle2"
sx={{
px: 2, py: 1,
color: theme.palette.primary.main,
fontWeight: 600,
letterSpacing: '0.5px',
textTransform: 'uppercase',
fontSize: '0.75rem',
}}
>
School Admin
</Typography>,
<MenuItem key="school-settings" onClick={() => handleNavigation('/school-settings')}>
<ListItemIcon><SchoolSettings /></ListItemIcon>
<ListItemText primary="School Settings" secondary="Calendar, overview" />
</MenuItem>,
<MenuItem key="staff-manager" onClick={() => handleNavigation('/staff-manager')}>
<ListItemIcon><People /></ListItemIcon>
<ListItemText primary="Staff Manager" secondary="Invite & manage teachers" />
</MenuItem>,
<MenuItem key="student-manager" onClick={() => handleNavigation('/student-manager')}>
<ListItemIcon><Teacher /></ListItemIcon>
<ListItemText primary="Student Manager" secondary="Invite & manage students" />
</MenuItem>,
<Divider key="school-admin-divider" />,
] : []),
// Features Section
<Typography
key="features-header"
@@ -311,8 +386,8 @@ const Header: React.FC = () => {
<ListItemText primary="Search" />
</MenuItem>,
// Admin Section
...(isAdmin ? [
// Platform Admin Section
...(isPlatformAdmin ? [
<Divider key="admin-divider" />,
<Typography
key="admin-header"
@@ -330,10 +405,8 @@ const Header: React.FC = () => {
Administration
</Typography>,
<MenuItem key="admin" onClick={() => handleNavigation('/admin')}>
<ListItemIcon>
<Admin />
</ListItemIcon>
<ListItemText primary="Admin Dashboard" />
<ListItemIcon><Admin /></ListItemIcon>
<ListItemText primary="Platform Admin" secondary="Schools & system stats" />
</MenuItem>
] : []),