@@ -0,0 +1,292 @@
/**
* How children do academically — tier-1 dot strips anchored on official
* England averages, tier-2 "More measures" one tap away, equity row against
* the computed state-school benchmark. Copy verbatim from the reviewed
* mockups; teacher-assessed measures are labelled as such.
*/
'use client' ;
import { latestValues , verdict } from '@/lib/compareLogic' ;
import type { Benchmarks , ComparisonData , NationalAverages , School } from '@/lib/types' ;
import { DotStrip } from '@/components/DotStrip' ;
import { Cell , Chip , RowLabel , Section , SectionGrid , sectionStyles as s } from './sectionShared' ;
import styles from './CompareAcademics.module.css' ;
interface StripSpec {
label : string ;
metric : string ;
anchorKey? : string ;
tip? : string ;
min? : number ;
max? : number ;
unit? : string ;
}
const TIER1_PRIMARY : StripSpec [ ] = [
{
label : 'Reading, writing & maths — expected standard' ,
metric : 'rwm_expected_pct' ,
anchorKey : 'rwm_expected_pct' ,
tip : '% of Year 6 pupils reaching the expected standard in reading, writing and maths.' ,
} ,
{ label : 'Reading' , metric : 'reading_expected_pct' , anchorKey : 'reading_expected_pct' } ,
{
label : 'Writing (teacher-assessed)' ,
metric : 'writing_expected_pct' ,
anchorKey : 'writing_expected_pct' ,
tip : 'Writing is assessed by teachers, not tested.' ,
} ,
{ label : 'Maths' , metric : 'maths_expected_pct' , anchorKey : 'maths_expected_pct' } ,
{
label : 'Working at a higher standard than expected' ,
metric : 'rwm_high_pct' ,
anchorKey : 'rwm_high_pct' ,
tip : 'A high score in the reading and maths tests plus “greater depth” in teacher-assessed writing.' ,
} ,
] ;
const TIER2_PRIMARY : StripSpec [ ] = [
{
label : 'Grammar, punctuation & spelling — expected standard' ,
metric : 'gps_expected_pct' ,
anchorKey : 'gps_expected_pct' ,
} ,
{
label : 'Science — expected standard (teacher-assessed)' ,
metric : 'science_expected_pct' ,
anchorKey : 'science_expected_pct' ,
tip : 'Teacher-assessed, like writing — there has been no KS2 science test since 2009, so comparisons are indicative.' ,
} ,
{
label : 'Average scaled score — reading' ,
metric : 'reading_avg_score' ,
anchorKey : 'reading_avg_score' ,
min : 100 ,
max : 120 ,
unit : '' ,
} ,
{
label : 'Average scaled score — maths' ,
metric : 'maths_avg_score' ,
anchorKey : 'maths_avg_score' ,
min : 100 ,
max : 120 ,
unit : '' ,
} ,
{
label : 'Average scaled score — grammar, punctuation & spelling' ,
metric : 'gps_avg_score' ,
anchorKey : 'gps_avg_score' ,
min : 100 ,
max : 120 ,
unit : '' ,
} ,
] ;
function Strip ( {
spec ,
data ,
urns ,
schoolNames ,
national ,
} : {
spec : StripSpec ;
data : Record < string , ComparisonData > ;
urns : number [ ] ;
schoolNames : string [ ] ;
national : Record < string , number > | undefined ;
} ) {
const values = latestValues ( data , urns , spec . metric ) . map ( ( v ) = >
v != null ? Math . round ( v ) : null ,
) ;
const anchorValue = spec . anchorKey ? national ? . [ spec . anchorKey ] : undefined ;
const anchor =
anchorValue != null
? { value : anchorValue , label : ` England ${ Math . round ( anchorValue ) } ${ spec . unit ? ? '%' } ` }
: null ;
if ( values . every ( ( v ) = > v == null ) ) return null ;
return (
< DotStrip
label = { spec . label }
values = { values }
schoolNames = { schoolNames }
anchor = { anchor }
min = { spec . min ? ? 0 }
max = { spec . max ? ? 100 }
unit = { spec . unit ? ? '%' }
tip = { spec . tip }
/ >
) ;
}
export function CompareAcademics ( {
schools ,
data ,
nationalAverages ,
benchmarks ,
} : {
schools : School [ ] ;
data : Record < string , ComparisonData > ;
nationalAverages? : NationalAverages ;
benchmarks? : Benchmarks ;
} ) {
const urns = schools . map ( ( school ) = > school . urn ) ;
const schoolNames = schools . map ( ( school ) = > school . school_name ) ;
const isSecondary = schools . some (
( school ) = > data [ String ( school . urn ) ] ? . school_info ? . attainment_8_score != null ,
) ;
if ( isSecondary ) {
const att8 = latestValues ( data , urns , 'attainment_8_score' ) ;
const banding = urns . map ( ( urn ) = > {
const rows = data [ String ( urn ) ] ? . yearly_data ? ? [ ] ;
for ( let i = rows . length - 1 ; i >= 0 ; i -- ) {
if ( rows [ i ] . progress_8_banding ) return rows [ i ] . progress_8_banding as string ;
}
return null ;
} ) ;
const grade5 = latestValues ( data , urns , 'english_maths_strong_pass_pct' ) ;
const ebacc = latestValues ( data , urns , 'ebacc_entry_pct' ) ;
const att8Anchor = nationalAverages ? . secondary ? . attainment_8_score ;
return (
< Section
title = "How students do academically"
how = "GCSE results (latest year). Attainment 8 averages performance across eight subjects; Progress 8 shows how much progress students make compared with similar students nationally — the wording is DfE's own banding."
>
< SectionGrid schools = { schools } >
< RowLabel tip = "Average Attainment 8 score across eight GCSE subjects." > Attainment 8 < / RowLabel >
{ schools . map ( ( school , i ) = > (
< Cell key = { school . urn } school = { school } index = { i } >
{ att8 [ i ] != null ? (
< >
< span className = { s . big } > { ( att8 [ i ] as number ) . toFixed ( 1 ) } < / span >
{ att8Anchor != null && (
< span className = { s . small } > England average { att8Anchor . toFixed ( 1 ) } < / span >
) }
< / >
) : (
< span className = { s . small } > No data < / span >
) }
< / Cell >
) ) }
< RowLabel tip = "DfE's own plain-English Progress 8 label." > Progress 8 < / RowLabel >
{ schools . map ( ( school , i ) = > (
< Cell key = { school . urn } school = { school } index = { i } >
{ banding [ i ] ? (
< Chip
tone = {
/ well above | above / i . test ( banding [ i ] as string )
? 'good'
: /well below|below/i . test ( banding [ i ] as string )
? 'warn'
: 'neutral'
}
>
{ banding [ i ] }
< / Chip >
) : (
< span className = { s . small } > No data < / span >
) }
< / Cell >
) ) }
< RowLabel tip = "% achieving grade 5 or above in both English and maths GCSEs." >
Grade 5 + in English & amp ; maths
< / RowLabel >
{ schools . map ( ( school , i ) = > (
< Cell key = { school . urn } school = { school } index = { i } >
{ grade5 [ i ] != null ? ` ${ Math . round ( grade5 [ i ] as number ) } % ` : < span className = { s . small } > No data < / span > }
< / Cell >
) ) }
< RowLabel tip = "% entering the English Baccalaureate subject combination." > EBacc entry < / RowLabel >
{ schools . map ( ( school , i ) = > (
< Cell key = { school . urn } school = { school } index = { i } >
{ ebacc [ i ] != null ? ` ${ Math . round ( ebacc [ i ] as number ) } % ` : < span className = { s . small } > No data < / span > }
< / Cell >
) ) }
< / SectionGrid >
< / Section >
) ;
}
const national = nationalAverages ? . primary ;
const disadvantaged = latestValues ( data , urns , 'rwm_expected_disadvantaged_pct' ) ;
const disadvantagedAnchor = benchmarks ? . primary ? . disadvantaged_rwm_expected_pct ? ? null ;
return (
< Section
title = "How children do academically"
how = "Results from national tests and teacher assessments at the end of Year 6 — writing is assessed by teachers, not tested. Each line runs from 0– 100%; the grey tick marks the England average, so dots to its right are above average."
>
< div className = { s . card } >
{ TIER1_PRIMARY . map ( ( spec ) = > (
< Strip
key = { spec . metric }
spec = { spec }
data = { data }
urns = { urns }
schoolNames = { schoolNames }
national = { national }
/ >
) ) }
< details className = { styles . moreMeasures } >
< summary > More measures — grammar , punctuation & amp ; spelling , science , average scaled scores < / summary >
{ TIER2_PRIMARY . map ( ( spec ) = > (
< Strip
key = { spec . metric }
spec = { spec }
data = { data }
urns = { urns }
schoolNames = { schoolNames }
national = { national }
/ >
) ) }
< p className = { styles . stripNote } >
The scaled - score strips show the 100 – 120 window of the full 80 – 120 range ; 100 is the
expected standard . Where an England tick is missing , the official figure isn & apos ; t in
our dataset yet .
< / p >
< / details >
< / div >
{ disadvantaged . some ( ( v ) = > v != null ) && (
< SectionGrid schools = { schools } >
< RowLabel tip = "% of disadvantaged pupils (free school meals in the last 6 years, or looked after by the local authority) reaching the expected standard. Benchmark computed across state schools in our dataset. Based on smaller pupil groups, so a single pupil can move a school's figure noticeably." >
Children from lower - income families
< / RowLabel >
{ schools . map ( ( school , i ) = > {
const value = disadvantaged [ i ] ;
return (
< Cell key = { school . urn } school = { school } index = { i } >
{ value != null ? (
< >
< span className = { s . big } style = { { fontSize : '1.1rem' } } >
{ Math . round ( value ) } %
< / span > { ' ' }
{ disadvantagedAnchor != null && (
< Chip tone = { verdict ( value , disadvantagedAnchor , 5 ) === 'below' ? 'warn' : 'good' } >
{ verdict ( value , disadvantagedAnchor , 5 ) === 'above' &&
` Well above the ${ Math . round ( disadvantagedAnchor ) } % state-school average ` }
{ verdict ( value , disadvantagedAnchor , 5 ) === 'close' &&
` Around the ${ Math . round ( disadvantagedAnchor ) } % state-school average ` }
{ verdict ( value , disadvantagedAnchor , 5 ) === 'below' &&
` Below the ${ Math . round ( disadvantagedAnchor ) } % state-school average ` }
< / Chip >
) }
< / >
) : (
< span className = { s . small } > No data < / span >
) }
< / Cell >
) ;
} ) }
< / SectionGrid >
) }
< / Section >
) ;
}