refactor(detail): render sections on the server behind a client shell
page.tsx now composes the sections and passes them through SchoolDetailShell
as children, so ~1,300 lines of static markup stop shipping as client
JavaScript. The shell keeps what is genuinely interactive: back link, header
reveal, hero map, compare CTA, sticky nav and scroll-spy.
The scroll-spy already located sections via document.getElementById, so it
works unchanged against server-rendered children.
Charts needed a client wrapper: next/dynamic with ssr:false is illegal in a
Server Component, so components/school/charts.tsx is the boundary that keeps
Chart.js (64 KB gz) lazy and browser-only.
Measured on this build:
- school route client chunk: 8 KB gz (33 KB raw)
- total static JS across all chunks: 380.6 -> 350.7 KB gz
- section markup is absent from every client chunk ("Got their first choice",
"Ofsted reports", "Most deprived" etc. all return 0 hits); shell strings
still present, as expected
- shared baseline unchanged at 172 KB gz -- out of scope, as designed
The 14 characterization tests pass byte-identical to the commit that
introduced them. Only the render helper changed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* PrimarySchoolSections — the section sequence for primary and all-through
|
||||
* detail pages. Server component.
|
||||
*
|
||||
* All-through schools route here rather than to SecondarySchoolSections,
|
||||
* because this list renders BOTH the KS2 and KS4 blocks (ResultsSection and
|
||||
* HistorySection branch on isAllThrough); the secondary list is KS4-only and
|
||||
* would silently drop their SATs data.
|
||||
*
|
||||
* The render conditions here MUST match buildNavItems in lib/schoolSections,
|
||||
* or the sticky nav will link to sections that do not exist.
|
||||
*/
|
||||
|
||||
import type {
|
||||
School, SchoolResult, AbsenceData, OfstedInspection, SchoolCensus,
|
||||
SchoolAdmissions, SchoolDeprivation, SchoolFinance, NationalAverages,
|
||||
} from '@/lib/types';
|
||||
import { ofstedLegacyAreas } from '@/lib/utils';
|
||||
import type { SchoolFlags } from '@/lib/schoolSections';
|
||||
import { OfstedSection } from './OfstedSection';
|
||||
import { ResultsSection } from './ResultsSection';
|
||||
import { AdmissionsSection } from './AdmissionsSection';
|
||||
import { InclusionSection } from './InclusionSection';
|
||||
import { HistorySection } from './HistorySection';
|
||||
import { SchoolLifeSection } from './SchoolLifeSection';
|
||||
import { LocalAreaSection } from './LocalAreaSection';
|
||||
import { FinancesSection } from './FinancesSection';
|
||||
|
||||
export interface PrimarySchoolSectionsProps {
|
||||
schoolInfo: School;
|
||||
yearlyData: SchoolResult[];
|
||||
absenceData: AbsenceData | null;
|
||||
ofsted: OfstedInspection | null;
|
||||
census: SchoolCensus | null;
|
||||
admissions: SchoolAdmissions | null;
|
||||
admissionsHistory: SchoolAdmissions[];
|
||||
deprivation: SchoolDeprivation | null;
|
||||
finance: SchoolFinance | null;
|
||||
nationalAvg: NationalAverages | null;
|
||||
flags: SchoolFlags;
|
||||
}
|
||||
|
||||
export function PrimarySchoolSections({
|
||||
schoolInfo, yearlyData, absenceData, ofsted, census,
|
||||
admissions, admissionsHistory, deprivation, finance, nationalAvg, flags,
|
||||
}: PrimarySchoolSectionsProps) {
|
||||
const primaryAvg = nationalAvg?.primary ?? {};
|
||||
const secondaryAvg = nationalAvg?.secondary ?? {};
|
||||
|
||||
const isReportCard = !!(ofsted?.report_card && Object.keys(ofsted.report_card).length > 0);
|
||||
// Report cards are dated by their own inspection (rc_inspection_date), never
|
||||
// the legacy inspection_date (report cards exist only from Nov 2025).
|
||||
const ofstedInspectedDate = isReportCard
|
||||
? ofsted?.rc_inspection_date ?? null
|
||||
: ofsted?.inspection_date ?? null;
|
||||
const oeifAreas = ofsted ? ofstedLegacyAreas(ofsted) : [];
|
||||
const oeifAllSameGrade =
|
||||
!!ofsted &&
|
||||
!isReportCard &&
|
||||
oeifAreas.length >= 3 &&
|
||||
oeifAreas.every((a) => a.value === ofsted.overall_effectiveness);
|
||||
|
||||
return (
|
||||
<>
|
||||
{ofsted && (
|
||||
<OfstedSection
|
||||
ofsted={ofsted}
|
||||
urn={schoolInfo.urn}
|
||||
isReportCard={isReportCard}
|
||||
ofstedInspectedDate={ofstedInspectedDate}
|
||||
oeifAllSameGrade={oeifAllSameGrade}
|
||||
oeifAreas={oeifAreas}
|
||||
variant="primary"
|
||||
/>
|
||||
)}
|
||||
|
||||
{flags.hasAnyResults && flags.latestResults && (
|
||||
<ResultsSection
|
||||
latestResults={flags.latestResults}
|
||||
schoolInfo={schoolInfo}
|
||||
primaryAvg={primaryAvg}
|
||||
secondaryAvg={secondaryAvg}
|
||||
isAllThrough={flags.isAllThrough}
|
||||
isSecondary={flags.isSecondary}
|
||||
isSpecial={flags.isSpecial}
|
||||
hasKS2Results={flags.hasKS2Results}
|
||||
hasKS4Results={flags.hasKS4Results}
|
||||
ks2Placeholder={flags.ks2Placeholder}
|
||||
suppressKs2Comparison={flags.suppressKs2Comparison}
|
||||
suppressKs4Comparison={flags.suppressKs4Comparison}
|
||||
/>
|
||||
)}
|
||||
|
||||
{admissions && (
|
||||
<AdmissionsSection
|
||||
admissions={admissions}
|
||||
admissionsHistory={admissionsHistory}
|
||||
isAllThrough={flags.isAllThrough}
|
||||
/>
|
||||
)}
|
||||
|
||||
{flags.hasInclusionData && (
|
||||
<InclusionSection
|
||||
latestResults={flags.latestResults}
|
||||
census={census}
|
||||
hasGenderSplit={flags.hasGenderSplit}
|
||||
primaryAvg={primaryAvg}
|
||||
/>
|
||||
)}
|
||||
|
||||
{yearlyData.length > 0 && (
|
||||
<HistorySection
|
||||
yearlyData={yearlyData}
|
||||
schoolInfo={schoolInfo}
|
||||
nationalAvg={nationalAvg}
|
||||
primaryAvg={primaryAvg}
|
||||
secondaryAvg={secondaryAvg}
|
||||
isAllThrough={flags.isAllThrough}
|
||||
isPrimary={flags.isPrimary}
|
||||
isSecondary={flags.isSecondary}
|
||||
hasKS2Results={flags.hasKS2Results}
|
||||
hasKS4Results={flags.hasKS4Results}
|
||||
suppressKs2Comparison={flags.suppressKs2Comparison}
|
||||
suppressKs4Comparison={flags.suppressKs4Comparison}
|
||||
/>
|
||||
)}
|
||||
|
||||
{flags.hasSchoolLife && (
|
||||
<SchoolLifeSection absenceData={absenceData} primaryAvg={primaryAvg} />
|
||||
)}
|
||||
|
||||
{flags.hasDeprivation && deprivation && (
|
||||
<LocalAreaSection deprivation={deprivation} />
|
||||
)}
|
||||
|
||||
{flags.hasFinance && finance && <FinancesSection finance={finance} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user