2026-03-28 22:36:00 +00:00
/ * *
* SecondarySchoolDetailView Component
* Dedicated detail view for secondary schools with tabbed navigation :
* Overview | Academic | Admissions | Wellbeing | Parents | Finance
* /
'use client' ;
import { useEffect , useState } from 'react' ;
import { useRouter } from 'next/navigation' ;
import { useComparison } from '@/hooks/useComparison' ;
import { PerformanceChart } from './PerformanceChart' ;
import { MetricTooltip } from './MetricTooltip' ;
import type {
School , SchoolResult , AbsenceData ,
OfstedInspection , OfstedParentView , SchoolCensus ,
SchoolAdmissions , SenDetail , Phonics ,
SchoolDeprivation , SchoolFinance , NationalAverages ,
} from '@/lib/types' ;
import { formatPercentage , formatProgress , formatAcademicYear } from '@/lib/utils' ;
import styles from './SecondarySchoolDetailView.module.css' ;
const OFSTED_LABELS : Record < number , string > = {
1 : 'Outstanding' , 2 : 'Good' , 3 : 'Requires Improvement' , 4 : 'Inadequate' ,
} ;
const RC_LABELS : Record < number , string > = {
1 : 'Exceptional' , 2 : 'Strong' , 3 : 'Expected standard' , 4 : 'Needs attention' , 5 : 'Urgent improvement' ,
} ;
const RC_CATEGORIES = [
{ key : 'rc_inclusion' as const , label : 'Inclusion' } ,
{ key : 'rc_curriculum_teaching' as const , label : 'Curriculum & Teaching' } ,
{ key : 'rc_achievement' as const , label : 'Achievement' } ,
{ key : 'rc_attendance_behaviour' as const , label : 'Attendance & Behaviour' } ,
{ key : 'rc_personal_development' as const , label : 'Personal Development' } ,
{ key : 'rc_leadership_governance' as const , label : 'Leadership & Governance' } ,
{ key : 'rc_early_years' as const , label : 'Early Years' } ,
{ key : 'rc_sixth_form' as const , label : 'Sixth Form' } ,
] ;
type Tab = 'overview' | 'academic' | 'admissions' | 'wellbeing' | 'parents' | 'finance' ;
interface SecondarySchoolDetailViewProps {
schoolInfo : School ;
yearlyData : SchoolResult [ ] ;
absenceData : AbsenceData | null ;
ofsted : OfstedInspection | null ;
parentView : OfstedParentView | null ;
census : SchoolCensus | null ;
admissions : SchoolAdmissions | null ;
senDetail : SenDetail | null ;
phonics : Phonics | null ;
deprivation : SchoolDeprivation | null ;
finance : SchoolFinance | null ;
}
function progressClass ( val : number | null | undefined , modStyles : Record < string , string > ) : string {
if ( val == null ) return '' ;
if ( val > 0 ) return modStyles . progressPositive ;
if ( val < 0 ) return modStyles . progressNegative ;
return '' ;
}
function deprivationDesc ( decile : number ) : string {
if ( decile <= 3 ) return ` This school is in one of England's most deprived areas (decile ${ decile } /10). Many pupils may face additional challenges at home. ` ;
if ( decile <= 7 ) return ` This school is in an area with average levels of deprivation (decile ${ decile } /10). ` ;
return ` This school is in one of England's less deprived areas (decile ${ decile } /10). ` ;
}
export function SecondarySchoolDetailView ( {
schoolInfo , yearlyData ,
ofsted , parentView , admissions , senDetail , deprivation , finance , absenceData ,
} : SecondarySchoolDetailViewProps ) {
const router = useRouter ( ) ;
const { addSchool , removeSchool , isSelected } = useComparison ( ) ;
const isInComparison = isSelected ( schoolInfo . urn ) ;
const latestResults = yearlyData . length > 0 ? yearlyData [ yearlyData . length - 1 ] : null ;
const [ activeTab , setActiveTab ] = useState < Tab > ( 'overview' ) ;
const [ nationalAvg , setNationalAvg ] = useState < NationalAverages | null > ( null ) ;
useEffect ( ( ) = > {
fetch ( '/api/national-averages' )
. then ( r = > r . ok ? r . json ( ) : null )
. then ( data = > { if ( data ) setNationalAvg ( data ) ; } )
. catch ( ( ) = > { } ) ;
} , [ ] ) ;
const secondaryAvg = nationalAvg ? . secondary ? ? { } ;
const hasSixthForm = schoolInfo . age_range ? . includes ( '18' ) ? ? false ;
const hasFinance = finance != null && finance . per_pupil_spend != null ;
const hasParents = parentView != null || ofsted != null ;
const hasDeprivation = deprivation != null && deprivation . idaci_decile != null ;
const p8Suspended = latestResults != null && latestResults . year >= 202425 ;
const admissionsTag = ( ( ) = > {
const policy = schoolInfo . admissions_policy ? . toLowerCase ( ) ? ? '' ;
if ( policy . includes ( 'selective' ) ) return 'Selective' ;
const denom = schoolInfo . religious_denomination ? ? '' ;
if ( denom && denom !== 'Does not apply' ) return 'Faith priority' ;
return null ;
} ) ( ) ;
const tabs : { key : Tab ; label : string } [ ] = [
{ key : 'overview' , label : 'Overview' } ,
{ key : 'academic' , label : 'Academic' } ,
{ key : 'admissions' , label : 'Admissions' } ,
{ key : 'wellbeing' , label : 'Wellbeing' } ,
. . . ( hasParents ? [ { key : 'parents' as Tab , label : 'Parents' } ] : [ ] ) ,
. . . ( hasFinance ? [ { key : 'finance' as Tab , label : 'Finance' } ] : [ ] ) ,
] ;
const handleComparisonToggle = ( ) = > {
if ( isInComparison ) {
removeSchool ( schoolInfo . urn ) ;
} else {
addSchool ( schoolInfo ) ;
}
} ;
return (
< div className = { styles . container } >
{ /* ── Header ─────────────────────────────────────── */ }
< header className = { styles . header } >
< div className = { styles . headerContent } >
< div className = { styles . titleSection } >
< h1 className = { styles . schoolName } > { schoolInfo . school_name } < / h1 >
< div className = { styles . badges } >
{ schoolInfo . school_type && (
< span className = { styles . badge } > { schoolInfo . school_type } < / span >
) }
{ schoolInfo . gender && schoolInfo . gender !== 'Mixed' && (
< span className = { styles . badge } > { schoolInfo . gender } & apos ; s school < / span >
) }
{ schoolInfo . age_range && (
< span className = { styles . badge } > { schoolInfo . age_range } < / span >
) }
{ hasSixthForm && (
< span className = { styles . badge } > Sixth form < / span >
) }
{ admissionsTag && (
< span className = { ` ${ styles . badge } ${ admissionsTag === 'Selective' ? styles.badgeSelective : styles.badgeFaith } ` } >
{ admissionsTag }
< / span >
) }
< / div >
{ schoolInfo . address && (
< p className = { styles . address } >
{ schoolInfo . address } { schoolInfo . postcode && ` , ${ schoolInfo . postcode } ` }
< / p >
) }
< div className = { styles . headerDetails } >
{ schoolInfo . headteacher_name && (
< span className = { styles . headerDetail } >
< strong > Headteacher : < / strong > { schoolInfo . headteacher_name }
< / span >
) }
{ schoolInfo . website && (
< span className = { styles . headerDetail } >
< a href = { / ^ https ? : \ / \ //i.test(schoolInfo.website) ? schoolInfo.website : `https://${schoolInfo.website}`} target="_blank" rel="noopener noreferrer">
School website ↗
< / a >
< / span >
) }
{ latestResults ? . total_pupils != null && (
< span className = { styles . headerDetail } >
< strong > Pupils : < / strong > { latestResults . total_pupils . toLocaleString ( ) }
{ schoolInfo . capacity != null && ` (capacity: ${ schoolInfo . capacity } ) ` }
< / span >
) }
{ schoolInfo . trust_name && (
< span className = { styles . headerDetail } >
Part of < strong > { schoolInfo . trust_name } < / strong >
< / span >
) }
< / div >
< / div >
< div className = { styles . actions } >
< button
onClick = { handleComparisonToggle }
className = { isInComparison ? styles.btnRemove : styles.btnAdd }
>
{ isInComparison ? '✓ In Comparison' : '+ Add to Compare' }
< / button >
< / div >
< / div >
< / header >
{ /* ── Tab navigation (sticky) ─────────────────────── */ }
< nav className = { styles . tabNav } aria-label = "Detail sections" >
< div className = { styles . tabNavInner } >
< button onClick = { ( ) = > router . back ( ) } className = { styles . backBtn } > ← Back < / button >
< div className = { styles . tabNavDivider } / >
{ tabs . map ( ( { key , label } ) = > (
< button
key = { key }
onClick = { ( ) = > setActiveTab ( key ) }
className = { ` ${ styles . tabBtn } ${ activeTab === key ? styles . tabBtnActive : '' } ` }
>
{ label }
< / button >
) ) }
< / div >
< / nav >
{ /* ── Overview Tab ─────────────────────────────────── */ }
{ activeTab === 'overview' && (
< div className = { styles . tabContent } >
{ /* Ofsted summary card */ }
{ ofsted && (
< div className = { styles . card } >
< h2 className = { styles . sectionTitle } >
{ ofsted . framework === 'ReportCard' ? 'Ofsted Report Card' : 'Ofsted Rating' }
{ ofsted . inspection_date && (
< span className = { styles . ofstedDate } >
{ ' ' } Inspected { new Date ( ofsted . inspection_date ) . toLocaleDateString ( 'en-GB' , { month : 'long' , year : 'numeric' } ) }
< / span >
) }
< a
href = { ` https://reports.ofsted.gov.uk/provider/21/ ${ schoolInfo . urn } ` }
target = "_blank"
rel = "noopener noreferrer"
className = { styles . ofstedReportLink }
>
Full report ↗
< / a >
< / h2 >
{ ofsted . framework === 'ReportCard' ? (
< p className = { styles . sectionSubtitle } >
From November 2025 , Ofsted replaced single overall grades with Report Cards .
< / p >
2026-03-29 10:59:30 +01:00
) : ofsted . overall_effectiveness ? (
2026-03-28 22:36:00 +00:00
< div className = { styles . ofstedHeader } >
< span className = { ` ${ styles . ofstedGrade } ${ styles [ ` ofstedGrade ${ ofsted . overall_effectiveness } ` ] } ` } >
2026-03-29 10:59:30 +01:00
{ OFSTED_LABELS [ ofsted . overall_effectiveness ] }
2026-03-28 22:36:00 +00:00
< / span >
< / div >
2026-03-29 10:59:30 +01:00
) : (
< >
< p className = { styles . sectionSubtitle } >
From September 2024 , Ofsted no longer gives a single overall grade .
< / p >
< div className = { styles . metricsGrid } >
{ [
{ label : 'Quality of Education' , value : ofsted.quality_of_education } ,
{ label : 'Behaviour & Attitudes' , value : ofsted.behaviour_attitudes } ,
{ label : 'Personal Development' , value : ofsted.personal_development } ,
{ label : 'Leadership & Management' , value : ofsted.leadership_management } ,
] . filter ( ( { value } ) = > value != null ) . map ( ( { label , value } ) = > (
< div key = { label } className = { styles . metricCard } >
< div className = { styles . metricLabel } > { label } < / div >
< div className = { ` ${ styles . metricValue } ${ styles [ ` ofstedGrade ${ value } ` ] } ` } >
{ OFSTED_LABELS [ value ! ] }
< / div >
< / div >
) ) }
< / div >
< / >
2026-03-28 22:36:00 +00:00
) }
{ parentView ? . q_recommend_pct != null && parentView . total_responses != null && parentView . total_responses > 0 && (
< p className = { styles . parentRecommendLine } >
< strong > { Math . round ( parentView . q_recommend_pct ) } % < / strong > of parents would recommend this school ( { parentView . total_responses . toLocaleString ( ) } responses )
< / p >
) }
< / div >
) }
{ /* Attainment 8 headline */ }
{ latestResults ? . attainment_8_score != null && (
< div className = { styles . card } >
< h2 className = { styles . sectionTitle } >
GCSE Performance at a Glance ( { formatAcademicYear ( latestResults . year ) } )
< / h2 >
< div className = { styles . metricsGrid } >
< div className = { styles . metricCard } >
< div className = { styles . metricLabel } > Attainment 8 < / div >
< div className = { styles . metricValue } > { latestResults . attainment_8_score . toFixed ( 1 ) } < / div >
{ secondaryAvg . attainment_8_score != null && (
< div className = { styles . metricHint } > National avg : { secondaryAvg . attainment_8_score . toFixed ( 1 ) } < / div >
) }
< / div >
{ latestResults . english_maths_standard_pass_pct != null && (
< div className = { styles . metricCard } >
< div className = { styles . metricLabel } > English & amp ; Maths Grade 4 + < / div >
< div className = { styles . metricValue } > { formatPercentage ( latestResults . english_maths_standard_pass_pct ) } < / div >
{ secondaryAvg . english_maths_standard_pass_pct != null && (
< div className = { styles . metricHint } > National avg : { secondaryAvg . english_maths_standard_pass_pct . toFixed ( 0 ) } % < / div >
) }
< / div >
) }
{ admissions ? . oversubscribed != null && (
< div className = { styles . metricCard } >
< div className = { styles . metricLabel } > Admissions < / div >
< div className = { ` ${ styles . metricValue } ${ admissions . oversubscribed ? styles.statusWarn : styles.statusGood } ` } >
{ admissions . oversubscribed ? 'Oversubscribed' : 'Places available' }
< / div >
< div className = { styles . metricHint } > Last year < / div >
< / div >
) }
< / div >
{ p8Suspended && (
< div className = { styles . p8Banner } >
Progress 8 scores for 2024 / 25 are not used for accountability purposes following the KS2 assessment disruption . Treat with caution .
< / div >
) }
< / div >
) }
2026-03-29 10:59:30 +01:00
{ /* Admissions summary */ }
{ admissions && ( admissions . published_admission_number != null || admissions . total_applications != null ) && (
< div className = { styles . card } >
< h2 className = { styles . sectionTitle } > Admissions at a Glance < / h2 >
< div className = { styles . metricsGrid } >
{ admissions . published_admission_number != null && (
< div className = { styles . metricCard } >
< div className = { styles . metricLabel } > Year 7 places ( PAN ) < / div >
< div className = { styles . metricValue } > { admissions . published_admission_number } < / div >
< / div >
) }
{ admissions . total_applications != null && (
< div className = { styles . metricCard } >
< div className = { styles . metricLabel } > Total applications < / div >
< div className = { styles . metricValue } > { admissions . total_applications . toLocaleString ( ) } < / div >
< / div >
) }
{ admissions . first_preference_offer_pct != null && (
< div className = { styles . metricCard } >
< div className = { styles . metricLabel } > 1 st choice offer rate < / div >
2026-03-29 11:22:13 +01:00
< div className = { styles . metricValue } > { formatPercentage ( admissions . first_preference_offer_pct ) } < / div >
2026-03-29 10:59:30 +01:00
< / div >
) }
< / div >
< button onClick = { ( ) = > setActiveTab ( 'admissions' ) } className = { styles . tabLink } >
Full admissions details →
< / button >
< / div >
) }
{ /* SEN & school context summary */ }
{ ( latestResults ? . sen_support_pct != null || latestResults ? . sen_ehcp_pct != null || latestResults ? . total_pupils != null ) && (
< div className = { styles . card } >
< h2 className = { styles . sectionTitle } > School Context < / h2 >
< div className = { styles . metricsGrid } >
{ latestResults ? . total_pupils != null && (
< div className = { styles . metricCard } >
< div className = { styles . metricLabel } > Total pupils < / div >
< div className = { styles . metricValue } > { latestResults . total_pupils . toLocaleString ( ) } < / div >
{ schoolInfo . capacity != null && (
< div className = { styles . metricHint } > Capacity : { schoolInfo . capacity } < / div >
) }
< / div >
) }
{ latestResults ? . sen_support_pct != null && (
< div className = { styles . metricCard } >
< div className = { styles . metricLabel } > SEN support < / div >
< div className = { styles . metricValue } > { formatPercentage ( latestResults . sen_support_pct ) } < / div >
< / div >
) }
{ latestResults ? . sen_ehcp_pct != null && (
< div className = { styles . metricCard } >
< div className = { styles . metricLabel } > EHCP < / div >
< div className = { styles . metricValue } > { formatPercentage ( latestResults . sen_ehcp_pct ) } < / div >
< / div >
) }
< / div >
< / div >
) }
2026-03-28 22:36:00 +00:00
{ /* Top Parent View scores */ }
{ parentView != null && parentView . total_responses != null && parentView . total_responses > 0 && (
< div className = { styles . card } >
< h2 className = { styles . sectionTitle } > What Parents Say < / h2 >
< div className = { styles . parentViewGrid } >
{ [
{ label : 'My child is happy here' , pct : parentView.q_happy_pct } ,
{ label : 'Would recommend this school' , pct : parentView.q_recommend_pct } ,
] . filter ( q = > q . pct != null ) . map ( ( { label , pct } ) = > (
< div key = { label } className = { styles . parentViewRow } >
< span className = { styles . parentViewLabel } > { label } < / span >
< div className = { styles . parentViewBar } >
< div className = { styles . parentViewFill } style = { { width : ` ${ pct } % ` } } / >
< / div >
< span className = { styles . parentViewPct } > { Math . round ( pct ! ) } % < / span >
< / div >
) ) }
< / div >
< button onClick = { ( ) = > setActiveTab ( 'parents' ) } className = { styles . tabLink } >
See all parent feedback →
< / button >
< / div >
) }
< / div >
) }
{ /* ── Academic Tab ─────────────────────────────────── */ }
{ activeTab === 'academic' && latestResults && (
< div className = { styles . tabContent } >
< div className = { styles . card } >
< h2 className = { styles . sectionTitle } >
GCSE Results ( { formatAcademicYear ( latestResults . year ) } )
< / h2 >
< p className = { styles . sectionSubtitle } >
GCSE results for Year 11 pupils . National averages shown for comparison .
< / p >
{ p8Suspended && (
< div className = { styles . p8Banner } >
Progress 8 scores for 2024 / 25 are not used for accountability purposes following the KS2 assessment disruption . Treat with caution .
< / div >
) }
< div className = { styles . metricsGrid } >
{ latestResults . attainment_8_score != null && (
< div className = { styles . metricCard } >
< div className = { styles . metricLabel } >
Attainment 8
< MetricTooltip metricKey = "attainment_8_score" / >
< / div >
< div className = { styles . metricValue } > { latestResults . attainment_8_score . toFixed ( 1 ) } < / div >
{ secondaryAvg . attainment_8_score != null && (
< div className = { styles . metricHint } > National avg : { secondaryAvg . attainment_8_score . toFixed ( 1 ) } < / div >
) }
< / div >
) }
{ latestResults . progress_8_score != null && (
< div className = { styles . metricCard } >
< div className = { styles . metricLabel } >
Progress 8
< MetricTooltip metricKey = "progress_8_score" / >
< / div >
< div className = { ` ${ styles . metricValue } ${ progressClass ( latestResults . progress_8_score , styles ) } ` } >
{ formatProgress ( latestResults . progress_8_score ) }
< / div >
{ ( latestResults . progress_8_lower_ci != null || latestResults . progress_8_upper_ci != null ) && (
< div className = { styles . metricHint } >
CI : { latestResults . progress_8_lower_ci ? . toFixed ( 2 ) ? ? '?' } to { latestResults . progress_8_upper_ci ? . toFixed ( 2 ) ? ? '?' }
< / div >
) }
< / div >
) }
{ latestResults . english_maths_standard_pass_pct != null && (
< div className = { styles . metricCard } >
< div className = { styles . metricLabel } >
English & amp ; Maths Grade 4 +
< MetricTooltip metricKey = "english_maths_standard_pass_pct" / >
< / div >
< div className = { styles . metricValue } > { formatPercentage ( latestResults . english_maths_standard_pass_pct ) } < / div >
{ secondaryAvg . english_maths_standard_pass_pct != null && (
< div className = { styles . metricHint } > National avg : { secondaryAvg . english_maths_standard_pass_pct . toFixed ( 0 ) } % < / div >
) }
< / div >
) }
{ latestResults . english_maths_strong_pass_pct != null && (
< div className = { styles . metricCard } >
< div className = { styles . metricLabel } >
English & amp ; Maths Grade 5 +
< MetricTooltip metricKey = "english_maths_strong_pass_pct" / >
< / div >
< div className = { styles . metricValue } > { formatPercentage ( latestResults . english_maths_strong_pass_pct ) } < / div >
{ secondaryAvg . english_maths_strong_pass_pct != null && (
< div className = { styles . metricHint } > National avg : { secondaryAvg . english_maths_strong_pass_pct . toFixed ( 0 ) } % < / div >
) }
< / div >
) }
< / div >
{ /* A8 component breakdown */ }
{ ( latestResults . progress_8_english != null || latestResults . progress_8_maths != null ||
latestResults . progress_8_ebacc != null || latestResults . progress_8_open != null ) && (
< >
< h3 className = { styles . subSectionTitle } > Attainment 8 Components ( Progress 8 contribution ) < / h3 >
< div className = { styles . metricTable } >
{ [
{ label : 'English' , val : latestResults.progress_8_english } ,
{ label : 'Maths' , val : latestResults.progress_8_maths } ,
{ label : 'EBacc subjects' , val : latestResults.progress_8_ebacc } ,
{ label : 'Open (other GCSEs)' , val : latestResults.progress_8_open } ,
] . filter ( r = > r . val != null ) . map ( ( { label , val } ) = > (
< div key = { label } className = { styles . metricRow } >
< span className = { styles . metricName } > { label } < / span >
< span className = { ` ${ styles . metricValue } ${ progressClass ( val , styles ) } ` } >
{ formatProgress ( val ! ) }
< / span >
< / div >
) ) }
< / div >
< / >
) }
{ /* EBacc */ }
{ ( latestResults . ebacc_entry_pct != null || latestResults . ebacc_standard_pass_pct != null ) && (
< >
< h3 className = { styles . subSectionTitle } style = { { marginTop : '1rem' } } >
English Baccalaureate ( EBacc )
< MetricTooltip metricKey = "ebacc_entry_pct" / >
< / h3 >
< div className = { styles . metricTable } >
{ latestResults . ebacc_entry_pct != null && (
< div className = { styles . metricRow } >
< span className = { styles . metricName } > Pupils entered for EBacc < / span >
< span className = { styles . metricValue } > { formatPercentage ( latestResults . ebacc_entry_pct ) } < / span >
< / div >
) }
{ latestResults . ebacc_standard_pass_pct != null && (
< div className = { styles . metricRow } >
< span className = { styles . metricName } > EBacc Grade 4 + < / span >
< span className = { styles . metricValue } > { formatPercentage ( latestResults . ebacc_standard_pass_pct ) } < / span >
< / div >
) }
{ latestResults . ebacc_strong_pass_pct != null && (
< div className = { styles . metricRow } >
< span className = { styles . metricName } > EBacc Grade 5 + < / span >
< span className = { styles . metricValue } > { formatPercentage ( latestResults . ebacc_strong_pass_pct ) } < / span >
< / div >
) }
{ latestResults . ebacc_avg_score != null && (
< div className = { styles . metricRow } >
< span className = { styles . metricName } > EBacc average point score < / span >
< span className = { styles . metricValue } > { latestResults . ebacc_avg_score . toFixed ( 2 ) } < / span >
< / div >
) }
< / div >
< / >
) }
< / div >
{ /* Performance over time */ }
{ yearlyData . length > 0 && (
< div className = { styles . card } >
< h2 className = { styles . sectionTitle } > Results Over Time < / h2 >
< div className = { styles . chartContainer } >
< PerformanceChart
data = { yearlyData }
schoolName = { schoolInfo . school_name }
isSecondary = { true }
/ >
< / div >
{ yearlyData . length > 1 && (
< >
< p className = { styles . historicalSubtitle } > Detailed year - by - year figures < / p >
< div className = { styles . tableWrapper } >
< table className = { styles . dataTable } >
< thead >
< tr >
< th > Year < / th >
< th > Attainment 8 < / th >
< th > Progress 8 < / th >
< th > Eng & amp ; Maths 4 + < / th >
< th > EBacc entry % < / th >
< / tr >
< / thead >
< tbody >
{ yearlyData . map ( ( result ) = > (
< tr key = { result . year } >
< td className = { styles . yearCell } > { formatAcademicYear ( result . year ) } < / td >
< td > { result . attainment_8_score != null ? result . attainment_8_score . toFixed ( 1 ) : '-' } < / td >
< td > { result . progress_8_score != null ? formatProgress ( result . progress_8_score ) : '-' } < / td >
< td > { result . english_maths_standard_pass_pct != null ? formatPercentage ( result . english_maths_standard_pass_pct ) : '-' } < / td >
< td > { result . ebacc_entry_pct != null ? formatPercentage ( result . ebacc_entry_pct ) : '-' } < / td >
< / tr >
) ) }
< / tbody >
< / table >
< / div >
< / >
) }
< / div >
) }
< / div >
) }
{ /* ── Admissions Tab ───────────────────────────────── */ }
{ activeTab === 'admissions' && (
< div className = { styles . tabContent } >
< div className = { styles . card } >
< h2 className = { styles . sectionTitle } > Admissions < / h2 >
{ admissionsTag && (
< div className = { ` ${ styles . admissionsTypeBadge } ${ admissionsTag === 'Selective' ? styles.admissionsSelective : styles.admissionsFaith } ` } >
< strong > { admissionsTag } < / strong > { ' ' }
{ admissionsTag === 'Selective'
? '— Entry to this school is by selective examination (e.g. 11+).'
: ` — This school has a faith-based admissions priority ( ${ schoolInfo . religious_denomination } ). ` }
< / div >
) }
{ admissions ? (
< >
< div className = { styles . metricsGrid } >
{ admissions . published_admission_number != null && (
< div className = { styles . metricCard } >
< div className = { styles . metricLabel } > Year 7 places per year ( PAN ) < / div >
< div className = { styles . metricValue } > { admissions . published_admission_number } < / div >
< / div >
) }
{ admissions . total_applications != null && (
< div className = { styles . metricCard } >
< div className = { styles . metricLabel } > Total applications < / div >
< div className = { styles . metricValue } > { admissions . total_applications . toLocaleString ( ) } < / div >
< / div >
) }
{ admissions . first_preference_applications != null && (
< div className = { styles . metricCard } >
< div className = { styles . metricLabel } > 1 st preference applications < / div >
< div className = { styles . metricValue } > { admissions . first_preference_applications . toLocaleString ( ) } < / div >
< / div >
) }
{ admissions . first_preference_offer_pct != null && (
< div className = { styles . metricCard } >
< div className = { styles . metricLabel } > Families who got their first choice < / div >
2026-03-29 11:22:13 +01:00
< div className = { styles . metricValue } > { formatPercentage ( admissions . first_preference_offer_pct ) } < / div >
2026-03-28 22:36:00 +00:00
< / div >
) }
< / div >
{ admissions . oversubscribed != null && (
< div className = { ` ${ styles . admissionsBadge } ${ admissions . oversubscribed ? styles.statusWarn : styles.statusGood } ` } >
{ admissions . oversubscribed
? '⚠ Applications exceeded places last year'
: '✓ Places were available last year' }
< / div >
) }
< p className = { styles . sectionSubtitle } style = { { marginTop : '1rem' } } >
Historical distance cut - off data is not available for this school . Contact the admissions authority for oversubscription criteria details .
< / p >
< / >
) : (
< p className = { styles . sectionSubtitle } > Admissions data for this school is not yet available . < / p >
) }
{ hasSixthForm && (
< div className = { styles . sixthFormNote } >
This school has a sixth form ( Post - 16 provision ) . Post - 16 destination data coming soon .
< / div >
) }
< / div >
< / div >
) }
{ /* ── Wellbeing Tab ────────────────────────────────── */ }
{ activeTab === 'wellbeing' && (
< div className = { styles . tabContent } >
{ /* SEN */ }
{ ( latestResults ? . sen_support_pct != null || latestResults ? . sen_ehcp_pct != null ) && (
< div className = { styles . card } >
< h2 className = { styles . sectionTitle } > Special Educational Needs ( SEN ) < / h2 >
< div className = { styles . metricsGrid } >
{ latestResults ? . sen_support_pct != null && (
< div className = { styles . metricCard } >
< div className = { styles . metricLabel } >
Pupils receiving SEN support
< MetricTooltip metricKey = "sen_support_pct" / >
< / div >
< div className = { styles . metricValue } > { formatPercentage ( latestResults . sen_support_pct ) } < / div >
< div className = { styles . metricHint } > SEN support without an EHCP < / div >
< / div >
) }
{ latestResults ? . sen_ehcp_pct != null && (
< div className = { styles . metricCard } >
< div className = { styles . metricLabel } >
Pupils with an EHCP
< MetricTooltip metricKey = "sen_ehcp_pct" / >
< / div >
< div className = { styles . metricValue } > { formatPercentage ( latestResults . sen_ehcp_pct ) } < / div >
< div className = { styles . metricHint } > Education , Health and Care Plan < / div >
< / div >
) }
{ latestResults ? . total_pupils != null && (
< div className = { styles . metricCard } >
< div className = { styles . metricLabel } > Total pupils < / div >
< div className = { styles . metricValue } > { latestResults . total_pupils . toLocaleString ( ) } < / div >
< / div >
) }
< / div >
< / div >
) }
{ /* Deprivation */ }
{ hasDeprivation && deprivation && (
< div className = { styles . card } >
< h2 className = { styles . sectionTitle } >
Local Area Context
< MetricTooltip metricKey = "idaci_decile" / >
< / h2 >
< div className = { styles . deprivationDots } >
{ Array . from ( { length : 10 } , ( _ , i ) = > (
< div
key = { i }
className = { ` ${ styles . deprivationDot } ${ i < deprivation . idaci_decile ! ? styles . deprivationDotFilled : '' } ` }
title = { ` Decile ${ i + 1 } ` }
/ >
) ) }
< / div >
< div className = { styles . deprivationScaleLabel } >
< span > Most deprived < / span >
< span > Least deprived < / span >
< / div >
< p className = { styles . deprivationDesc } > { deprivationDesc ( deprivation . idaci_decile ! ) } < / p >
< / div >
) }
{ latestResults ? . sen_support_pct == null && latestResults ? . sen_ehcp_pct == null && ! hasDeprivation && (
< div className = { styles . card } >
< p className = { styles . sectionSubtitle } > Wellbeing data is not yet available for this school . < / p >
< / div >
) }
< / div >
) }
{ /* ── Parents Tab ─────────────────────────────────── */ }
{ activeTab === 'parents' && (
< div className = { styles . tabContent } >
{ /* Full Ofsted detail */ }
{ ofsted && (
< div className = { styles . card } >
< h2 className = { styles . sectionTitle } >
{ ofsted . framework === 'ReportCard' ? 'Ofsted Report Card' : 'Ofsted Rating' }
{ ofsted . inspection_date && (
< span className = { styles . ofstedDate } >
{ ' ' } Inspected { new Date ( ofsted . inspection_date ) . toLocaleDateString ( 'en-GB' , { day : 'numeric' , month : 'long' , year : 'numeric' } ) }
< / span >
) }
< a
href = { ` https://reports.ofsted.gov.uk/provider/21/ ${ schoolInfo . urn } ` }
target = "_blank"
rel = "noopener noreferrer"
className = { styles . ofstedReportLink }
>
Full report ↗
< / a >
< / h2 >
{ ofsted . framework === 'ReportCard' ? (
< >
< p className = { styles . ofstedDisclaimer } >
From November 2025 , Ofsted replaced single overall grades with Report Cards rating schools across several areas .
< / p >
< div className = { styles . metricsGrid } >
{ ofsted . rc_safeguarding_met != null && (
< div className = { styles . metricCard } >
< div className = { styles . metricLabel } > Safeguarding < / div >
< div className = { ` ${ styles . metricValue } ${ ofsted . rc_safeguarding_met ? styles.safeguardingMet : styles.safeguardingNotMet } ` } >
{ ofsted . rc_safeguarding_met ? 'Met' : 'Not met' }
< / div >
< / div >
) }
{ RC_CATEGORIES . filter ( ( { key } ) = > key !== 'rc_early_years' || ofsted [ key ] != null ) . map ( ( { key , label } ) = > {
const value = ofsted [ key ] as number | null ;
return value != null ? (
< div key = { key } className = { styles . metricCard } >
< div className = { styles . metricLabel } > { label } < / div >
< div className = { ` ${ styles . metricValue } ${ styles [ ` rcGrade ${ value } ` ] } ` } >
{ RC_LABELS [ value ] }
< / div >
< / div >
) : null ;
} ) }
< / div >
< / >
) : (
< >
< div className = { styles . ofstedHeader } >
< span className = { ` ${ styles . ofstedGrade } ${ styles [ ` ofstedGrade ${ ofsted . overall_effectiveness } ` ] } ` } >
{ ofsted . overall_effectiveness ? OFSTED_LABELS [ ofsted . overall_effectiveness ] : 'Not rated' }
< / span >
{ ofsted . previous_overall != null &&
ofsted . previous_overall !== ofsted . overall_effectiveness && (
< span className = { styles . ofstedPrevious } >
Previously : { OFSTED_LABELS [ ofsted . previous_overall ] }
< / span >
) }
< / div >
< p className = { styles . ofstedDisclaimer } >
From September 2024 , Ofsted no longer makes an overall effectiveness judgement in inspections .
< / p >
< div className = { styles . metricsGrid } >
{ [
{ label : 'Quality of Teaching' , value : ofsted.quality_of_education } ,
{ label : 'Behaviour in School' , value : ofsted.behaviour_attitudes } ,
{ label : 'Pupils\' Wider Development' , value : ofsted.personal_development } ,
{ label : 'School Leadership' , value : ofsted.leadership_management } ,
. . . ( ofsted . early_years_provision != null
? [ { label : 'Early Years (Reception)' , value : ofsted.early_years_provision } ]
: [ ] ) ,
] . map ( ( { label , value } ) = > value != null && (
< div key = { label } className = { styles . metricCard } >
< div className = { styles . metricLabel } > { label } < / div >
< div className = { ` ${ styles . metricValue } ${ styles [ ` ofstedGrade ${ value } ` ] } ` } >
{ OFSTED_LABELS [ value ] }
< / div >
< / div >
) ) }
< / div >
< / >
) }
< / div >
) }
{ /* Parent View survey */ }
{ parentView && parentView . total_responses != null && parentView . total_responses > 0 && (
< div className = { styles . card } >
< h2 className = { styles . sectionTitle } >
What Parents Say
< span className = { styles . responseBadge } >
{ parentView . total_responses . toLocaleString ( ) } responses
< / span >
< / h2 >
< p className = { styles . sectionSubtitle } >
From the Ofsted Parent View survey — parents share their experience of this school .
< / p >
< div className = { styles . parentViewGrid } >
{ [
{ label : 'Would recommend this school' , pct : parentView.q_recommend_pct } ,
{ label : 'My child is happy here' , pct : parentView.q_happy_pct } ,
{ label : 'My child feels safe here' , pct : parentView.q_safe_pct } ,
{ label : 'Teaching is good' , pct : parentView.q_teaching_pct } ,
{ label : 'My child makes good progress' , pct : parentView.q_progress_pct } ,
{ label : 'School looks after pupils\' wellbeing' , pct : parentView.q_wellbeing_pct } ,
{ label : 'Behaviour is well managed' , pct : parentView.q_behaviour_pct } ,
{ label : 'School deals well with bullying' , pct : parentView.q_bullying_pct } ,
{ label : 'Communicates well with parents' , pct : parentView.q_communication_pct } ,
] . filter ( q = > q . pct != null ) . map ( ( { label , pct } ) = > (
< div key = { label } className = { styles . parentViewRow } >
< span className = { styles . parentViewLabel } > { label } < / span >
< div className = { styles . parentViewBar } >
< div className = { styles . parentViewFill } style = { { width : ` ${ pct } % ` } } / >
< / div >
< span className = { styles . parentViewPct } > { Math . round ( pct ! ) } % < / span >
< / div >
) ) }
< / div >
< / div >
) }
{ ! ofsted && ( ! parentView || parentView . total_responses == null || parentView . total_responses === 0 ) && (
< div className = { styles . card } >
< p className = { styles . sectionSubtitle } > Parent and Ofsted data is not yet available for this school . < / p >
< / div >
) }
< / div >
) }
{ /* ── Finance Tab ─────────────────────────────────── */ }
{ activeTab === 'finance' && hasFinance && finance && (
< div className = { styles . tabContent } >
< div className = { styles . card } >
< h2 className = { styles . sectionTitle } > School Finances ( { formatAcademicYear ( finance . year ) } ) < / h2 >
< p className = { styles . sectionSubtitle } >
Per - pupil spending shows how much the school has to spend on each child & apos ; s education .
< / p >
< div className = { styles . metricsGrid } >
< div className = { styles . metricCard } >
< div className = { styles . metricLabel } > Total spend per pupil per year < / div >
< div className = { styles . metricValue } > £ { Math . round ( finance . per_pupil_spend ! ) . toLocaleString ( ) } < / div >
< div className = { styles . metricHint } > How much the school has to spend on each pupil annually < / div >
< / div >
{ finance . teacher_cost_pct != null && (
< div className = { styles . metricCard } >
< div className = { styles . metricLabel } > Share of budget spent on teachers < / div >
< div className = { styles . metricValue } > { finance . teacher_cost_pct . toFixed ( 1 ) } % < / div >
< / div >
) }
{ finance . staff_cost_pct != null && (
< div className = { styles . metricCard } >
< div className = { styles . metricLabel } > Share of budget spent on all staff < / div >
< div className = { styles . metricValue } > { finance . staff_cost_pct . toFixed ( 1 ) } % < / div >
< / div >
) }
{ finance . premises_cost_pct != null && (
< div className = { styles . metricCard } >
< div className = { styles . metricLabel } > Share of budget spent on premises < / div >
< div className = { styles . metricValue } > { finance . premises_cost_pct . toFixed ( 1 ) } % < / div >
< / div >
) }
< / div >
< / div >
< / div >
) }
< / div >
) ;
}