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>
157 lines
5.9 KiB
TypeScript
157 lines
5.9 KiB
TypeScript
/**
|
|
* SecondarySchoolSections — the section sequence for secondary detail pages.
|
|
* Server component.
|
|
*
|
|
* Wrapped in `.secondaryScope`, which activates the secondary-only style
|
|
* overrides in schoolSections.module.css. Those rules target class names the
|
|
* primary page also uses (.card, .sectionTitle, .metricCard …), so scoping is
|
|
* what keeps them from restyling primary pages.
|
|
*
|
|
* The render conditions here MUST match buildSecondaryNavItems 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,
|
|
SchoolDestinations, NearbySchool,
|
|
} from '@/lib/types';
|
|
import { ofstedLegacyAreas } from '@/lib/utils';
|
|
import type { SecondaryFlags } from '@/lib/schoolSections';
|
|
import { OfstedSection } from './OfstedSection';
|
|
import { GcseSection } from './GcseSection';
|
|
import { DestinationsSection } from './DestinationsSection';
|
|
import { Post16DestinationsSection } from './Post16DestinationsSection';
|
|
import { SecondaryAdmissionsSection } from './SecondaryAdmissionsSection';
|
|
import { DistanceSection } from './DistanceSection';
|
|
import { SecondaryHistorySection } from './SecondaryHistorySection';
|
|
import { WellbeingSection } from './WellbeingSection';
|
|
import { FinancesSection } from './FinancesSection';
|
|
import { NearbySchoolsSection } from './NearbySchoolsSection';
|
|
import styles from './schoolSections.module.css';
|
|
|
|
export interface SecondarySchoolSectionsProps {
|
|
schoolInfo: School;
|
|
yearlyData: SchoolResult[];
|
|
absenceData: AbsenceData | null;
|
|
ofsted: OfstedInspection | null;
|
|
census: SchoolCensus | null;
|
|
admissions: SchoolAdmissions | null;
|
|
/** Needed to tell a year with no published cut-off apart from a year the
|
|
* school simply was not oversubscribed. */
|
|
admissionsHistory: SchoolAdmissions[];
|
|
/** Absent — not null — while the admission_distance flag is off. The two
|
|
* mean different things to the reader and must stay distinguishable:
|
|
* see SecondaryAdmissionsSection, which words the absence. */
|
|
admissionDistance: SchoolAdmissionDistance | null | undefined;
|
|
deprivation: SchoolDeprivation | null;
|
|
finance: SchoolFinance | null;
|
|
nationalAvg: NationalAverages | null;
|
|
destinations: SchoolDestinations | null;
|
|
/** Nearby schools of a comparable intake. Absent on an older API build. */
|
|
nearbySchools?: NearbySchool[];
|
|
flags: SecondaryFlags;
|
|
}
|
|
|
|
export function SecondarySchoolSections({
|
|
schoolInfo, yearlyData, ofsted, census,
|
|
admissions, admissionsHistory, admissionDistance,
|
|
deprivation, finance, nationalAvg, destinations, nearbySchools, flags,
|
|
}: SecondarySchoolSectionsProps) {
|
|
const secondaryAvg = nationalAvg?.secondary ?? {};
|
|
|
|
const isReportCard = !!(ofsted?.report_card && Object.keys(ofsted.report_card).length > 0);
|
|
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 (
|
|
<div className={styles.secondaryScope}>
|
|
{ofsted && (
|
|
<OfstedSection
|
|
ofsted={ofsted}
|
|
urn={schoolInfo.urn}
|
|
isReportCard={isReportCard}
|
|
ofstedInspectedDate={ofstedInspectedDate}
|
|
oeifAllSameGrade={oeifAllSameGrade}
|
|
oeifAreas={oeifAreas}
|
|
variant="secondary"
|
|
/>
|
|
)}
|
|
|
|
{flags.hasResults && flags.latestResults && (
|
|
<GcseSection
|
|
latestResults={flags.latestResults}
|
|
schoolInfo={schoolInfo}
|
|
secondaryAvg={secondaryAvg}
|
|
p8Suspended={flags.p8Suspended}
|
|
suppressComparison={flags.suppressComparison}
|
|
/>
|
|
)}
|
|
|
|
{/* Destinations follow attainment: they answer "and then what happened",
|
|
which only lands once the results are in view. */}
|
|
{flags.hasKs4Destinations && destinations?.ks4 && (
|
|
<DestinationsSection destinations={destinations.ks4} />
|
|
)}
|
|
|
|
{/* Sixth-form schools only. Absent, not placeheld, for a school with no
|
|
post-16 provision — the question simply does not apply there. */}
|
|
{flags.hasKs5Destinations && destinations?.ks5 && (
|
|
<Post16DestinationsSection destinations={destinations.ks5} />
|
|
)}
|
|
|
|
{/* See PrimarySchoolSections: distance and EES admissions are independent
|
|
sources, so either one warrants the section. */}
|
|
{(admissions || admissionDistance) && (
|
|
<SecondaryAdmissionsSection
|
|
admissions={admissions}
|
|
admissionDistance={admissionDistance}
|
|
admissionsHistory={admissionsHistory}
|
|
schoolInfo={schoolInfo}
|
|
/>
|
|
)}
|
|
|
|
{/* See PrimarySchoolSections — same placement, same reason. */}
|
|
<DistanceSection admissionDistance={admissionDistance} schoolInfo={schoolInfo} />
|
|
|
|
{yearlyData.length > 1 && (
|
|
<SecondaryHistorySection
|
|
yearlyData={yearlyData}
|
|
schoolInfo={schoolInfo}
|
|
nationalAvg={nationalAvg}
|
|
secondaryAvg={secondaryAvg}
|
|
suppressComparison={flags.suppressComparison}
|
|
/>
|
|
)}
|
|
|
|
{flags.hasWellbeing && (
|
|
<WellbeingSection
|
|
latestResults={flags.latestResults}
|
|
census={census}
|
|
schoolInfo={schoolInfo}
|
|
deprivation={deprivation}
|
|
hasDeprivation={flags.hasDeprivation}
|
|
/>
|
|
)}
|
|
|
|
{flags.hasFinance && finance && (
|
|
<FinancesSection finance={finance} showPremises />
|
|
)}
|
|
|
|
{/* Last: it is where the reader goes next, not part of this school. */}
|
|
<NearbySchoolsSection
|
|
urn={schoolInfo.urn}
|
|
thisMetricValue={flags.latestResults?.attainment_8_score ?? null}
|
|
nearby={nearbySchools}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|