feat(school-detail): page-wide improvements across 5 sections
Build and Push Docker Images / Build Backend (FastAPI) (push) Successful in 12s
Build and Push Docker Images / Build Frontend (Next.js) (push) Successful in 49s
Build and Push Docker Images / Build Pipeline (Meltano + dbt + Airflow) (push) Successful in 12s
Build and Push Docker Images / Trigger Portainer Update (push) Successful in 1s

History chart
- Strip the redundant school-name title (already the page heading)
- Default to 2 visible lines: Reading, Writing & Maths expected % (teal,
  bold) + Exceeding (gold, lighter); progress score lines hidden by
  default, togglable via legend
- Add dashed national average reference line for RWM (primary) or
  Attainment 8 (secondary) so the school's trajectory is always in
  context
- Add trend summary chip above the chart computed from the data
  ("↓ Peaked at 90% (2016/17), currently 70%")
- Add COVID footnote when 2019/20 and 2020/21 data is absent

Ofsted section
- Collapse the four identical "Outstanding / Outstanding / Outstanding /
  Outstanding" boxes into a single prose line when all sub-grades match
  the overall verdict; show individual cards only when grades differ

SATs sub-metrics
- DeltaChip vs national average on Expected level row for Reading,
  Writing and Maths (national averages already in the API response)

Admissions
- Fix label: "Year 3 places per year" → "Reception places per year" for
  primary schools

Pupils & Inclusion
- DeltaChip + national avg hint on Eligible for pupil premium and
  Pupils receiving SEN support (both keys present in /api/national-averages)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Tudor Sitaru
