add parent pages

This commit is contained in:
root
2026-04-23 02:24:05 -04:00
parent 9191fd32f0
commit 94700d4f0f
30 changed files with 8179 additions and 634 deletions
+104
View File
@@ -0,0 +1,104 @@
import { useState } from 'react'
import { Link, NavLink, Outlet, useNavigate } from 'react-router-dom'
import { useAuth } from '../auth/AuthProvider'
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
export function MainLayout() {
const { user, logout, selectedRole, roles } = useAuth()
const navigate = useNavigate()
const [navOpen, setNavOpen] = useState(false)
const role = selectedRole ?? roles[0] ?? ''
const dashboardPath =
role === 'parent'
? '/app/parent/home'
: role === 'teacher' || role === 'teacher_assistant'
? '/app/teacher/dashboard'
: '/app/home'
return (
<div className="main-layout-app d-flex flex-column min-vh-100">
<header className="navbar navbar-expand-lg custom-navbar sticky-top shadow-sm px-3">
<div className="container-fluid">
<Link className="navbar-brand fw-semibold d-inline-flex align-items-center gap-2" to={dashboardPath}>
<img className="main-layout-logo" src="/logo.png" alt="Al Rahma Sunday School logo" />
Al Rahma Sunday School
</Link>
<button
className="navbar-toggler"
type="button"
aria-controls="mainLayoutNavbar"
aria-expanded={navOpen}
aria-label="Toggle navigation"
onClick={() => setNavOpen((open) => !open)}
>
<span className="navbar-toggler-icon" />
</button>
<div id="mainLayoutNavbar" className={`collapse navbar-collapse ${navOpen ? 'show' : ''}`}>
<nav className="ms-lg-auto me-lg-3 my-3 my-lg-0 main-layout-menu parent-navbar-menu" aria-label="Parent navigation">
{parentNavItems.map((item) => (
<NavLink
key={item.to}
to={item.to}
className={({ isActive }) => `nav-link parent-navbar-link ${isActive ? 'active fw-semibold' : ''}`}
onClick={() => setNavOpen(false)}
>
<i className={item.icon} aria-hidden />
<span>{item.label}</span>
</NavLink>
))}
</nav>
<div className="d-flex align-items-center gap-2">
<span className="navbar-text small">{user?.name}</span>
<button
type="button"
className="btn btn-outline-light btn-sm"
onClick={() => {
logout()
navigate('/login', { replace: true })
}}
>
Log out
</button>
</div>
</div>
</div>
</header>
<main className="container mt-4 flex-grow-1">
<Outlet />
</main>
<footer className="footer py-4 mt-auto border-top">
<div className="container text-muted small d-flex flex-wrap justify-content-between gap-2">
<span>Al Rahma Sunday School</span>
<span>Parent and Teacher Portal</span>
</div>
</footer>
<div className="modal fade" id="familyCardModal" tabIndex={-1} aria-hidden="true">
<div className="modal-dialog modal-xl modal-dialog-scrollable">
<div className="modal-content">
<div className="modal-header">
<h5 className="modal-title">Family</h5>
<button type="button" className="btn-close" data-bs-dismiss="modal" aria-label="Close" />
</div>
<div className="modal-body p-0" id="familyCardContent" />
</div>
</div>
</div>
</div>
)
}
+66 -16
View File
@@ -50,7 +50,7 @@ function NavBranch({ item, depth }: { item: NavItem; depth: number }) {
const linkInner = (
<>
{icon}
{label}
<span className="nav-label-chip">{label}</span>
</>
)
@@ -90,11 +90,11 @@ function NavBranch({ item, depth }: { item: NavItem; depth: number }) {
<li>
{main}
{children.length > 0 ? (
<div className="mt-1">
<ul className="nav flex-column gap-1 ps-0 mb-0 mt-1 list-unstyled">
{children.map((ch) => (
<NavBranch key={ch.id} item={ch} depth={depth + 1} />
))}
</div>
</ul>
) : null}
</li>
)
@@ -105,6 +105,7 @@ export function ManagementLayout() {
const navigate = useNavigate()
const [items, setItems] = useState<NavItem[]>([])
const [menuError, setMenuError] = useState<string | null>(null)
const [sidebarOpen, setSidebarOpen] = useState(false)
useEffect(() => {
let cancelled = false
@@ -129,8 +130,8 @@ export function ManagementLayout() {
}, [])
return (
<div className="management-app d-flex flex-column min-vh-100">
<header className="navbar navbar-dark border-bottom shadow-sm px-3 py-2">
<div className={`management-app d-flex flex-column min-vh-100 ${sidebarOpen ? 'mgmt-sidebar-open' : ''}`}>
<header className="navbar navbar-dark sticky-top border-bottom shadow-sm px-3 py-2">
<div className="container-fluid px-2 d-flex flex-wrap align-items-center justify-content-between gap-2">
<Link className="navbar-brand fw-semibold mb-0" to="/app/home">
Al Rahma Sunday School
@@ -151,12 +152,31 @@ export function ManagementLayout() {
</div>
</header>
<div className="container-fluid flex-grow-1 py-3">
<div className="row g-3">
<aside className="col-12 col-md-3 col-xl-2">
<div className="border rounded bg-white shadow-sm">
<div className="p-3 small text-muted border-bottom">Navigation</div>
<nav className="p-2 pb-3">
<button
id="mgmtSidebarToggle"
className="mgmt-sidebar-toggle"
type="button"
aria-label="Toggle sidebar"
aria-expanded={sidebarOpen}
onClick={() => setSidebarOpen((open) => !open)}
>
<i className="bi bi-chevron-right" aria-hidden />
</button>
{sidebarOpen ? (
<button
className="mgmt-sidebar-backdrop"
type="button"
aria-label="Close navigation"
onClick={() => setSidebarOpen(false)}
/>
) : null}
<div className="wrapper flex-grow-1">
<aside id="navbarManagement" className="mgmt-sidebar" data-mgmt-menu-mode="dark">
<div className="mgmt-sidebar-content">
<div className="px-2 py-2 small text-muted border-bottom mb-2">Navigation</div>
<nav className="pb-3">
<NavLink
to="/app/home"
className={({ isActive }) =>
@@ -164,7 +184,7 @@ export function ManagementLayout() {
}
>
<i className="bi bi-house-door me-2" aria-hidden />
Home
<span className="nav-label-chip">Home</span>
</NavLink>
<NavLink
to="/app/browse"
@@ -173,7 +193,7 @@ export function ManagementLayout() {
}
>
<i className="bi bi-diagram-3 me-2" aria-hidden />
CI views
<span className="nav-label-chip">CI views</span>
</NavLink>
{menuError ? (
<div className="alert alert-warning small py-2">{menuError}</div>
@@ -182,11 +202,41 @@ export function ManagementLayout() {
)}
</nav>
</div>
</aside>
</aside>
<main className="col-12 col-md-9 col-xl-10 management-main">
<main className="content management-main">
<Outlet />
</main>
</main>
</div>
<footer className="footer-back border-top bg-white py-3 mt-auto">
<div className="container-fluid px-4 text-muted small d-flex flex-wrap justify-content-between gap-2">
<span>Al Rahma Sunday School</span>
<span>Management Portal</span>
</div>
</footer>
<div className="modal fade" id="familyCardModal" tabIndex={-1} aria-hidden="true">
<div className="modal-dialog modal-xl modal-dialog-scrollable">
<div className="modal-content">
<div className="modal-header">
<h5 className="modal-title">Family</h5>
<button type="button" className="btn-close" data-bs-dismiss="modal" aria-label="Close" />
</div>
<div className="modal-body p-0" id="familyCardContent" />
</div>
</div>
</div>
<div className="modal fade" id="paymentsModal" tabIndex={-1} aria-hidden="true">
<div className="modal-dialog modal-lg modal-dialog-scrollable">
<div className="modal-content">
<div className="modal-header">
<h5 className="modal-title">Payments</h5>
<button type="button" className="btn-close" data-bs-dismiss="modal" aria-label="Close" />
</div>
<div className="modal-body p-0" id="paymentsModalContent" />
</div>
</div>
</div>
</div>