import { type FormEvent, useEffect, useMemo, useState } from 'react' import { Link, useSearchParams } from 'react-router-dom' import { useSchoolYearOptions } from '../../hooks/useSchoolYearOptions' import { fetchTeacherStaffAttendance, saveTeacherStaffAttendance } from '../../api/session' import { TEACHER_ATTENDANCE_MONTH_PATH } from './attendancePaths' import type { TeacherStaffAttendanceRow } from '../../api/types' function formatUsDate(ymd: string): string { if (!/^\d{4}-\d{2}-\d{2}$/.test(ymd)) return ymd const [y, m, d] = ymd.split('-') return `${m}-${d}-${y}` } /** Port of `Views/attendance/teacher_attendance_form.php` */ export function TeacherAttendanceFormPage() { const [searchParams, setSearchParams] = useSearchParams() const schoolYear = searchParams.get('school_year') ?? '' const semester = searchParams.get('semester') ?? '' const date = searchParams.get('date') ?? new Date().toISOString().slice(0, 10) const classSectionId = Number(searchParams.get('class_section_id') ?? '0') const [sections, setSections] = useState>({}) const [grid, setGrid] = useState([]) const [locked, setLocked] = useState(false) const [loading, setLoading] = useState(true) const [saving, setSaving] = useState(false) const [message, setMessage] = useState(null) const [error, setError] = useState(null) const { options, selectedYear } = useSchoolYearOptions({ preferredYear: schoolYear, }) useEffect(() => { let cancelled = false ;(async () => { setLoading(true) setError(null) try { const data = await fetchTeacherStaffAttendance({ date, semester: semester || null, schoolYear: schoolYear || null, classSectionId: classSectionId || null, }) if (cancelled) return setSections(data.sections ?? {}) setGrid(data.grid ?? []) setLocked(Boolean(data.locked)) } catch (e) { if (!cancelled) setError(e instanceof Error ? e.message : 'Unable to load teacher attendance.') } finally { if (!cancelled) setLoading(false) } })() return () => { cancelled = true } }, [date, semester, schoolYear, classSectionId]) const dateLabel = useMemo(() => formatUsDate(date), [date]) function setRow(tid: number, patch: Partial) { setGrid((prev) => prev.map((r) => ((r.teacher_id ?? 0) === tid ? { ...r, ...patch } : r)), ) } function markAllPresent() { setGrid((prev) => prev.map((r) => ({ ...r, status: 'present' }))) } function clearAll() { setGrid((prev) => prev.map((r) => ({ ...r, status: '', reason: '' }))) } async function onSubmit(e: FormEvent) { e.preventDefault() if (!classSectionId) return setSaving(true) setMessage(null) setError(null) try { const teachers: Record = {} for (const r of grid) { const tid = r.teacher_id ?? 0 if (!tid) continue teachers[String(tid)] = { status: r.status ?? '', reason: r.reason ?? '', } } await saveTeacherStaffAttendance({ date, semester, school_year: schoolYear, class_section_id: classSectionId, teachers, }) setMessage('Saved.') } catch (err) { setError(err instanceof Error ? err.message : 'Save failed.') } finally { setSaving(false) } } function applyFilters(e: FormEvent) { e.preventDefault() const fd = new FormData(e.currentTarget) const p = new URLSearchParams() const sy = String(fd.get('school_year') ?? '') const sem = String(fd.get('semester') ?? '') const dt = String(fd.get('date') ?? '') const cs = String(fd.get('class_section_id') ?? '0') if (sy) p.set('school_year', sy) if (sem) p.set('semester', sem) if (dt) p.set('date', dt) if (cs && cs !== '0') p.set('class_section_id', cs) setSearchParams(p) } const sectionEntries = Object.entries(sections) return (

Teacher Attendance

← Back to Monthly Attendance
{message ?
{message}
: null} {error ?
{error}
: null}
{classSectionId ? ( ) : null}
{!classSectionId ? null : ( <> {locked ? (
Note: Student/section attendance for this day appears to be finalized. Admin edits to teacher attendance here are allowed and will not unlock the section.
) : null}
Teachers on {dateLabel}
{loading ? (

Loading…

) : grid.length === 0 ? (

No teachers found for this section/term.

) : ( {grid.map((row, idx) => { const tid = row.teacher_id ?? 0 const name = `${row.firstname ?? ''} ${row.lastname ?? ''}`.trim() || `User#${tid}` const pos = String(row.position ?? '').toLowerCase() const stat = String(row.status ?? '').toLowerCase() return ( ) })}
# Name Role Status Reason
{idx + 1} {name} {pos === 'main' ? 'Main Teacher' : 'Assistant'} setRow(tid, { reason: ev.target.value })} />
)}
)}
) }