import { useCallback, useEffect, useMemo, useState } from 'react' import { Link, useSearchParams } from 'react-router-dom' import { ApiHttpError } from '../../api/http' import { fetchClassPrepIndex, saveClassPrepAdjustment } from '../../api/session' import type { ClassPrepIndexResponse, ClassPrepSectionRow } from '../../api/types' function adjustQty(input: HTMLInputElement, delta: number) { const curr = parseInt(input.value, 10) || 0 input.value = String(curr + delta) } function defaultSchoolYear() { const y = new Date().getFullYear() return `${y}-${y + 1}` } export function ClassPrepListPage() { const [searchParams, setSearchParams] = useSearchParams() const schoolYear = searchParams.get('school_year') ?? defaultSchoolYear() const semester = searchParams.get('semester') ?? '' const [data, setData] = useState(null) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [savingId, setSavingId] = useState(null) const [saveMsg, setSaveMsg] = useState(null) const yearOptions = useMemo(() => { const out: string[] = [] for (let y = new Date().getFullYear(); y >= 2020; y--) out.push(`${y}-${y + 1}`) return out }, []) useEffect(() => { let cancelled = false setLoading(true) setError(null) fetchClassPrepIndex({ school_year: schoolYear, semester: semester || undefined, }) .then((d) => { if (!cancelled) setData(d) }) .catch((e: unknown) => { if (!cancelled) setError(e instanceof ApiHttpError ? e.message : 'Failed to load class preparation data.') }) .finally(() => { if (!cancelled) setLoading(false) }) return () => { cancelled = true } }, [schoolYear, semester]) const onFilterSubmit = useCallback( (e: React.FormEvent) => { e.preventDefault() const fd = new FormData(e.currentTarget) const sy = String(fd.get('school_year') ?? '') const sem = String(fd.get('semester') ?? '') const next = new URLSearchParams() if (sy) next.set('school_year', sy) if (sem) next.set('semester', sem) setSearchParams(next) }, [setSearchParams], ) async function onSaveAdjustment(prep: ClassPrepSectionRow, form: HTMLFormElement) { setSavingId(String(prep.class_section_id)) setSaveMsg(null) const fd = new FormData(form) const adjustments: Record = {} fd.forEach((v, k) => { if (k.startsWith('adj__')) { const name = k.slice(5) adjustments[name] = parseInt(String(v), 10) || 0 } }) try { await saveClassPrepAdjustment({ class_section_id: prep.class_section_id, school_year: schoolYear, semester: semester || undefined, adjustments, }) setSaveMsg('Saved.') const fresh = await fetchClassPrepIndex({ school_year: schoolYear, semester: semester || undefined, }) setData(fresh) } catch (e: unknown) { setSaveMsg(e instanceof ApiHttpError ? e.message : 'Save failed.') } finally { setSavingId(null) } } const printBase = `/app/administrator/class-prep/print` const semQuery = semester ? `?semester=${encodeURIComponent(semester)}` : '' return (

Class Preparation

{loading ? (

Loading…

) : error ? (
{error}
) : null} {saveMsg ? (
{saveMsg}
) : null}
{(data?.prep_results ?? []).map((prep) => { const adjMap = prep.adjustments ?? {} return (
{prep.class_section}
{prep.needs_print ? ( Updated since last print ) : ( Up-to-date )} {prep.last_printed_at ? ( Last printed: {prep.last_printed_at} ) : null}
Students:
{prep.student_count}
Prep Items
    {Object.entries(prep.prep_items ?? {}).map(([item, qty]) => (
  • {item} 0 ? 'bg-primary' : 'bg-secondary' }`} > {Number(qty)}
  • ))}
Adjustments (Tables)
{ e.preventDefault() void onSaveAdjustment(prep, e.currentTarget) }} > {Object.entries(adjMap).map(([item, value]) => (
))}
Print
) })}
Totals for All Used Items
{Object.entries(data?.total_needed ?? {}).map(([item, needed]) => { const have = Number((data?.available ?? {})[item] ?? 0) const short = Math.max(0, Number(needed) - have) return ( 0 ? 'table-warning' : ''}> ) })}
Item Total Needed In Inventory Short By
{item} {Number(needed)} {have} 0 ? 'text-danger fw-bold' : 'text-success'}`} > {short}
{data?.shortages && Object.keys(data.shortages).length > 0 ? (
Shortages Detected
    {Object.entries(data.shortages).map(([item, short]) => (
  • {item}: short by {Number(short)} pcs
  • ))}
) : data && Object.keys(data.total_needed ?? {}).length > 0 ? (
All items are available in stock.
) : null}
) }