import React, { useEffect, useState, useCallback } from 'react'; import { Box, Typography, Button, CircularProgress, Alert, Chip, Table, TableHead, TableBody, TableRow, TableCell, Select, MenuItem, FormControl, Tooltip, Divider, Accordion, AccordionSummary, AccordionDetails, Grid, Card, CardContent, IconButton, } from '@mui/material'; import { ExpandMore, People, School, MenuBook, EventNote, HourglassEmpty, Refresh, Edit, } from '@mui/icons-material'; import { useNavigate } from 'react-router'; import { useAuth } from '../../contexts/AuthContext'; const API_BASE = import.meta.env.VITE_API_BASE || import.meta.env.VITE_API_URL || '/api'; const DAY_TYPE_OPTIONS = ['Academic', 'Holiday', 'Staff', 'OffTimetable']; const DAY_TYPE_COLORS: Record = { Academic: 'primary', Holiday: 'warning', Staff: 'success', OffTimetable: 'default', }; interface Term { id: string; term_name: string; term_number: number; start_date: string; end_date: string; academic_days: number; total_days: number; is_current: boolean; } interface CalendarDay { id: string; date: string; day_of_week: string; day_type: string; week_cycle: string; week_number: number | null; academic_day_number: number | null; excluded_period_codes: string[]; } interface Overview { user_role: string; counts: { staff: number; students: number; classes: number; pending_invitations: number; }; terms: Term[]; has_calendar: boolean; } // ─── Stat card ──────────────────────────────────────────────────────────────── function StatCard({ label, value, icon: Icon, onClick }: { label: string; value: number | string; icon: React.ElementType; onClick?: () => void; }) { return ( {value} {label} ); } // ─── Calendar days table ────────────────────────────────────────────────────── function CalendarDaysTable({ termId, headers, isAdmin }: { termId: string; headers: Record; isAdmin: boolean; }) { const [days, setDays] = useState([]); const [loading, setLoading] = useState(false); const [saving, setSaving] = useState(null); useEffect(() => { setLoading(true); fetch(`${API_BASE}/school/calendar/days?term_id=${termId}`, { headers }) .then(r => r.json()) .then(data => setDays(data.days || [])) .catch(() => {}) .finally(() => setLoading(false)); }, [termId]); const handleTypeChange = async (dayId: string, newType: string) => { setSaving(dayId); try { await fetch(`${API_BASE}/school/calendar/days/${dayId}`, { method: 'PATCH', headers: { ...headers, 'Content-Type': 'application/json' }, body: JSON.stringify({ day_type: newType }), }); setDays(prev => prev.map(d => d.id === dayId ? { ...d, day_type: newType } : d)); } catch { } setSaving(null); }; if (loading) return ; return ( Date Day Week Cycle Type {days.map(day => ( {day.date} {day.day_of_week.slice(0, 3)} {day.week_number ?? '—'} {day.week_cycle ? : } {isAdmin ? ( ) : ( )} ))}
); } // ─── Main page ──────────────────────────────────────────────────────────────── const SchoolSettingsPage: React.FC = () => { const { accessToken } = useAuth(); const navigate = useNavigate(); const [overview, setOverview] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [expandedTerm, setExpandedTerm] = useState(false); const headers = { Authorization: `Bearer ${accessToken}` }; const loadOverview = useCallback(async () => { if (!accessToken) return; setLoading(true); setError(null); try { const res = await fetch(`${API_BASE}/school/overview`, { headers }); const data = await res.json(); if (data.status === 'ok') setOverview(data); else setError(data.detail || 'Failed to load school overview'); } catch (e: any) { setError(e.message); } finally { setLoading(false); } }, [accessToken]); useEffect(() => { loadOverview(); }, [loadOverview]); const isAdmin = overview?.user_role === 'school_admin' || overview?.user_role === 'department_head'; if (loading) return ; return ( School Settings {overview && ( {overview.user_role} )} {error && setError(null)}>{error}} {overview && ( <> {/* Stat cards */} navigate('/staff-manager')} /> navigate('/student-manager')} /> navigate('/classes')} /> {/* Calendar */} Academic Calendar {!overview.has_calendar && ( No calendar configured )} {overview.terms.length === 0 ? ( No academic calendar set up yet. Use the School Calendar Wizard in the navigation panel to set one up. ) : ( overview.terms.map(term => ( setExpandedTerm(open ? term.id : false)} sx={{ mb: 0.5 }} > }> {term.term_name} {term.is_current && ( )} {term.start_date} → {term.end_date} {term.total_days > term.academic_days && ( )} {isAdmin && ( Changing a day type automatically creates or removes its periods. )} )) )} {/* Quick links */} Manage )} ); }; export default SchoolSettingsPage;