Compare commits
2
Commits
ec2d12478e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f961bf7f4 | ||
|
|
803e68970c |
@@ -37,8 +37,9 @@ export function renderSchoolDetail(fixture: any) {
|
|||||||
return render(
|
return render(
|
||||||
withProviders(
|
withProviders(
|
||||||
<SchoolDetailShell
|
<SchoolDetailShell
|
||||||
{...fixture}
|
schoolInfo={fixture.schoolInfo}
|
||||||
nationalAvg={nationalAveragesFixture}
|
yearlyData={fixture.yearlyData}
|
||||||
|
census={fixture.census}
|
||||||
navItems={navItems}
|
navItems={navItems}
|
||||||
>
|
>
|
||||||
<PrimarySchoolSections
|
<PrimarySchoolSections
|
||||||
@@ -62,8 +63,9 @@ export function renderSecondarySchoolDetail(fixture: any) {
|
|||||||
return render(
|
return render(
|
||||||
withProviders(
|
withProviders(
|
||||||
<SchoolDetailShell
|
<SchoolDetailShell
|
||||||
{...fixture}
|
schoolInfo={fixture.schoolInfo}
|
||||||
nationalAvg={nationalAveragesFixture}
|
yearlyData={fixture.yearlyData}
|
||||||
|
census={fixture.census}
|
||||||
navItems={navItems}
|
navItems={navItems}
|
||||||
>
|
>
|
||||||
<SecondarySchoolSections
|
<SecondarySchoolSections
|
||||||
|
|||||||
@@ -218,14 +218,7 @@ export default async function SchoolPage({ params }: SchoolPageProps) {
|
|||||||
<SchoolDetailShell
|
<SchoolDetailShell
|
||||||
schoolInfo={school_info}
|
schoolInfo={school_info}
|
||||||
yearlyData={yearly_data}
|
yearlyData={yearly_data}
|
||||||
absenceData={absence_data}
|
|
||||||
ofsted={ofsted ?? null}
|
|
||||||
census={census ?? null}
|
census={census ?? null}
|
||||||
admissions={admissions ?? null}
|
|
||||||
admissionsHistory={admissions_history ?? []}
|
|
||||||
deprivation={deprivation ?? null}
|
|
||||||
finance={finance ?? null}
|
|
||||||
nationalAvg={nationalAvg}
|
|
||||||
navItems={secondaryNavItems}
|
navItems={secondaryNavItems}
|
||||||
>
|
>
|
||||||
<SecondarySchoolSections
|
<SecondarySchoolSections
|
||||||
@@ -245,14 +238,7 @@ export default async function SchoolPage({ params }: SchoolPageProps) {
|
|||||||
<SchoolDetailShell
|
<SchoolDetailShell
|
||||||
schoolInfo={school_info}
|
schoolInfo={school_info}
|
||||||
yearlyData={yearly_data}
|
yearlyData={yearly_data}
|
||||||
absenceData={absence_data}
|
|
||||||
ofsted={ofsted ?? null}
|
|
||||||
census={census ?? null}
|
census={census ?? null}
|
||||||
admissions={admissions ?? null}
|
|
||||||
admissionsHistory={admissions_history ?? []}
|
|
||||||
deprivation={deprivation ?? null}
|
|
||||||
finance={finance ?? null}
|
|
||||||
nationalAvg={nationalAvg}
|
|
||||||
navItems={primaryNavItems}
|
navItems={primaryNavItems}
|
||||||
>
|
>
|
||||||
<PrimarySchoolSections
|
<PrimarySchoolSections
|
||||||
|
|||||||
@@ -20,34 +20,23 @@ import { useEffect, useRef, useState, type ReactNode } from 'react';
|
|||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useComparison } from '@/hooks/useComparison';
|
import { useComparison } from '@/hooks/useComparison';
|
||||||
import { SchoolHeroMap, type SchoolHeroMapHandle } from '../SchoolHeroMap';
|
import { SchoolHeroMap, type SchoolHeroMapHandle } from '../SchoolHeroMap';
|
||||||
import type {
|
import type { School, SchoolResult, SchoolCensus } from '@/lib/types';
|
||||||
School, SchoolResult, AbsenceData,
|
import { formatAgeRange, isProposedToClose } from '@/lib/utils';
|
||||||
OfstedInspection, SchoolCensus,
|
import type { NavItem } from '@/lib/schoolSections';
|
||||||
SchoolAdmissions,
|
|
||||||
SchoolDeprivation, SchoolFinance, NationalAverages,
|
|
||||||
} from '@/lib/types';
|
|
||||||
import {
|
|
||||||
formatPercentage, formatProgress, formatAcademicYear, formatAgeRange, isProposedToClose, ofstedLegacyAreas,
|
|
||||||
} from '@/lib/utils';
|
|
||||||
import { computeSchoolFlags, type NavItem } from '@/lib/schoolSections';
|
|
||||||
|
|
||||||
import { track, getNavigationSource } from '@/lib/analytics';
|
import { track, getNavigationSource } from '@/lib/analytics';
|
||||||
import styles from './SchoolDetailShell.module.css';
|
import styles from './SchoolDetailShell.module.css';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only what the chrome itself renders. Everything the sections need — Ofsted,
|
||||||
|
* admissions, deprivation, finance, national averages — goes straight to the
|
||||||
|
* section composers in page.tsx and never reaches the client.
|
||||||
|
*/
|
||||||
export interface SchoolDetailShellProps {
|
export interface SchoolDetailShellProps {
|
||||||
schoolInfo: School;
|
schoolInfo: School;
|
||||||
|
/** Only for the header's pupil-count fallback. */
|
||||||
yearlyData: SchoolResult[];
|
yearlyData: SchoolResult[];
|
||||||
absenceData: AbsenceData | null;
|
|
||||||
ofsted: OfstedInspection | null;
|
|
||||||
census: SchoolCensus | null;
|
census: SchoolCensus | null;
|
||||||
admissions: SchoolAdmissions | null;
|
|
||||||
admissionsHistory: SchoolAdmissions[];
|
|
||||||
deprivation: SchoolDeprivation | null;
|
|
||||||
finance: SchoolFinance | null;
|
|
||||||
/** Fetched on the server so the England-comparison deltas are in the
|
|
||||||
* initial HTML; null when the endpoint is unavailable. */
|
|
||||||
nationalAvg: NationalAverages | null;
|
|
||||||
/** Section list for the sticky nav, computed on the server. */
|
/** Section list for the sticky nav, computed on the server. */
|
||||||
navItems: NavItem[];
|
navItems: NavItem[];
|
||||||
/** The server-rendered sections. */
|
/** The server-rendered sections. */
|
||||||
@@ -55,9 +44,7 @@ export interface SchoolDetailShellProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function SchoolDetailShell({
|
export function SchoolDetailShell({
|
||||||
schoolInfo, yearlyData, absenceData,
|
schoolInfo, yearlyData, census, navItems, children,
|
||||||
ofsted, census, admissions, admissionsHistory, deprivation, finance,
|
|
||||||
nationalAvg, navItems, children,
|
|
||||||
}: SchoolDetailShellProps) {
|
}: SchoolDetailShellProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { addSchool, removeSchool, isSelected } = useComparison();
|
const { addSchool, removeSchool, isSelected } = useComparison();
|
||||||
@@ -135,21 +122,14 @@ export function SchoolDetailShell({
|
|||||||
return () => window.removeEventListener('keydown', onKey);
|
return () => window.removeEventListener('keydown', onKey);
|
||||||
}, [sectionsOpen]);
|
}, [sectionsOpen]);
|
||||||
|
|
||||||
// Derived data-shape logic lives in lib/schoolSections so the server route
|
// The chrome needs only these four. The section-shape flags are computed
|
||||||
// can compute the section list without importing this client component.
|
// once on the server (lib/schoolSections) and consumed by the section
|
||||||
const flags = computeSchoolFlags({
|
// composers; recomputing them here would duplicate that work for values
|
||||||
schoolInfo, yearlyData, absenceData, census, deprivation, finance,
|
// this component never renders.
|
||||||
});
|
const latestResults = yearlyData.length > 0 ? yearlyData[yearlyData.length - 1] : null;
|
||||||
const {
|
|
||||||
latestResults, isAllThrough, isSecondary, isPrimary,
|
|
||||||
hasGenderSplit, hasInclusionData, hasSchoolLife, hasDeprivation,
|
|
||||||
hasFinance, hasLocation, hasKS2Results, hasKS4Results, hasAnyResults,
|
|
||||||
isSpecial, ks2Placeholder, suppressKs2Comparison, suppressKs4Comparison,
|
|
||||||
} = flags;
|
|
||||||
const phase = schoolInfo.phase ?? '';
|
const phase = schoolInfo.phase ?? '';
|
||||||
|
const isAllThrough = phase.toLowerCase() === 'all-through';
|
||||||
const primaryAvg = nationalAvg?.primary ?? {};
|
const hasLocation = schoolInfo.latitude != null && schoolInfo.longitude != null;
|
||||||
const secondaryAvg = nationalAvg?.secondary ?? {};
|
|
||||||
|
|
||||||
const handleComparisonToggle = () => {
|
const handleComparisonToggle = () => {
|
||||||
if (isInComparison) {
|
if (isInComparison) {
|
||||||
@@ -172,13 +152,6 @@ export function SchoolDetailShell({
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [schoolInfo.urn]);
|
}, [schoolInfo.urn]);
|
||||||
|
|
||||||
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).`;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Track active section as user scrolls
|
// Track active section as user scrolls
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const ids = navItems.map(n => n.id);
|
const ids = navItems.map(n => n.id);
|
||||||
@@ -211,29 +184,6 @@ export function SchoolDetailShell({
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [navItems.map(n => n.id).join(',')]);
|
}, [navItems.map(n => n.id).join(',')]);
|
||||||
|
|
||||||
// A report card is identified by the presence of report-card area
|
|
||||||
// judgements, NOT by `framework` — the API sets `framework` to the raw
|
|
||||||
// event grouping (e.g. "Schools - S5") even for report-card schools, so
|
|
||||||
// the old `framework === 'ReportCard'` test never matched and report cards
|
|
||||||
// were rendered as legacy ratings dated to a pre-Nov-2025 inspection.
|
|
||||||
const isReportCard = !!(
|
|
||||||
ofsted?.report_card && Object.keys(ofsted.report_card).length > 0
|
|
||||||
);
|
|
||||||
// A report card is dated by its own inspection (rc_inspection_date); the
|
|
||||||
// legacy inspection_date belongs to an older inspection and must never
|
|
||||||
// date a report card (report cards exist only from Nov 2025).
|
|
||||||
const ofstedInspectedDate = isReportCard
|
|
||||||
? ofsted?.rc_inspection_date ?? null
|
|
||||||
: ofsted?.inspection_date ?? null;
|
|
||||||
|
|
||||||
// ── Ofsted: detect if all OEIF sub-grades match the overall ───────────
|
|
||||||
const oeifAreas = ofsted ? ofstedLegacyAreas(ofsted) : [];
|
|
||||||
const oeifAllSameGrade =
|
|
||||||
!!ofsted &&
|
|
||||||
!isReportCard &&
|
|
||||||
oeifAreas.length >= 3 &&
|
|
||||||
oeifAreas.every((a) => a.value === ofsted.overall_effectiveness);
|
|
||||||
|
|
||||||
// Label shown in the mobile "section" menu button — the section in view.
|
// Label shown in the mobile "section" menu button — the section in view.
|
||||||
const activeNavLabel = (navItems.find((n) => n.id === activeSection) ?? navItems[0])?.label ?? '';
|
const activeNavLabel = (navItems.find((n) => n.id === activeSection) ?? navItems[0])?.label ?? '';
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user