diff --git a/nextjs-app/components/SchoolDetailView.module.css b/nextjs-app/components/SchoolDetailView.module.css index 14c434b..7191417 100644 --- a/nextjs-app/components/SchoolDetailView.module.css +++ b/nextjs-app/components/SchoolDetailView.module.css @@ -8,7 +8,7 @@ border: 1px solid var(--border-color, #e5dfd5); border-radius: 10px; padding: 1.25rem 1.5rem; - margin-bottom: 1rem; + margin-bottom: 0; box-shadow: var(--shadow-soft); } @@ -117,41 +117,51 @@ opacity: 0.9; } -/* Quick Summary Strip */ -.summaryStrip { - display: flex; - gap: 0.625rem; - flex-wrap: wrap; - margin: 0 0 1.25rem; +/* ── Sticky Section Navigation ──────────────────────── */ +.sectionNav { + position: sticky; + top: 0; + z-index: 10; + background: var(--bg-card, white); + border: 1px solid var(--border-color, #e5dfd5); + border-top: none; + border-radius: 0 0 10px 10px; + padding: 0.5rem 1rem; + margin-bottom: 1rem; + overflow-x: auto; + white-space: nowrap; + -webkit-overflow-scrolling: touch; + scrollbar-width: none; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04); } -.summaryPill { - padding: 0.35rem 0.875rem; - border-radius: 999px; - font-size: 0.8125rem; - font-weight: 600; +.sectionNav::-webkit-scrollbar { + display: none; +} + +.sectionNavInner { display: inline-flex; - align-items: center; - gap: 0.3rem; + gap: 0.375rem; } -.summaryPillGood { - background: #d1fae5; - color: #065f46; +.sectionNavLink { + display: inline-block; + padding: 0.3rem 0.625rem; + font-size: 0.75rem; + font-weight: 500; + color: var(--text-secondary, #5c564d); + text-decoration: none; + border-radius: 4px; + transition: all 0.15s ease; + white-space: nowrap; } -.summaryPillWarn { - background: #fef3c7; - color: #92400e; +.sectionNavLink:hover { + background: var(--bg-secondary, #f3ede4); + color: var(--text-primary, #1a1612); } -.summaryPillBad { - background: #fee2e2; - color: #991b1b; -} - -/* Unified card — replaces summary / chartsSection / detailedMetrics / - absenceSection / historySection / supplementarySection / mapSection */ +/* Unified card for all content sections */ .card { background: var(--bg-card, white); border: 1px solid var(--border-color, #e5dfd5); @@ -159,6 +169,7 @@ padding: 1.25rem 1.5rem; margin-bottom: 1rem; box-shadow: var(--shadow-soft); + scroll-margin-top: 3.5rem; } /* Section Title */ @@ -192,7 +203,7 @@ margin: -0.5rem 0 1rem; } -/* Response count badge (used in "What Parents Say") */ +/* Response count badge */ .responseBadge { font-size: 0.75rem; font-weight: 500; @@ -211,6 +222,18 @@ margin: 1.25rem 0 0.75rem; } +/* Parent recommendation line in Ofsted section */ +.parentRecommendLine { + font-size: 0.85rem; + color: var(--text-secondary, #5c564d); + margin: 0.5rem 0 0; +} + +.parentRecommendLine strong { + color: var(--accent-teal, #2d7d7d); + font-weight: 700; +} + /* Metrics Grid & Cards */ .metricsGrid { display: grid; @@ -252,11 +275,6 @@ font-style: italic; } -.metricTrend { - font-size: 1rem; - color: var(--accent-teal, #2d7d7d); -} - /* Progress score colour coding */ .progressPositive { color: var(--accent-teal, #2d7d7d); @@ -268,6 +286,22 @@ font-weight: 700; } +/* ── Semantic status colours (unified) ────────────── */ +.statusGood { + background: rgba(45, 125, 125, 0.10); + color: var(--accent-teal, #2d7d7d); +} + +.statusWarn { + background: rgba(201, 162, 39, 0.12); + color: #b8920e; +} + +.statusBad { + background: rgba(224, 114, 86, 0.12); + color: var(--accent-coral, #e07256); +} + /* Charts Section */ .chartContainer { width: 100%; @@ -339,6 +373,12 @@ margin-top: 0.5rem; } +.historicalSubtitle { + font-size: 0.8rem; + color: var(--text-muted, #8a847a); + margin: 1.25rem 0 0.25rem; +} + .dataTable { width: 100%; border-collapse: collapse; @@ -468,7 +508,7 @@ color: var(--text-primary, #1a1612); } -/* Admissions badges */ +/* Admissions badge — uses unified status colours */ .admissionsBadge { display: inline-flex; align-items: center; @@ -480,16 +520,6 @@ margin-top: 0.75rem; } -.admissionsBadgeWarn { - background: rgba(201, 162, 39, 0.15); - color: #b8920e; -} - -.admissionsBadgeGood { - background: rgba(60, 140, 60, 0.12); - color: #3c8c3c; -} - /* Deprivation dot scale */ .deprivationDots { display: flex; diff --git a/nextjs-app/components/SchoolDetailView.tsx b/nextjs-app/components/SchoolDetailView.tsx index d9afa6e..d357b33 100644 --- a/nextjs-app/components/SchoolDetailView.tsx +++ b/nextjs-app/components/SchoolDetailView.tsx @@ -36,17 +36,10 @@ const NATIONAL_AVG = { per_pupil_spend: 6000, }; -function pillClass(pct: number | null | undefined): string { - if (pct == null) return styles.summaryPillWarn; - if (pct >= 80) return styles.summaryPillGood; - if (pct >= 60) return styles.summaryPillWarn; - return styles.summaryPillBad; -} - function progressClass(val: number | null | undefined): string { if (val == null) return ''; - if (val > 0.1) return styles.progressPositive; - if (val < -0.1) return styles.progressNegative; + if (val > 0) return styles.progressPositive; + if (val < 0) return styles.progressNegative; return ''; } @@ -82,13 +75,39 @@ export function SchoolDetailView({ } }; - // Deprivation plain-English description const deprivationDesc = (decile: number) => { 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).`; }; + // Guard for Pupils & Inclusion — only show if at least one metric is available + const hasInclusionData = (latestResults?.disadvantaged_pct != null) + || (latestResults?.eal_pct != null) + || (latestResults?.sen_support_pct != null) + || senDetail != null; + + const hasSchoolLife = absenceData != null || census?.class_size_avg != null; + const hasPhonics = phonics != null && phonics.year1_phonics_pct != null; + const hasDeprivation = deprivation != null && deprivation.idaci_decile != null; + const hasFinance = finance != null && finance.per_pupil_spend != null; + const hasLocation = schoolInfo.latitude != null && schoolInfo.longitude != null; + + // Build section nav items dynamically — only sections with data + const navItems: { id: string; label: string }[] = []; + if (ofsted) navItems.push({ id: 'ofsted', label: 'Ofsted' }); + if (parentView && parentView.total_responses != null && parentView.total_responses > 0) + navItems.push({ id: 'parents', label: 'Parents' }); + if (latestResults) navItems.push({ id: 'sats', label: 'SATs' }); + if (hasPhonics) navItems.push({ id: 'phonics', label: 'Phonics' }); + if (hasSchoolLife) navItems.push({ id: 'school-life', label: 'School Life' }); + if (admissions) navItems.push({ id: 'admissions', label: 'Admissions' }); + if (hasInclusionData) navItems.push({ id: 'inclusion', label: 'Pupils' }); + if (hasLocation) navItems.push({ id: 'location', label: 'Location' }); + if (hasDeprivation) navItems.push({ id: 'local-area', label: 'Local Area' }); + if (hasFinance) navItems.push({ id: 'finances', label: 'Finances' }); + if (yearlyData.length > 0) navItems.push({ id: 'history', label: 'History' }); + return (
+ {Math.round(parentView.q_recommend_pct)}% of parents would recommend this school ({parentView.total_responses.toLocaleString()} responses) +
+ )}End-of-primary-school tests taken by Year 6 pupils. National averages shown for comparison.
+ + {/* Headline numbers: RWM combined */}- Progress scores measure how much pupils improved compared to similar schools nationally. Above 0 = better than average, below 0 = below average. -
- )} -+ Progress scores measure how much pupils improved compared to similar schools nationally. Above 0 = better than average, below 0 = below average. +
+ )}
Phonics is a key early reading skill. Children are tested at the end of Year 1.
@@ -438,8 +427,8 @@ export function SchoolDetailView({
)}
{/* School Life */}
- {(absenceData || census?.class_size_avg != null) && (
- {deprivationDesc(deprivation.idaci_decile)} {deprivationDesc(deprivation.idaci_decile!)}
Per-pupil spending shows how much the school has to spend on each child's education.
@@ -597,7 +586,7 @@ export function SchoolDetailView({
Detailed year-by-year figuresSchool Life
How Hard to Get Into This School
+ How Hard to Get Into This School ({admissions.year})
Pupils & Inclusion
Location
Local Area Context
School Finances ({finance.year})
Results Over Time
Year-by-Year Summary
-
-
-
-
-
-
-
- {yearlyData.map((result) => (
- Year
- Reading, Writing & Maths (expected %)
- Exceeding expected (%)
- Reading Progress
- Writing Progress
- Maths Progress
-
-
- ))}
-
- {result.year}
- {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) : '-'}
-
+
+
+
+
+
+
+ {yearlyData.map((result) => (
+ Year
+ Reading, Writing & Maths (expected %)
+ Exceeding expected (%)
+ Reading Progress
+ Writing Progress
+ Maths Progress
+
+
+ ))}
+
+ {result.year}
+ {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) : '-'}
+