2026-04-08 15:13:13 +01:00
parent 41cefeedf6
commit dfa8058efc
4 changed files with 269 additions and 144 deletions
+73 -24
View File
@@ -138,6 +138,19 @@ export function SchoolDetailView({
if (hasFinance) navItems.push({ id: 'finances', label: 'Finances' });
if (yearlyData.length > 0) navItems.push({ id: 'history', label: 'History' });
// ── Ofsted: detect if all OEIF sub-grades match the overall ───────────
const oeifAllSameGrade = (() => {
if (!ofsted || ofsted.framework === 'ReportCard') return false;
const subs = [
ofsted.quality_of_education,
ofsted.behaviour_attitudes,
ofsted.personal_development,
ofsted.leadership_management,
...(ofsted.early_years_provision != null ? [ofsted.early_years_provision] : []),
].filter((v): v is number => v != null);
return subs.length >= 3 && subs.every(v => v === ofsted.overall_effectiveness);
})();
// ── Hero: framework-aware signal chip + narrative summary ─────────────
const ofstedHeroChip = buildOfstedHeroChip(ofsted);
const heroSummary = buildSchoolSummary(schoolInfo, ofsted, admissions, latestResults);
@@ -385,24 +398,30 @@ export function SchoolDetailView({
<strong>{Math.round(parentView.q_recommend_pct)}%</strong> of parents would recommend this school ({parentView.total_responses.toLocaleString()} responses)
</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]}
{oeifAllSameGrade ? (
<p className={styles.ofstedAllSame}>
Rated <strong>{OFSTED_LABELS[ofsted.overall_effectiveness!]}</strong> across all inspected areas Quality of Teaching, Behaviour, Pupils&apos; Development and Leadership.
</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>
))}
</div>
)}
</>
)}
</section>
@@ -513,7 +532,12 @@ export function SchoolDetailView({
{latestResults.reading_expected_pct !== null && (
<div className={styles.metricRow}>
<span className={styles.metricName}>Expected level</span>
<span className={styles.metricValue}>{formatPercentage(latestResults.reading_expected_pct)}</span>
<span className={styles.metricValue}>
{formatPercentage(latestResults.reading_expected_pct)}
{primaryAvg.reading_expected_pct != null && (
<DeltaChip value={latestResults.reading_expected_pct} baseline={primaryAvg.reading_expected_pct} unit="pts" size="sm" />
)}
</span>
</div>
)}
{latestResults.reading_high_pct !== null && (
@@ -551,7 +575,12 @@ export function SchoolDetailView({
{latestResults.writing_expected_pct !== null && (
<div className={styles.metricRow}>
<span className={styles.metricName}>Expected level</span>
<span className={styles.metricValue}>{formatPercentage(latestResults.writing_expected_pct)}</span>
<span className={styles.metricValue}>
{formatPercentage(latestResults.writing_expected_pct)}
{primaryAvg.writing_expected_pct != null && (
<DeltaChip value={latestResults.writing_expected_pct} baseline={primaryAvg.writing_expected_pct} unit="pts" size="sm" />
)}
</span>
</div>
)}
{latestResults.writing_high_pct !== null && (
@@ -580,7 +609,12 @@ export function SchoolDetailView({
{latestResults.maths_expected_pct !== null && (
<div className={styles.metricRow}>
<span className={styles.metricName}>Expected level</span>
<span className={styles.metricValue}>{formatPercentage(latestResults.maths_expected_pct)}</span>
<span className={styles.metricValue}>
{formatPercentage(latestResults.maths_expected_pct)}
{primaryAvg.maths_expected_pct != null && (
<DeltaChip value={latestResults.maths_expected_pct} baseline={primaryAvg.maths_expected_pct} unit="pts" size="sm" />
)}
</span>
</div>
)}
{latestResults.maths_high_pct !== null && (
@@ -792,7 +826,7 @@ export function SchoolDetailView({
<div className={styles.metricsGrid}>
{admissions.published_admission_number != null && (
<div className={styles.metricCard}>
<div className={styles.metricLabel}>{isSecondary ? 'Year 7' : 'Year 3'} places per year</div>
<div className={styles.metricLabel}>{isSecondary ? 'Year 7' : 'Reception'} places per year</div>
<div className={styles.metricValue}>{admissions.published_admission_number}</div>
</div>
)}
@@ -820,8 +854,13 @@ export function SchoolDetailView({
{latestResults?.disadvantaged_pct != null && (
<div className={styles.metricCard}>
<div className={styles.metricLabel}>Eligible for pupil premium</div>
<div className={styles.metricValue}>{formatPercentage(latestResults.disadvantaged_pct)}</div>
<div className={styles.metricHint}>Pupils from disadvantaged backgrounds</div>
<div className={styles.metricValue}>
{formatPercentage(latestResults.disadvantaged_pct)}
{primaryAvg.disadvantaged_pct != null && (
<DeltaChip value={latestResults.disadvantaged_pct} baseline={primaryAvg.disadvantaged_pct} unit="pts" size="sm" />
)}
</div>
<div className={styles.metricHint}>Pupils from disadvantaged backgrounds{primaryAvg.disadvantaged_pct != null ? ` · national avg: ${primaryAvg.disadvantaged_pct.toFixed(0)}%` : ''}</div>
</div>
)}
{latestResults?.eal_pct != null && (
@@ -839,7 +878,15 @@ export function SchoolDetailView({
Pupils receiving SEN support
<MetricTooltip metricKey="sen_support_pct" />
</div>
<div className={styles.metricValue}>{formatPercentage(latestResults.sen_support_pct)}</div>
<div className={styles.metricValue}>
{formatPercentage(latestResults.sen_support_pct)}
{primaryAvg.sen_support_pct != null && (
<DeltaChip value={latestResults.sen_support_pct} baseline={primaryAvg.sen_support_pct} unit="pts" size="sm" />
)}
</div>
{primaryAvg.sen_support_pct != null && (
<div className={styles.metricHint}>National avg: {primaryAvg.sen_support_pct.toFixed(0)}%</div>
)}
</div>
)}
</div>
@@ -945,6 +992,8 @@ export function SchoolDetailView({
data={yearlyData}
schoolName={schoolInfo.school_name}
isSecondary={isSecondary}
nationalRwmAvg={isPrimary ? (primaryAvg.rwm_expected_pct ?? null) : null}
nationalAtt8Avg={isSecondary ? (secondaryAvg.attainment_8_score ?? null) : null}
/>
</div>
{yearlyData.length > 1 && (