feat(web): render similar schools on both detail templates

Inside SchoolDetailShell rather than after it, because the sticky nav's
scroll-spy finds sections with getElementById and can only reach one that
lives in the shell. Last in the order, and last in the nav, because the two
must agree or the nav links to an anchor that was never rendered.

hasSimilarSchools is optional on NavItemsInput, matching hasLocation beside
it: absent has to mean "no section", and making it required would have
churned ten unrelated call sites for no added safety.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
TudorandClaude Opus 5 committed 2026-09-21 22:42:59 +01:00
1 parent 91314a80b5
commit 4e0d8bcf87
5 files changed
+86 -5

No files matched your search

@@ -14,6 +14,7 @@
import type {
School, SchoolResult, AbsenceData, OfstedInspection, SchoolCensus,
SchoolAdmissions, SchoolAdmissionDistance, SchoolDeprivation, SchoolFinance, NationalAverages,
SimilarSchool,
} from '@/lib/types';
import { ofstedLegacyAreas } from '@/lib/utils';
import type { SchoolFlags } from '@/lib/schoolSections';
@@ -26,6 +27,7 @@ import { HistorySection } from './HistorySection';
import { SchoolLifeSection } from './SchoolLifeSection';
import { LocalAreaSection } from './LocalAreaSection';
import { FinancesSection } from './FinancesSection';
import { SimilarSchoolsSection } from './SimilarSchoolsSection';
export interface PrimarySchoolSectionsProps {
schoolInfo: School;
@@ -39,13 +41,15 @@ export interface PrimarySchoolSectionsProps {
deprivation: SchoolDeprivation | null;
finance: SchoolFinance | null;
nationalAvg: NationalAverages | null;
/** Nearby schools of a comparable intake. Absent on an older API build. */
similarSchools?: SimilarSchool[];
flags: SchoolFlags;
}
export function PrimarySchoolSections({
schoolInfo, yearlyData, absenceData, ofsted, census,
admissions, admissionsHistory, admissionDistance,
deprivation, finance, nationalAvg, flags,
deprivation, finance, nationalAvg, similarSchools, flags,
}: PrimarySchoolSectionsProps) {
const primaryAvg = nationalAvg?.primary ?? {};
const secondaryAvg = nationalAvg?.secondary ?? {};
@@ -146,6 +150,15 @@ export function PrimarySchoolSections({
)}
{flags.hasFinance && finance && <FinancesSection finance={finance} />}
{/* Last: it is where the reader goes next, not part of this school. */}
<SimilarSchoolsSection
urn={schoolInfo.urn}
schoolName={schoolInfo.school_name}
phaseNoun={flags.isAllThrough ? 'all-through' : 'primary'}
thisMetricValue={flags.latestResults?.rwm_expected_pct ?? null}
similar={similarSchools}
/>
</>
);
}
@@ -14,7 +14,7 @@
import type {
School, SchoolResult, AbsenceData, OfstedInspection, SchoolCensus,
SchoolAdmissions, SchoolAdmissionDistance, SchoolDeprivation, SchoolFinance, NationalAverages,
SchoolDestinations,
SchoolDestinations, SimilarSchool,
} from '@/lib/types';
import { ofstedLegacyAreas } from '@/lib/utils';
import type { SecondaryFlags } from '@/lib/schoolSections';
@@ -27,6 +27,7 @@ import { DistanceSection } from './DistanceSection';
import { SecondaryHistorySection } from './SecondaryHistorySection';
import { WellbeingSection } from './WellbeingSection';
import { FinancesSection } from './FinancesSection';
import { SimilarSchoolsSection } from './SimilarSchoolsSection';
import styles from './schoolSections.module.css';
export interface SecondarySchoolSectionsProps {
@@ -47,13 +48,15 @@ export interface SecondarySchoolSectionsProps {
finance: SchoolFinance | null;
nationalAvg: NationalAverages | null;
destinations: SchoolDestinations | null;
/** Nearby schools of a comparable intake. Absent on an older API build. */
similarSchools?: SimilarSchool[];
flags: SecondaryFlags;
}
export function SecondarySchoolSections({
schoolInfo, yearlyData, ofsted, census,
admissions, admissionsHistory, admissionDistance,
deprivation, finance, nationalAvg, destinations, flags,
deprivation, finance, nationalAvg, destinations, similarSchools, flags,
}: SecondarySchoolSectionsProps) {
const secondaryAvg = nationalAvg?.secondary ?? {};
@@ -141,6 +144,15 @@ export function SecondarySchoolSections({
{flags.hasFinance && finance && (
<FinancesSection finance={finance} showPremises />
)}
{/* Last: it is where the reader goes next, not part of this school. */}
<SimilarSchoolsSection
urn={schoolInfo.urn}
schoolName={schoolInfo.school_name}
phaseNoun="secondary"
thisMetricValue={flags.latestResults?.attainment_8_score ?? null}
similar={similarSchools}
/>
</div>
);
}