import { useEffect, useMemo, useState } from 'react' import { useNavigate, useParams } from 'react-router-dom' import { fetchAdminCompetitionWinnerCreate, fetchAdminCompetitionWinnerSettings, saveAdminCompetitionWinner, } from '../../api/session' import type { CompetitionWinnerAdminClassRow, CompetitionWinnerAdminFormResponse } from '../../api/types' import { ApiScopeNote, CompetitionPageShell, COMPETITION_WINNERS_BASE } from './competitionWinnersShared' type CompetitionFormState = { title: string class_section_id: string semester: string school_year: string start_date: string end_date: string } function numberOrNull(value: string): number | null { const trimmed = value.trim() if (trimmed === '') return null const parsed = Number(trimmed) return Number.isFinite(parsed) ? parsed : null } function initialFormState(payload: CompetitionWinnerAdminFormResponse): CompetitionFormState { const competition = payload.competition return { title: String(competition?.title ?? ''), class_section_id: competition?.class_section_id === null || competition?.class_section_id === undefined ? '' : String(competition.class_section_id), semester: String(competition?.semester ?? payload.defaultSemester ?? ''), school_year: String(competition?.school_year ?? payload.defaultSchoolYear ?? ''), start_date: String(competition?.start_date ?? ''), end_date: String(competition?.end_date ?? ''), } } function normalizeRowValue(value: number | string | null | undefined): string { return value === null || value === undefined ? '' : String(value) } /** CI `admin/competition_winners/form.php` */ export function CompetitionWinnerFormPage() { const { id } = useParams() const navigate = useNavigate() const isEdit = Boolean(id) const competitionId = Number(id) const [form, setForm] = useState({ title: '', class_section_id: '', semester: '', school_year: '', start_date: '', end_date: '', }) const [classRows, setClassRows] = useState([]) const [classSections, setClassSections] = useState([]) const [saving, setSaving] = useState(false) const [message, setMessage] = useState(null) const [error, setError] = useState(null) useEffect(() => { ;(async () => { try { const response = isEdit && Number.isFinite(competitionId) && competitionId > 0 ? await fetchAdminCompetitionWinnerSettings(competitionId) : await fetchAdminCompetitionWinnerCreate() if (!response.ok) { setError(response.message ?? 'Unable to load competition settings.') return } setForm(initialFormState(response)) setClassRows(response.classRows ?? []) setClassSections(response.classSections ?? []) setError(null) } catch (e) { setError(e instanceof Error ? e.message : 'Unable to load competition settings.') } })() }, [competitionId, isEdit]) const visibleRows = useMemo(() => { const selectedClassId = numberOrNull(form.class_section_id) if (selectedClassId === null) return classRows return classRows.filter((row) => row.class_section_id === selectedClassId) }, [classRows, form.class_section_id]) function updateClassRow( classSectionId: number, key: | 'question_count' | 'override_winners' | 'prize_1' | 'prize_2' | 'prize_3' | 'prize_4' | 'prize_5' | 'prize_6', value: string, ) { setClassRows((current) => current.map((row) => { if (row.class_section_id !== classSectionId) return row return { ...row, [key]: value.trim() === '' ? null : Number(value), } }), ) } async function onSubmit(e: React.FormEvent) { e.preventDefault() try { setSaving(true) setMessage(null) setError(null) const winnerOverrides = Object.fromEntries( classRows.map((row) => [String(row.class_section_id), normalizeRowValue(row.override_winners)]), ) const questionCounts = Object.fromEntries( classRows.map((row) => [String(row.class_section_id), normalizeRowValue(row.question_count)]), ) const prizes = Object.fromEntries( classRows.map((row) => [ String(row.class_section_id), { 1: normalizeRowValue(row.prize_1), 2: normalizeRowValue(row.prize_2), 3: normalizeRowValue(row.prize_3), 4: normalizeRowValue(row.prize_4), 5: normalizeRowValue(row.prize_5), 6: normalizeRowValue(row.prize_6), }, ]), ) const result = await saveAdminCompetitionWinner( { title: form.title, class_section_id: numberOrNull(form.class_section_id), semester: form.semester || null, school_year: form.school_year || null, start_date: form.start_date || null, end_date: form.end_date || null, winner_overrides: winnerOverrides, question_counts: questionCounts, prizes, }, isEdit && Number.isFinite(competitionId) && competitionId > 0 ? competitionId : undefined, ) if (!result.ok) { setError(result.message ?? 'Unable to save competition.') return } const nextId = result.id ?? competitionId setMessage(result.message ?? 'Competition saved.') if (Number.isFinite(nextId) && nextId > 0) { navigate(`${COMPETITION_WINNERS_BASE}/${nextId}/settings`, { replace: true }) return } navigate(COMPETITION_WINNERS_BASE) } catch (e) { setError(e instanceof Error ? e.message : 'Unable to save competition.') } finally { setSaving(false) } } return ( This screen now saves directly through the Laravel admin competition endpoints, including per-class winner overrides and prize grids. {error ?
{error}
: null} {message ?
{message}
: null}
void onSubmit(e)}>
setForm((current) => ({ ...current, title: e.target.value }))} required />
Leave empty to manage winners for all classes.
setForm((current) => ({ ...current, semester: e.target.value }))} />
setForm((current) => ({ ...current, school_year: e.target.value }))} placeholder="2025-2026" />
Counts use the school year above.
setForm((current) => ({ ...current, start_date: e.target.value }))} />
setForm((current) => ({ ...current, end_date: e.target.value }))} />
Winners per Class
{visibleRows.length === 0 ? ( ) : ( visibleRows.map((row) => { const finalWinners = row.override_winners === null || row.override_winners === undefined ? row.auto_winners : row.override_winners return ( {[1, 2, 3, 4, 5, 6].map((rank) => ( ))} ) }) )}
Class Students Auto Winners Questions 1st 2nd 3rd 4th 5th 6th Override Final Winners
No class rows found for the selected scope.
{row.class_section_name} {row.student_count} {row.auto_winners} updateClassRow(row.class_section_id, 'question_count', e.target.value)} /> updateClassRow( row.class_section_id, `prize_${rank}` as | 'prize_1' | 'prize_2' | 'prize_3' | 'prize_4' | 'prize_5' | 'prize_6', e.target.value, ) } /> updateClassRow(row.class_section_id, 'override_winners', e.target.value)} /> {finalWinners}
) }