fix teacher, parent and admin pages
This commit is contained in:
+403
-57
@@ -1,78 +1,424 @@
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { fetchAdministratorDashboardMetrics } from '../api/session'
|
||||
import type { AdministratorDashboardMetricsResponse, AuthUser } from '../api/types'
|
||||
import { useAuth } from '../auth/AuthProvider'
|
||||
import './HomePage.css'
|
||||
|
||||
/** Public home must not call administrator APIs unless the user is allowed (avoids 401 for teachers/guests). */
|
||||
function userMayLoadAdministratorHomeMetrics(user: AuthUser | null): boolean {
|
||||
if (!user?.roles) return false
|
||||
const r = user.roles
|
||||
return Boolean(r.administrator || r.admin || r.super_admin)
|
||||
}
|
||||
|
||||
/** Carousel + sections ported from CodeIgniter `Views/index.php` (Al Rahma public home). */
|
||||
const CAROUSEL_SLIDES = [
|
||||
{
|
||||
image: '/images/carousel-1.png',
|
||||
alt: 'Not only a book to be read but a guide illuminating our lives.',
|
||||
title: 'Not only a book to be read but a guide illuminating our lives.',
|
||||
body:
|
||||
'At Al Rahma Sunday School, we teach the Quran as a living guide, nurturing love and respect for the words of Allah (SWT). Our program supports each child’s journey through recitation, memorization, and age-appropriate translation and understanding.',
|
||||
},
|
||||
{
|
||||
image: '/images/carousel-0.png',
|
||||
alt: 'Faith and Knowledge Hand in Hand',
|
||||
title: 'Faith and Knowledge Hand in Hand',
|
||||
body:
|
||||
'Our Sunday School program (Grades 1-9, Youth) is evenly divided between Islamic Studies in English—exploring topics about Allah, Islamic values, Muslim life and lessons from the Prophets—and Quran/Arabic studies focused on recitation, memorization and language. This balanced approach nurtures both understanding and practice of Islam.',
|
||||
},
|
||||
{
|
||||
image: '/images/carousel-3.png',
|
||||
alt: 'Nurturing Hearts and Minds',
|
||||
title: 'Nurturing Hearts and Minds',
|
||||
body:
|
||||
'At our school, we inspire a lifelong love for Islamic values, for our worship rituals and our prophets through engaging lessons, interactive activities, and a supportive environment. Every student grows in knowledge, faith and character, learning to live the teachings of Islam in everyday life.',
|
||||
},
|
||||
{
|
||||
image: '/images/carousel-4.png',
|
||||
alt: 'Exciting Fun, Inside and Out!',
|
||||
title: 'Exciting Fun, Inside and Out!',
|
||||
body:
|
||||
'From festive Eid celebrations to adventurous outdoor outings, our school events bring learning, laughter, and unforgettable memories together.',
|
||||
},
|
||||
{
|
||||
image: '/images/carousel-5.png',
|
||||
alt: 'Strengthening Faith, Character, and Community Bonds',
|
||||
title: 'Strengthening Faith, Character, and Community Bonds',
|
||||
body:
|
||||
'A warm, faith-centered community helps children thrive, build confidence, compassion, and friendships.',
|
||||
},
|
||||
] as const
|
||||
|
||||
const STAT_DEF = [
|
||||
{ key: 'students', label: 'Students', circle: 'circle-primary', icon: 'bi-mortarboard' },
|
||||
{ key: 'teachers', label: 'Teachers', circle: 'circle-info', icon: 'bi-person-video3' },
|
||||
{ key: 'teacherAssistants', label: 'TAs', circle: 'circle-success', icon: 'bi-person-gear' },
|
||||
{ key: 'admins', label: 'Admins', circle: 'circle-warning', icon: 'bi-shield-check' },
|
||||
{ key: 'parents', label: 'Parents', circle: 'circle-light', icon: 'bi-people' },
|
||||
] as const
|
||||
|
||||
type CountKey = (typeof STAT_DEF)[number]['key']
|
||||
|
||||
function formatStat(
|
||||
n: number | undefined,
|
||||
numberFmt: Intl.NumberFormat,
|
||||
): string {
|
||||
return typeof n === 'number' && Number.isFinite(n) ? numberFmt.format(n) : '—'
|
||||
}
|
||||
|
||||
/** Public landing — structure inspired by CodeIgniter `Views/landing_page/guest_dashboard.php` hero. */
|
||||
export function HomePage() {
|
||||
const { isAuthenticated } = useAuth()
|
||||
const { token, user } = useAuth()
|
||||
const numberFmt = useMemo(() => new Intl.NumberFormat(), [])
|
||||
const [counts, setCounts] = useState<AdministratorDashboardMetricsResponse['counts']>({})
|
||||
const [showTop, setShowTop] = useState(false)
|
||||
|
||||
const mayLoadMetrics = Boolean(token && userMayLoadAdministratorHomeMetrics(user))
|
||||
|
||||
useEffect(() => {
|
||||
if (!mayLoadMetrics) {
|
||||
setCounts({})
|
||||
return
|
||||
}
|
||||
|
||||
let cancelled = false
|
||||
const loadStats = async () => {
|
||||
try {
|
||||
const data = await fetchAdministratorDashboardMetrics()
|
||||
if (!cancelled) setCounts(data.counts ?? {})
|
||||
} catch {
|
||||
if (!cancelled) setCounts({})
|
||||
}
|
||||
}
|
||||
void loadStats()
|
||||
const interval = window.setInterval(() => void loadStats(), 60_000)
|
||||
const onVisibility = () => {
|
||||
if (document.visibilityState === 'visible') void loadStats()
|
||||
}
|
||||
document.addEventListener('visibilitychange', onVisibility)
|
||||
return () => {
|
||||
cancelled = true
|
||||
window.clearInterval(interval)
|
||||
document.removeEventListener('visibilitychange', onVisibility)
|
||||
}
|
||||
}, [mayLoadMetrics])
|
||||
|
||||
useEffect(() => {
|
||||
const onScroll = () => setShowTop(window.scrollY > 100)
|
||||
window.addEventListener('scroll', onScroll, { passive: true })
|
||||
onScroll()
|
||||
return () => window.removeEventListener('scroll', onScroll)
|
||||
}, [])
|
||||
|
||||
function countFor(key: CountKey): string {
|
||||
return formatStat(counts?.[key], numberFmt)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex-grow-1">
|
||||
<section className="hero-section position-relative overflow-hidden bg-white">
|
||||
<div className="welcome-message text-center py-5 px-3">
|
||||
<h1 className="display-5 fw-semibold text-success mb-2">
|
||||
Welcome to Al Rahma Sunday School
|
||||
</h1>
|
||||
<h2 className="h4 text-secondary mb-3">
|
||||
{isAuthenticated ? 'Welcome back' : 'Welcome, Guest!'}
|
||||
</h2>
|
||||
<p className="lead text-muted mx-auto mb-4" style={{ maxWidth: 640 }}>
|
||||
Use the portal to manage enrollment, attendance, grades, and family information — the
|
||||
same workflows as the CodeIgniter site, backed by the Laravel API.
|
||||
</p>
|
||||
<div className="d-flex flex-wrap justify-content-center gap-2">
|
||||
{isAuthenticated ? (
|
||||
<Link className="btn btn-success btn-lg px-4" to="/app/home">
|
||||
Go to dashboard
|
||||
</Link>
|
||||
) : (
|
||||
<>
|
||||
<Link className="btn btn-success btn-lg px-4" to="/login">
|
||||
Sign in
|
||||
</Link>
|
||||
<Link className="btn btn-outline-success btn-lg px-4" to="/register">
|
||||
Registration Form
|
||||
</Link>
|
||||
</>
|
||||
)}
|
||||
<Link className="btn btn-outline-secondary btn-lg" to="/docs">
|
||||
API documentation
|
||||
</Link>
|
||||
</div>
|
||||
<div className="home-landing flex-grow-1">
|
||||
<div
|
||||
id="homeMainCarousel"
|
||||
className="carousel slide carousel-fade home-carousel"
|
||||
data-bs-ride="carousel"
|
||||
>
|
||||
<div className="carousel-indicators">
|
||||
{CAROUSEL_SLIDES.map((_, i) => (
|
||||
<button
|
||||
key={i}
|
||||
type="button"
|
||||
data-bs-target="#homeMainCarousel"
|
||||
data-bs-slide-to={i}
|
||||
className={i === 0 ? 'active' : undefined}
|
||||
aria-current={i === 0 ? 'true' : undefined}
|
||||
aria-label={`Slide ${i + 1}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="carousel-inner">
|
||||
{CAROUSEL_SLIDES.map((slide, i) => (
|
||||
<div key={slide.image} className={`carousel-item${i === 0 ? ' active' : ''}`}>
|
||||
<img src={slide.image} className="d-block w-100" alt={slide.alt} />
|
||||
<div className="carousel-caption">
|
||||
<h2 className="mb-3">{slide.title}</h2>
|
||||
<p className="mb-0">{slide.body}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<button
|
||||
className="carousel-control-prev"
|
||||
type="button"
|
||||
data-bs-target="#homeMainCarousel"
|
||||
data-bs-slide="prev"
|
||||
>
|
||||
<span className="carousel-control-prev-icon" aria-hidden />
|
||||
<span className="visually-hidden">Previous</span>
|
||||
</button>
|
||||
<button
|
||||
className="carousel-control-next"
|
||||
type="button"
|
||||
data-bs-target="#homeMainCarousel"
|
||||
data-bs-slide="next"
|
||||
>
|
||||
<span className="carousel-control-next-icon" aria-hidden />
|
||||
<span className="visually-hidden">Next</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<section className="text-center my-3">
|
||||
<a
|
||||
href="/account_creation_guide.pdf"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-decoration-none"
|
||||
style={{ fontWeight: 900, color: '#000' }}
|
||||
>
|
||||
<h3 className="fw-bold my-0">
|
||||
<span className="text-decoration-underline">
|
||||
Join Al Rahma family today! Click here for a quick guide on how to create an account.
|
||||
</span>
|
||||
</h3>
|
||||
</a>
|
||||
</section>
|
||||
|
||||
<section className="py-5 border-top bg-light">
|
||||
<div className="container-xxl py-3 home-section-xl content-section">
|
||||
<div className="container">
|
||||
<div className="row g-4 justify-content-center">
|
||||
<div className="col-md-4">
|
||||
<div className="card border-0 shadow-sm h-100">
|
||||
<div className="card-body">
|
||||
<h3 className="h6 text-success">
|
||||
<i className="bi bi-house-door me-2" aria-hidden />
|
||||
Families
|
||||
</h3>
|
||||
<p className="small text-muted mb-0">
|
||||
Parents sign in after registration is activated by email.
|
||||
</p>
|
||||
<div className="home-stats">
|
||||
<h2 className="d-flex justify-content-center p-md-1 fs-3 fw-bold green-title mb-0">
|
||||
Active Participants
|
||||
</h2>
|
||||
<br />
|
||||
<div className="stats-row">
|
||||
{STAT_DEF.map((s) => (
|
||||
<div key={s.key} className={`stat-circle ${s.circle}`}>
|
||||
<div className="stat-icon">
|
||||
<i className={`bi ${s.icon}`} aria-hidden />
|
||||
</div>
|
||||
<div className="stat-value">{countFor(s.key)}</div>
|
||||
<div className="stat-title">{s.label}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="container-xxl py-3 home-section-xl content-section">
|
||||
<div className="container">
|
||||
<div className="row bg-light squared align-items-center g-0">
|
||||
<div className="col-lg-6">
|
||||
<div className="d-flex flex-column justify-content-center p-4 p-md-5">
|
||||
<h2 className="mb-4 fs-2 fw-bold green-title">
|
||||
Faith, Education and Community for Over 30 Years
|
||||
</h2>
|
||||
<p className="mb-4">
|
||||
The Islamic Society of Greater Lowell (ISGL), established in 1993 in Chelmsford,
|
||||
Massachusetts, serves as both a mosque and a vibrant community center. It provides a
|
||||
place of worship for Allah (SWT), offers Islamic education and facilitates religious
|
||||
and social activities for Muslims in the Merrimack Valley. As a non-profit,
|
||||
charitable, and tax-exempt organization, ISGL also provides essential services such as
|
||||
marriage ceremonies, burial arrangements and community support while promoting a
|
||||
greater understanding of Islam in America.
|
||||
</p>
|
||||
<p className="mb-4">
|
||||
Under the umbrella of ISGL, Al Rahma Sunday School has been serving the community for
|
||||
over thirty years. Throughout the decades, it has provided students with a strong
|
||||
foundation in Islamic Studies and Quran, fostering both knowledge and character. With
|
||||
its dedicated teachers and well-rounded academic program, the school continues to
|
||||
guide generations of Muslim youth in their faith, values and practice of Islam.
|
||||
</p>
|
||||
<p>For more information, click below to visit ISGL official website.</p>
|
||||
<div>
|
||||
<a
|
||||
className="btn btn-home-cta px-4 py-2 fw-semibold"
|
||||
href="https://isgl.org"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
ISGL Official Website
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-4">
|
||||
<div className="card border-0 shadow-sm h-100">
|
||||
<div className="card-body">
|
||||
<h3 className="h6 text-success">
|
||||
<i className="bi bi-shield-lock me-2" aria-hidden />
|
||||
Secure portal
|
||||
</h3>
|
||||
<p className="small text-muted mb-0">
|
||||
Sessions use JWT from the Laravel API (<code>/api/v1/auth/login</code>).
|
||||
</p>
|
||||
<div className="col-lg-6 image-column pe-lg-3">
|
||||
<div className="position-relative h-100 w-100">
|
||||
<img
|
||||
className="w-100 h-100 rounded-end"
|
||||
src="/images/isgl_home.png"
|
||||
alt="ISGL"
|
||||
style={{ objectFit: 'cover', minHeight: 280 }}
|
||||
onError={(e) => {
|
||||
const img = e.currentTarget
|
||||
img.onerror = null
|
||||
img.src = '/images/call-to-action.jpg'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="container-xxl py-3 home-section-xl content-section">
|
||||
<div className="container">
|
||||
<div className="row bg-light squared align-items-center g-0">
|
||||
<div className="col-lg-6">
|
||||
<div className="d-flex flex-column justify-content-center p-4 p-md-5">
|
||||
<h2 className="mb-4 fs-2 fw-bold green-title">Our Mission</h2>
|
||||
<p>
|
||||
Al Rahma Sunday School's mission is to make the Masjid a central part of our
|
||||
Muslim children's lives from a very young age. We are dedicated to offering a
|
||||
strong, well-rounded curriculum in Quran, Arabic and Islamic Studies. Our goal is to
|
||||
deliver the highest quality education to help instill Islamic values, beliefs and
|
||||
practices in each student. Additionally, we regularly organize engaging and fun events
|
||||
throughout the year to make the learning experience more enjoyable and spiritually
|
||||
enriching.
|
||||
</p>
|
||||
<p className="mb-0">
|
||||
We deeply appreciate your continued support and cooperation in helping us fulfill this
|
||||
mission. Together, we can provide a strong Islamic foundation for our children and shape
|
||||
a future generation that is confident in their faith and rooted in Islamic values.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-lg-6 about-img py-4 py-lg-0">
|
||||
<div className="row g-2 justify-content-center">
|
||||
<div className="col-12 text-center">
|
||||
<img
|
||||
className="img-fluid w-75 rounded-circle bg-light p-3"
|
||||
src="/images/about-1.jpg"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
<div className="col-6 text-start" style={{ marginTop: '-150px' }}>
|
||||
<img
|
||||
className="img-fluid w-100 rounded-circle bg-light p-3"
|
||||
src="/images/about-2.jpg"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
<div className="col-6 text-end" style={{ marginTop: '-150px' }}>
|
||||
<img
|
||||
className="img-fluid w-100 rounded-circle bg-light p-3"
|
||||
src="/images/about-3.jpg"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div className="container-xxl py-3 home-section-xl content-section">
|
||||
<div className="container">
|
||||
<div className="row bg-light squared align-items-center g-0">
|
||||
<div className="col-lg-6">
|
||||
<div className="d-flex flex-column justify-content-center p-4 p-md-5">
|
||||
<h2 className="mb-4 fs-2 fw-bold green-title">Become A Teacher or Admin</h2>
|
||||
<p className="mb-4">
|
||||
Volunteering at Al Rahma Sunday School is a deeply fulfilling way to give back to the
|
||||
community and help shape the next generation. Whether you're passionate about
|
||||
teaching or prefer working behind the scenes, your time and dedication can make a
|
||||
lasting difference. Join a team committed to nurturing young minds and strengthening
|
||||
their connection to faith.
|
||||
</p>
|
||||
<p className="mb-4">
|
||||
As a volunteer teacher or teacher assistant, you'll have the opportunity to
|
||||
inspire, educate, and guide children on their spiritual journey. Teachers play a vital
|
||||
role in helping students understand the Quran, Islamic values, and the Arabic language
|
||||
in a warm and engaging classroom environment. Teacher assistants support instruction,
|
||||
help manage classroom activities, and provide one-on-one assistance to students when
|
||||
needed. Whether leading the class or lending a helping hand, your commitment can
|
||||
spark a lifelong love for learning and faith in the hearts of our students.
|
||||
</p>
|
||||
<p className="mb-4">
|
||||
Administrative volunteers are essential to the success of our school. From organizing
|
||||
materials and managing communications to helping coordinate school events and
|
||||
logistics, your work ensures a smooth and efficient learning experience for students
|
||||
and teachers alike. If you enjoy structured tasks, planning, or simply being helpful
|
||||
in a quiet but powerful way, this role is perfect for you.
|
||||
</p>
|
||||
<div>
|
||||
<Link className="btn btn-home-cta py-3 px-5 mt-1" to="/register">
|
||||
Get Started Now <i className="bi bi-arrow-right ms-2" aria-hidden />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-lg-6 image-column pe-lg-3">
|
||||
<div className="position-relative h-100 w-100">
|
||||
<img
|
||||
className="w-100 h-100 rounded-end"
|
||||
src="/images/call-to-action.jpg"
|
||||
alt=""
|
||||
style={{ objectFit: 'cover', minHeight: 280 }}
|
||||
onError={(e) => {
|
||||
const img = e.currentTarget
|
||||
img.onerror = null
|
||||
img.src = '/images/isgl_home.png'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="container-xxl py-3 home-section-xl content-section">
|
||||
<div className="container">
|
||||
<div className="row bg-light squared position-relative align-items-center g-0">
|
||||
<div className="col-lg-6">
|
||||
<div className="h-100 d-flex flex-column justify-content-center p-4 p-md-5">
|
||||
<h2 className="mb-4 fs-2 fw-bold green-title">Our Curriculum and Grade Structure</h2>
|
||||
<p>
|
||||
Our program spans nine structured grade levels, each building upon the previous
|
||||
year's knowledge to ensure a solid foundation in faith, character, and Islamic
|
||||
learning.
|
||||
</p>
|
||||
<p>
|
||||
Upon completing the 9th grade, students transition into our three-year Youth Program,
|
||||
which emphasizes deeper community engagement, personal development and practical
|
||||
application of Islamic principles. Participation in the Youth Program requires students
|
||||
to be at least 15 years old, ensuring they are mature enough to benefit from its
|
||||
advanced content.
|
||||
</p>
|
||||
<p>
|
||||
Starting this academic year, children must be at least 6 years old by 12-31-2025 to
|
||||
enroll in Grade 1. For younger learners, we are pleased to offer our newly established
|
||||
Kindergarten class, welcoming children who are at least 5 years old by 12-31-2025. This
|
||||
early education program is designed to introduce young minds to the basics of Islamic
|
||||
teachings in a warm, age-appropriate environment.
|
||||
</p>
|
||||
<div>
|
||||
<Link className="btn btn-home-cta py-3 px-5 mt-3" to="/register">
|
||||
Get Started Now <i className="bi bi-arrow-right ms-2" aria-hidden />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-lg-6 text-start py-4 py-lg-0">
|
||||
<div className="image-container mx-auto mx-lg-0">
|
||||
<img
|
||||
src="/images/12.jpeg"
|
||||
className="overlapping-image image1"
|
||||
alt="Students and activities"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className={`btn btn-home-cta btn-lg rounded-1 back-to-top${showTop ? ' visible' : ''}`}
|
||||
aria-label="Back to top"
|
||||
onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })}
|
||||
>
|
||||
<i className="bi bi-arrow-up" aria-hidden />
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user