import { useCallback, useEffect, useRef, useState } from 'react' import { Link, NavLink, Outlet, useLocation, useNavigate } from 'react-router-dom' import { useAuth } from '../auth/AuthProvider' import { CiPublicFooter } from '../components/CiPartials' import { ReadOnlyBanner, SchoolYearSelector } from '../components/schoolYear' import { readPortalStylePrefs, resolvePortalColorMode, type PortalStylePrefs } from '../lib/portalAppearance' import { isParentPortalRole, isTeacherPortalRole } from '../lib/portalRoles' const parentNavItems = [ { to: '/app/parent/home', icon: 'bi bi-house-door', label: 'Dashboard' }, { to: '/app/parent/attendance', icon: 'bi bi-calendar-check', label: 'Attendance' }, { to: '/app/parent/calendar', icon: 'bi bi-calendar2-week', label: 'Calendar' }, { to: '/app/parent/progress', icon: 'bi bi-card-checklist', label: 'Class Progress' }, { to: '/app/parent/enroll-classes', icon: 'bi bi-pencil-square', label: 'Enroll in Classes' }, { to: '/app/parent/events', icon: 'bi bi-bell', label: 'Events' }, { to: '/app/parent/payment', icon: 'bi bi-credit-card', label: 'Invoice' }, { to: '/app/parent/register-student', icon: 'bi bi-person-plus', label: 'Register Students' }, { to: '/app/parent/report-attendance', icon: 'bi bi-megaphone', label: 'Report Absence' }, { to: '/app/parent/report-cards', icon: 'bi bi-file-earmark-text', label: 'Report Cards' }, { to: '/app/parent/scores', icon: 'bi bi-clipboard-data', label: 'Scores' }, ] as const /** Teacher top shortcuts (aligned with legacy teacher navbar). */ const teacherNavItems = [ { to: '/app/teacher_dashboard', icon: 'bi bi-house-door', label: 'Dashboard' }, { to: '/app/teacher/showupdate-attendance', icon: 'bi bi-calendar-check', label: 'Attendance' }, { to: '/app/teacher/inventory/book-distribute', icon: 'bi bi-book', label: 'Books' }, { to: '/app/teacher/calendar', icon: 'bi bi-calendar2-week', label: 'Calendar' }, { to: '/app/teacher/class-view', icon: 'bi bi-pencil-square', label: 'Class' }, { to: '/app/teacher/class-progress-submit', icon: 'bi bi-card-checklist', label: 'Class Progress' }, { to: '/app/teacher/competition-scores', icon: 'bi bi-trophy', label: 'Competitions' }, { to: '/app/teacher/exam-drafts', icon: 'bi bi-file-earmark-text', label: 'Exam Drafts' }, { to: '/app/teacher/print-requests', icon: 'bi bi-printer', label: 'Print' }, { to: '/app/student/score-card/list', icon: 'bi bi-card-text', label: 'Score Card' }, { to: '/app/teacher/scores', icon: 'bi bi-bar-chart-line', label: 'Scores' }, { to: '/app/teacher/absence-vacation', icon: 'bi bi-megaphone', label: 'TimeOff' }, ] as const function initialsFromName(name: string | undefined | null): string { const t = name?.trim() if (!t) return '?' const parts = t.split(/\s+/).filter(Boolean) if (parts.length >= 2) { const a = parts[0][0] ?? '' const b = parts[parts.length - 1][0] ?? '' return (a + b).toUpperCase() } if (parts[0].length >= 2) return parts[0].slice(0, 2).toUpperCase() return (parts[0][0] ?? '?').toUpperCase() } function formatRoleLabel(slug: string): string { const t = slug.trim() if (!t) return '' return t.replace(/_/g, ' ').replace(/\b\w/g, (letter) => letter.toUpperCase()) } export function MainLayout() { const { user, logout, selectedRole, roles } = useAuth() const navigate = useNavigate() const location = useLocation() const [navOpen, setNavOpen] = useState(false) const [portalStyle, setPortalStyle] = useState(readPortalStylePrefs) const [osPrefersDark, setOsPrefersDark] = useState(() => typeof window !== 'undefined' && window.matchMedia('(prefers-color-scheme: dark)').matches, ) const [userMenuOpen, setUserMenuOpen] = useState(false) const closeTimerRef = useRef | null>(null) const clusterRef = useRef(null) const userMenuTriggerRef = useRef(null) const clearCloseTimer = useCallback(() => { if (closeTimerRef.current) { clearTimeout(closeTimerRef.current) closeTimerRef.current = null } }, []) const openUserMenu = useCallback(() => { clearCloseTimer() setUserMenuOpen(true) }, [clearCloseTimer]) const closeUserMenu = useCallback(() => { clearCloseTimer() const root = clusterRef.current if (root?.contains(document.activeElement)) { userMenuTriggerRef.current?.focus() } setUserMenuOpen(false) }, [clearCloseTimer]) const scheduleCloseUserMenu = useCallback(() => { clearCloseTimer() closeTimerRef.current = setTimeout(() => { const root = clusterRef.current if (root?.contains(document.activeElement)) { closeTimerRef.current = null return } closeUserMenu() closeTimerRef.current = null }, 200) }, [clearCloseTimer, closeUserMenu]) useEffect(() => () => clearCloseTimer(), [clearCloseTimer]) const onClusterBlurCapture = useCallback(() => { window.setTimeout(() => { const root = clusterRef.current if (!root || !root.contains(document.activeElement)) { closeUserMenu() } }, 0) }, [closeUserMenu]) const onAvatarClick = useCallback(() => { if (typeof window === 'undefined') return if (window.matchMedia('(hover: none) and (pointer: coarse)').matches) { setUserMenuOpen((open) => !open) } }, []) const closeMenus = useCallback(() => { closeUserMenu() setNavOpen(false) }, [closeUserMenu]) useEffect(() => { const sync = () => setPortalStyle(readPortalStylePrefs()) window.addEventListener('alrahma-portal-style', sync) return () => window.removeEventListener('alrahma-portal-style', sync) }, []) useEffect(() => { const mq = window.matchMedia('(prefers-color-scheme: dark)') const onChange = () => setOsPrefersDark(mq.matches) mq.addEventListener('change', onChange) return () => mq.removeEventListener('change', onChange) }, []) const role = selectedRole ?? roles[0] ?? '' const canSwitchRole = roles.length > 1 const dashboardPath = isParentPortalRole(role) ? '/app/parent/home' : isTeacherPortalRole(role) ? '/app/teacher_dashboard' : '/app/home' const mainNavItems = isParentPortalRole(role) ? parentNavItems : isTeacherPortalRole(role) ? teacherNavItems : [] const initials = initialsFromName(user?.name) const resolvedColorMode = resolvePortalColorMode(portalStyle, osPrefersDark) return (
Al Rahma Sunday School logo
{user ? (
) : null} {user ? (
{canSwitchRole ? ( ) : null}
Signed in as
{user.name}
{role ? ( <>
Role
{formatRoleLabel(role)}
) : null}
{canSwitchRole ? ( Switch role ) : null} Style edit Settings
) : null}
) }