PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m19s
PR Checks / Backend Smoke (pull_request) Successful in 10s
PR Checks / Build Backend (no push) (pull_request) Successful in 19s
PR Checks / Build Frontend (no push) (pull_request) Successful in 1m32s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 1m18s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 14s
"Other schools nearby" was followed by "Other primary schools near <school>.", which says the same thing again. The heading now stands alone. nearbyNoun() and the phase and schoolName props existed only to build that line, so they go with it. Its bottom margin was the only gap between the heading and the cards, so the header row carries that gap now, and centres the heading against the carousel arrows now that it is a single line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
163 lines
5.9 KiB
TypeScript
163 lines
5.9 KiB
TypeScript
/**
|
|
* 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, SchoolAdmissionDistance, SchoolDeprivation, SchoolFinance, NationalAverages,
|
|
NearbySchool,
|
|
} 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 { DistanceSection } from './DistanceSection';
|
|
import { InclusionSection } from './InclusionSection';
|
|
import { HistorySection } from './HistorySection';
|
|
import { SchoolLifeSection } from './SchoolLifeSection';
|
|
import { LocalAreaSection } from './LocalAreaSection';
|
|
import { FinancesSection } from './FinancesSection';
|
|
import { NearbySchoolsSection } from './NearbySchoolsSection';
|
|
|
|
export interface PrimarySchoolSectionsProps {
|
|
schoolInfo: School;
|
|
yearlyData: SchoolResult[];
|
|
absenceData: AbsenceData | null;
|
|
ofsted: OfstedInspection | null;
|
|
census: SchoolCensus | null;
|
|
admissions: SchoolAdmissions | null;
|
|
admissionsHistory: SchoolAdmissions[];
|
|
admissionDistance: SchoolAdmissionDistance | null;
|
|
deprivation: SchoolDeprivation | null;
|
|
finance: SchoolFinance | null;
|
|
nationalAvg: NationalAverages | null;
|
|
/** Nearby schools of a comparable intake. Absent on an older API build. */
|
|
nearbySchools?: NearbySchool[];
|
|
flags: SchoolFlags;
|
|
}
|
|
|
|
export function PrimarySchoolSections({
|
|
schoolInfo, yearlyData, absenceData, ofsted, census,
|
|
admissions, admissionsHistory, admissionDistance,
|
|
deprivation, finance, nationalAvg, nearbySchools, 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}
|
|
/>
|
|
)}
|
|
|
|
{/* Either source is enough to justify the section. The cut-off distance
|
|
and the EES admissions figures come from different places and a
|
|
minority of schools have one without the other — gating on admissions
|
|
alone would hide a published distance on those pages. */}
|
|
{(admissions || admissionDistance) && (
|
|
<AdmissionsSection
|
|
admissions={admissions}
|
|
admissionsHistory={admissionsHistory}
|
|
admissionDistance={admissionDistance}
|
|
isAllThrough={flags.isAllThrough}
|
|
/>
|
|
)}
|
|
|
|
{/* Its own section, directly after Admissions: it answers the question
|
|
the tile above raises. */}
|
|
<DistanceSection admissionDistance={admissionDistance} schoolInfo={schoolInfo} />
|
|
|
|
{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} />}
|
|
|
|
{/* Last: it is where the reader goes next, not part of this school. */}
|
|
<NearbySchoolsSection
|
|
urn={schoolInfo.urn}
|
|
thisMetricValue={flags.latestResults?.rwm_expected_pct ?? null}
|
|
nearby={nearbySchools}
|
|
/>
|
|
</>
|
|
);
|
|
}
|