diff --git a/nextjs-app/components/school/AdmissionsSection.tsx b/nextjs-app/components/school/AdmissionsSection.tsx new file mode 100644 index 0000000..95f90dd --- /dev/null +++ b/nextjs-app/components/school/AdmissionsSection.tsx @@ -0,0 +1,137 @@ +/** + * AdmissionsSection — primary detail pages. + * + * Not shared with the secondary page: the two versions were only 14% similar + * (this one carries the year/trend toggle and the offer-rate chart; the + * secondary one is a much simpler panel). See SecondaryAdmissionsSection. + * + * Server component. The year/trend toggle is delegated to the small + * AdmissionsViewToggle client island, which receives both views as + * server-rendered children. When there is only one year of offer data no + * toggle renders at all, so such pages ship zero admissions JavaScript. + */ + +import dynamic from 'next/dynamic'; +import type { ReactNode } from 'react'; +import type { SchoolAdmissions } from '@/lib/types'; +import { formatAcademicYear, formatPercentage } from '@/lib/utils'; +import { summariseAdmissions } from '@/lib/compareLogic'; +import { Section, sectionStyles as styles } from './sectionShared'; +import { AdmissionsViewToggle } from './AdmissionsViewToggle'; + +const AdmissionsTrendChart = dynamic(() => import('../AdmissionsTrendChart'), { ssr: false }); + +export function AdmissionsSection({ + admissions, + admissionsHistory, + isAllThrough, +}: { + admissions: SchoolAdmissions; + admissionsHistory: SchoolAdmissions[]; + isAllThrough: boolean; +}) { + // Trend toggle only appears with ≥2 years carrying an offer rate. + const admissionsOfferYears = admissionsHistory.filter((h) => h.first_preference_offer_pct != null).length; + const showAdmissionsTrend = admissionsOfferYears >= 2; + // Banded interpretation of the first-choice offer rate ("More than half of + // first choices missed out" etc.) — the same banding the compare screen + // uses, so a low offer rate reads as how severe it actually is. + const admissionsSummary = summariseAdmissions(admissions); + + const title = <>Admissions{!showAdmissionsTrend && ` (${formatAcademicYear(admissions.year)})`}>; + + {/* All-through admissions data covers a single entry point (usually the + Year 7 secondary intake), not reception — say so, or a parent could + read these as the whole-school figures. */} + const subtitle: ReactNode = isAllThrough && admissions.school_phase ? ( +
+ These figures are for {admissions.school_phase.toLowerCase()} entry + {/secondary/i.test(admissions.school_phase) ? ' (Year 7)' : /primary/i.test(admissions.school_phase) ? ' (Reception)' : ''}. +
+ ) : null; + + const yearView = ( + <> +{admissionsSummary.chip.text}
+ )} + > + ); + + const trendView = ( + <> ++ This year ({formatAcademicYear(admissions.year)}),{' '} + {admissions.first_preference_applications != null && ( + <>{admissions.first_preference_applications} families put it first for > + )} + {admissions.places_offered != null && <>{admissions.places_offered} places>} + {admissions.total_applications != null && ` — ${admissions.total_applications.toLocaleString()} applications in total`}. +
+ > + ); + + return ( ++ Per-pupil spending shows how much the school has to spend on each child's education. +
++ GCSE results for Year 11 pupils.{!suppressComparison && ' England averages shown for comparison.'} +
+ +| Year | + {isAllThrough ? ( + <> +RWM (expected %) | +Exceeding (%) | +Attainment 8 | +Progress 8 | +English & Maths Grade 4+ | + > + ) : isSecondary ? ( + <> +Attainment 8 | +Progress 8 | +English & Maths Grade 4+ | +English & Maths Grade 5+ | + > + ) : ( + <> +Reading, Writing & Maths (expected %) | +Exceeding expected (%) | +Reading Progress | +Writing Progress | +Maths Progress | + > + )} +
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| {formatAcademicYear(result.year)} | + {isAllThrough ? ( + <> +{result.rwm_expected_pct !== null ? formatPercentage(result.rwm_expected_pct) : '-'} | +{result.rwm_high_pct !== null ? formatPercentage(result.rwm_high_pct) : '-'} | +{result.attainment_8_score !== null ? result.attainment_8_score.toFixed(1) : '-'} | +{result.progress_8_score !== null ? formatProgress(result.progress_8_score) : '-'} | +{result.english_maths_standard_pass_pct !== null ? formatPercentage(result.english_maths_standard_pass_pct) : '-'} | + > + ) : isSecondary ? ( + <> +{result.attainment_8_score !== null ? result.attainment_8_score.toFixed(1) : '-'} | +{result.progress_8_score !== null ? formatProgress(result.progress_8_score) : '-'} | +{result.english_maths_standard_pass_pct !== null ? formatPercentage(result.english_maths_standard_pass_pct) : '-'} | +{result.english_maths_strong_pass_pct !== null ? formatPercentage(result.english_maths_strong_pass_pct) : '-'} | + > + ) : ( + <> +{result.rwm_expected_pct !== null ? formatPercentage(result.rwm_expected_pct) : '-'} | +{result.rwm_high_pct !== null ? formatPercentage(result.rwm_high_pct) : '-'} | +{result.reading_progress !== null ? formatProgress(result.reading_progress) : '-'} | +{result.writing_progress !== null ? formatProgress(result.writing_progress) : '-'} | +{result.maths_progress !== null ? formatProgress(result.maths_progress) : '-'} | + > + )} +
{deprivationDesc(deprivation.idaci_decile!)}
++ From November 2025, Ofsted replaced single overall grades with Report Cards rating schools across several areas. +
++ {ofsted.grade_source === 'ungraded_carried_forward' + ? 'This overall grade is carried forward from an earlier inspection — Ofsted has since visited without issuing a new overall grade. From September 2024, Ofsted no longer makes an overall effectiveness judgement.' + : isSecondary + ? 'From September 2024, Ofsted no longer makes an overall effectiveness judgement in inspections.' + : 'From September 2024, Ofsted no longer makes an overall effectiveness judgement in inspections of state-funded schools.'} +
+ {oeifAllSameGrade ? ( ++ Rated {OFSTED_LABELS[ofsted.overall_effectiveness!]} across all inspected areas — Quality of Teaching, Behaviour, Pupils' Development and Leadership. +
+ ) : ( ++ From September 2024, Ofsted no longer gives a single overall grade. +
++ {isSpecial + ? (isSecondary + ? 'GCSE results for Year 11 pupils.' + : 'End-of-primary-school tests taken by Year 6 pupils.') + : isAllThrough + ? 'KS2 SATs (end of Year 6) and GCSE results (Year 11) — this school covers both. England averages shown for comparison.' + : isSecondary + ? 'GCSE results for Year 11 pupils. England averages shown for comparison.' + : 'End-of-primary-school tests taken by Year 6 pupils. England averages shown for comparison.'} +
+ + {/* Explains up front why the England comparison is dropped below, so + a 0% headline never reads as a failing grade against a benchmark + that doesn't fit. Type-aware copy (special vs PRU vs AP). */} ++ Progress scores measure how much pupils improved compared to similar schools nationally. Above 0 = better than average, below 0 = below average. +
+ )} + > + )} + + {/* ── Secondary / KS4 content ── */} + {hasKS4Results && ( + <> + {isAllThrough && ( ++ Historical distance cut-off data is not available for this school. Contact the admissions authority for oversubscription criteria details. +
+ + {hasSixthForm && ( +| Year | +Attainment 8 | +Progress 8 | +Eng & Maths 4+ | +EBacc entry % | +
|---|---|---|---|---|
| {formatAcademicYear(result.year)} | +{result.attainment_8_score != null ? result.attainment_8_score.toFixed(1) : '-'} | +{result.progress_8_score != null ? formatProgress(result.progress_8_score) : '-'} | +{result.english_maths_standard_pass_pct != null ? formatPercentage(result.english_maths_standard_pass_pct) : '-'} | +{result.ebacc_entry_pct != null ? formatPercentage(result.ebacc_entry_pct) : '-'} | +
{deprivationDesc(deprivation.idaci_decile!)}
+ > + )} +