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
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:
@@ -1,9 +1,34 @@
|
||||
.chartOuter {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.trendSummary {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-secondary, #5c564d);
|
||||
padding: 0.5rem 0.875rem;
|
||||
background: var(--bg-secondary, #f3ede4);
|
||||
border-left: 3px solid var(--accent-teal, #2d7d7d);
|
||||
border-radius: 0 4px 4px 0;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.chartWrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.covidNote,
|
||||
.chartHint {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-muted, #8a847a);
|
||||
margin: 0;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.chartWrapper {
|
||||
font-size: 0.875rem;
|
||||
|
||||
@@ -16,217 +16,257 @@ import {
|
||||
Tooltip,
|
||||
Legend,
|
||||
ChartOptions,
|
||||
ChartDataset,
|
||||
} from 'chart.js';
|
||||
import type { SchoolResult } from '@/lib/types';
|
||||
import { formatAcademicYear } from '@/lib/utils';
|
||||
import styles from './PerformanceChart.module.css';
|
||||
|
||||
// Register Chart.js components
|
||||
ChartJS.register(
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
PointElement,
|
||||
LineElement,
|
||||
Title,
|
||||
Tooltip,
|
||||
Legend
|
||||
);
|
||||
ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend);
|
||||
|
||||
interface PerformanceChartProps {
|
||||
data: SchoolResult[];
|
||||
schoolName: string;
|
||||
isSecondary?: boolean;
|
||||
/** National average RWM expected % — rendered as a dashed reference line */
|
||||
nationalRwmAvg?: number | null;
|
||||
/** National average Attainment 8 — rendered as a dashed reference line */
|
||||
nationalAtt8Avg?: number | null;
|
||||
}
|
||||
|
||||
export function PerformanceChart({ data, schoolName, isSecondary = false }: PerformanceChartProps) {
|
||||
// Sort data by year
|
||||
// Academic years when SATs/GCSEs were cancelled due to COVID
|
||||
const COVID_YEARS = new Set([201920, 202021]);
|
||||
|
||||
export function PerformanceChart({
|
||||
data,
|
||||
isSecondary = false,
|
||||
nationalRwmAvg,
|
||||
nationalAtt8Avg,
|
||||
}: PerformanceChartProps) {
|
||||
const sortedData = [...data].sort((a, b) => a.year - b.year);
|
||||
const years = sortedData.map(d => formatAcademicYear(d.year));
|
||||
|
||||
// Prepare datasets — phase-aware
|
||||
const datasets = isSecondary ? [
|
||||
// ── Trend summary (primary only) ──────────────────────────────────────
|
||||
const trendSummary = (() => {
|
||||
if (isSecondary) return null;
|
||||
const rwm = sortedData.filter(d => d.rwm_expected_pct != null);
|
||||
if (rwm.length < 2) return null;
|
||||
const latest = rwm[rwm.length - 1];
|
||||
const prev = rwm[rwm.length - 2];
|
||||
const best = rwm.reduce((a, b) => (b.rwm_expected_pct! > a.rwm_expected_pct! ? b : a));
|
||||
const latestPct = Math.round(latest.rwm_expected_pct!);
|
||||
const bestPct = Math.round(best.rwm_expected_pct!);
|
||||
const delta = latest.rwm_expected_pct! - prev.rwm_expected_pct!;
|
||||
const arrow = delta > 1 ? '↑' : delta < -1 ? '↓' : '→';
|
||||
if (best.year === latest.year) {
|
||||
return `${arrow} Best year on record — ${latestPct}% Reading, Writing & Maths`;
|
||||
}
|
||||
return `${arrow} Peaked at ${bestPct}% (${formatAcademicYear(best.year)}), currently ${latestPct}%`;
|
||||
})();
|
||||
|
||||
// ── COVID gap note ─────────────────────────────────────────────────────
|
||||
const hasCovidGap = isSecondary
|
||||
? false
|
||||
: COVID_YEARS.size > 0 &&
|
||||
[...COVID_YEARS].some(y => !sortedData.find(d => d.year === y));
|
||||
|
||||
// ── Datasets ──────────────────────────────────────────────────────────
|
||||
const refLineStyle = {
|
||||
borderColor: 'rgba(90,80,70,0.35)',
|
||||
backgroundColor: 'transparent',
|
||||
borderWidth: 1.5,
|
||||
borderDash: [6, 4] as number[],
|
||||
pointRadius: 0,
|
||||
tension: 0,
|
||||
order: 10,
|
||||
};
|
||||
|
||||
const datasets: ChartDataset<'line'>[] = isSecondary ? [
|
||||
{
|
||||
label: 'Attainment 8',
|
||||
data: sortedData.map(d => d.attainment_8_score),
|
||||
borderColor: 'rgb(59, 130, 246)',
|
||||
backgroundColor: 'rgba(59, 130, 246, 0.1)',
|
||||
borderColor: '#2d7d7d',
|
||||
backgroundColor: 'rgba(45,125,125,0.08)',
|
||||
borderWidth: 2.5,
|
||||
tension: 0.3,
|
||||
pointRadius: 4,
|
||||
pointHoverRadius: 6,
|
||||
yAxisID: 'y',
|
||||
},
|
||||
{
|
||||
label: 'English & Maths Grade 4+',
|
||||
data: sortedData.map(d => d.english_maths_standard_pass_pct),
|
||||
borderColor: 'rgb(16, 185, 129)',
|
||||
backgroundColor: 'rgba(16, 185, 129, 0.1)',
|
||||
borderColor: '#c9a227',
|
||||
backgroundColor: 'rgba(201,162,39,0.08)',
|
||||
borderWidth: 1.5,
|
||||
tension: 0.3,
|
||||
pointRadius: 3,
|
||||
yAxisID: 'y',
|
||||
},
|
||||
{
|
||||
label: 'Progress 8',
|
||||
data: sortedData.map(d => d.progress_8_score),
|
||||
borderColor: 'rgb(245, 158, 11)',
|
||||
backgroundColor: 'rgba(245, 158, 11, 0.1)',
|
||||
borderColor: 'rgb(139,92,246)',
|
||||
backgroundColor: 'rgba(139,92,246,0.08)',
|
||||
borderWidth: 1.5,
|
||||
tension: 0.3,
|
||||
pointRadius: 3,
|
||||
hidden: true,
|
||||
yAxisID: 'y1',
|
||||
},
|
||||
...(nationalAtt8Avg != null ? [{
|
||||
...refLineStyle,
|
||||
label: 'National average',
|
||||
data: sortedData.map(() => nationalAtt8Avg!),
|
||||
yAxisID: 'y',
|
||||
} as ChartDataset<'line'>] : []),
|
||||
] : [
|
||||
{
|
||||
label: 'Reading, Writing & Maths Expected %',
|
||||
label: 'Reading, Writing & Maths expected %',
|
||||
data: sortedData.map(d => d.rwm_expected_pct),
|
||||
borderColor: 'rgb(59, 130, 246)',
|
||||
backgroundColor: 'rgba(59, 130, 246, 0.1)',
|
||||
borderColor: '#2d7d7d',
|
||||
backgroundColor: 'rgba(45,125,125,0.08)',
|
||||
borderWidth: 2.5,
|
||||
tension: 0.3,
|
||||
pointRadius: 4,
|
||||
pointHoverRadius: 6,
|
||||
yAxisID: 'y',
|
||||
},
|
||||
{
|
||||
label: 'Reading, Writing & Maths Higher %',
|
||||
label: 'Exceeding expected level',
|
||||
data: sortedData.map(d => d.rwm_high_pct),
|
||||
borderColor: 'rgb(16, 185, 129)',
|
||||
backgroundColor: 'rgba(16, 185, 129, 0.1)',
|
||||
borderColor: '#c9a227',
|
||||
backgroundColor: 'rgba(201,162,39,0.08)',
|
||||
borderWidth: 1.5,
|
||||
tension: 0.3,
|
||||
pointRadius: 3,
|
||||
yAxisID: 'y',
|
||||
},
|
||||
...(nationalRwmAvg != null ? [{
|
||||
...refLineStyle,
|
||||
label: 'National average',
|
||||
data: sortedData.map(() => nationalRwmAvg!),
|
||||
yAxisID: 'y',
|
||||
} as ChartDataset<'line'>] : []),
|
||||
{
|
||||
label: 'Reading Progress',
|
||||
label: 'Reading progress',
|
||||
data: sortedData.map(d => d.reading_progress),
|
||||
borderColor: 'rgb(245, 158, 11)',
|
||||
backgroundColor: 'rgba(245, 158, 11, 0.1)',
|
||||
borderColor: 'rgb(59,130,246)',
|
||||
backgroundColor: 'rgba(59,130,246,0.08)',
|
||||
borderWidth: 1.5,
|
||||
tension: 0.3,
|
||||
pointRadius: 3,
|
||||
hidden: true,
|
||||
yAxisID: 'y1',
|
||||
},
|
||||
{
|
||||
label: 'Writing Progress',
|
||||
label: 'Writing progress',
|
||||
data: sortedData.map(d => d.writing_progress),
|
||||
borderColor: 'rgb(139, 92, 246)',
|
||||
backgroundColor: 'rgba(139, 92, 246, 0.1)',
|
||||
borderColor: 'rgb(139,92,246)',
|
||||
backgroundColor: 'rgba(139,92,246,0.08)',
|
||||
borderWidth: 1.5,
|
||||
tension: 0.3,
|
||||
pointRadius: 3,
|
||||
hidden: true,
|
||||
yAxisID: 'y1',
|
||||
},
|
||||
{
|
||||
label: 'Maths Progress',
|
||||
label: 'Maths progress',
|
||||
data: sortedData.map(d => d.maths_progress),
|
||||
borderColor: 'rgb(236, 72, 153)',
|
||||
backgroundColor: 'rgba(236, 72, 153, 0.1)',
|
||||
borderColor: 'rgb(236,72,153)',
|
||||
backgroundColor: 'rgba(236,72,153,0.08)',
|
||||
borderWidth: 1.5,
|
||||
tension: 0.3,
|
||||
pointRadius: 3,
|
||||
hidden: true,
|
||||
yAxisID: 'y1',
|
||||
},
|
||||
];
|
||||
|
||||
const chartData = {
|
||||
labels: years,
|
||||
datasets,
|
||||
};
|
||||
|
||||
const options: ChartOptions<'line'> = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
interaction: {
|
||||
mode: 'index' as const,
|
||||
intersect: false,
|
||||
},
|
||||
interaction: { mode: 'index', intersect: false },
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'top' as const,
|
||||
position: 'top',
|
||||
labels: {
|
||||
usePointStyle: true,
|
||||
padding: 15,
|
||||
font: {
|
||||
size: 12,
|
||||
},
|
||||
},
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: `${schoolName} - Performance Over Time`,
|
||||
font: {
|
||||
size: 16,
|
||||
weight: 'bold',
|
||||
},
|
||||
padding: {
|
||||
bottom: 20,
|
||||
padding: 14,
|
||||
font: { size: 12 },
|
||||
filter: item => item.text !== 'National average' || true,
|
||||
},
|
||||
},
|
||||
title: { display: false },
|
||||
tooltip: {
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.8)',
|
||||
backgroundColor: 'rgba(26,22,18,0.92)',
|
||||
padding: 12,
|
||||
titleFont: {
|
||||
size: 14,
|
||||
},
|
||||
bodyFont: {
|
||||
size: 13,
|
||||
},
|
||||
titleFont: { size: 13 },
|
||||
bodyFont: { size: 12 },
|
||||
callbacks: {
|
||||
label: function(context) {
|
||||
let label = context.dataset.label || '';
|
||||
if (label) {
|
||||
label += ': ';
|
||||
}
|
||||
if (context.parsed.y !== null) {
|
||||
if (context.dataset.yAxisID === 'y1') {
|
||||
// Progress scores
|
||||
label += context.parsed.y.toFixed(1);
|
||||
} else {
|
||||
// Percentages
|
||||
label += context.parsed.y.toFixed(1) + '%';
|
||||
}
|
||||
}
|
||||
return label;
|
||||
}
|
||||
}
|
||||
label: ctx => {
|
||||
const label = ctx.dataset.label ?? '';
|
||||
if (ctx.parsed.y == null) return label;
|
||||
const isProgress = ctx.dataset.yAxisID === 'y1';
|
||||
const suffix = isProgress ? '' : '%';
|
||||
const val = ctx.parsed.y.toFixed(1);
|
||||
return `${label}: ${val}${suffix}`;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
type: 'linear' as const,
|
||||
type: 'linear',
|
||||
display: true,
|
||||
position: 'left' as const,
|
||||
position: 'left',
|
||||
title: {
|
||||
display: true,
|
||||
text: isSecondary ? 'Score / Percentage (%)' : 'Percentage (%)',
|
||||
font: {
|
||||
size: 12,
|
||||
weight: 'bold',
|
||||
},
|
||||
text: isSecondary ? 'Score / %' : 'Percentage (%)',
|
||||
font: { size: 11 },
|
||||
},
|
||||
min: 0,
|
||||
max: isSecondary ? undefined : 100,
|
||||
grid: {
|
||||
color: 'rgba(0, 0, 0, 0.05)',
|
||||
},
|
||||
grid: { color: 'rgba(0,0,0,0.05)' },
|
||||
ticks: { font: { size: 11 } },
|
||||
},
|
||||
y1: {
|
||||
type: 'linear' as const,
|
||||
type: 'linear',
|
||||
display: true,
|
||||
position: 'right' as const,
|
||||
position: 'right',
|
||||
title: {
|
||||
display: true,
|
||||
text: isSecondary ? 'Progress 8 Score' : 'Progress Score',
|
||||
font: {
|
||||
size: 12,
|
||||
weight: 'bold',
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
drawOnChartArea: false,
|
||||
text: isSecondary ? 'Progress 8' : 'Progress score',
|
||||
font: { size: 11 },
|
||||
},
|
||||
grid: { drawOnChartArea: false },
|
||||
ticks: { font: { size: 11 } },
|
||||
},
|
||||
x: {
|
||||
grid: {
|
||||
display: false,
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Year',
|
||||
font: {
|
||||
size: 12,
|
||||
weight: 'bold',
|
||||
},
|
||||
},
|
||||
grid: { display: false },
|
||||
ticks: { font: { size: 11 } },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.chartOuter}>
|
||||
{trendSummary && (
|
||||
<div className={styles.trendSummary}>{trendSummary}</div>
|
||||
)}
|
||||
<div className={styles.chartWrapper}>
|
||||
<Line data={chartData} options={options} />
|
||||
<Line data={{ labels: years, datasets }} options={options} />
|
||||
</div>
|
||||
{hasCovidGap && (
|
||||
<p className={styles.covidNote}>
|
||||
* No data for 2019/20 or 2020/21 — national assessments were cancelled due to COVID-19.
|
||||
</p>
|
||||
)}
|
||||
{!isSecondary && (
|
||||
<p className={styles.chartHint}>
|
||||
Progress scores (Reading, Writing, Maths) are hidden by default — click them in the legend to show.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -635,6 +635,17 @@
|
||||
}
|
||||
|
||||
/* Progress note */
|
||||
.ofstedAllSame {
|
||||
font-size: 0.9375rem;
|
||||
color: var(--text-secondary, #5c564d);
|
||||
margin: 0.5rem 0 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.ofstedAllSame strong {
|
||||
color: var(--text-primary, #1a1612);
|
||||
}
|
||||
|
||||
.progressNote {
|
||||
margin-top: 0.75rem;
|
||||
font-size: 0.8rem;
|
||||
|
||||
@@ -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,6 +398,11 @@ export function SchoolDetailView({
|
||||
<strong>{Math.round(parentView.q_recommend_pct)}%</strong> of parents would recommend this school ({parentView.total_responses.toLocaleString()} responses)
|
||||
</p>
|
||||
)}
|
||||
{oeifAllSameGrade ? (
|
||||
<p className={styles.ofstedAllSame}>
|
||||
Rated <strong>{OFSTED_LABELS[ofsted.overall_effectiveness!]}</strong> across all inspected areas — Quality of Teaching, Behaviour, Pupils' Development and Leadership.
|
||||
</p>
|
||||
) : (
|
||||
<div className={styles.metricsGrid}>
|
||||
{[
|
||||
{ label: 'Quality of Teaching', value: ofsted.quality_of_education },
|
||||
@@ -403,6 +421,7 @@ export function SchoolDetailView({
|
||||
</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 && (
|
||||
|
||||
Reference in New Issue
Block a user