From b31e71ac884df6507f567adc046c9fc52d9d310a Mon Sep 17 00:00:00 2001 From: Tudor Date: Sat, 18 Jul 2026 19:09:16 +0100 Subject: [PATCH] fix(detail): render report cards and date them by the report-card inspection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The detail views detected report cards via ofsted.framework === 'ReportCard', but the API sets framework to the raw event grouping ('Schools - S5') even for report-card schools — so the check never matched: report-card schools rendered as legacy 'Ofsted Rating' badges dated to a pre-Nov-2025 inspection (e.g. Barclay shown as 'Outstanding, Inspected 7 October 2021' instead of its Feb 2026 report card). Detect report cards by the presence of the report_card object (matching the compare screen), and date them with rc_inspection_date, never the legacy inspection_date. Applies to both primary and secondary detail views. New e2e assertion against a real report-card school gates it. Note: the list/map surface has the same latent issue via ofsted_framework (LeafletMapInner) — flagged as a separate follow-up. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_0146VHeLAWjDVE2B5uU67jCB --- e2e/tests/journeys.spec.ts | 27 +++++++++++++++++++ nextjs-app/components/SchoolDetailView.tsx | 25 +++++++++++++---- .../components/SecondarySchoolDetailView.tsx | 24 +++++++++++++---- 3 files changed, 66 insertions(+), 10 deletions(-) diff --git a/e2e/tests/journeys.spec.ts b/e2e/tests/journeys.spec.ts index 7bc9401..751f47c 100644 --- a/e2e/tests/journeys.spec.ts +++ b/e2e/tests/journeys.spec.ts @@ -94,6 +94,33 @@ test('school detail page renders name and performance data', async ({ page }) => await expect(page.locator('canvas:visible').first()).toBeVisible({ timeout: 15_000 }); }); +test('a report-card school shows its report card, dated to the report-card inspection', async ({ page }) => { + // Detail views detected report cards via `framework`, which the API never + // sets to "ReportCard" — so report-card schools rendered as legacy ratings + // dated to a pre-Nov-2025 inspection. Detection now keys off the report_card + // object and dates it with rc_inspection_date. + const RC_URN = 138690; // Barclay Primary — has a Nov-2025+ report card + const res = await page.request.get(`/api/schools/${RC_URN}`); + expect(res.ok()).toBeTruthy(); + const ofsted = (await res.json()).ofsted; + test.skip( + !ofsted?.report_card || Object.keys(ofsted.report_card).length === 0, + 'precondition: chosen URN must currently have a report card', + ); + const rcYear = new Date(ofsted.rc_inspection_date).getFullYear(); + const legacyYear = new Date(ofsted.inspection_date).getFullYear(); + + await page.goto(`/school/${RC_URN}`); + const ofstedSection = page.locator('#ofsted'); + // Detection fixed: rendered as a Report Card, not a legacy "Ofsted Rating". + await expect(ofstedSection.getByText('Ofsted Report Card')).toBeVisible({ timeout: 15_000 }); + // Dating fixed: dated to the report-card inspection, never the legacy one. + await expect(ofstedSection.getByText(new RegExp(`Inspected .*${rcYear}`))).toBeVisible(); + if (legacyYear !== rcYear) { + await expect(ofstedSection.getByText(new RegExp(`Inspected .*${legacyYear}`))).toHaveCount(0); + } +}); + test('school with no performance data still gets a working detail page', async ({ page }) => { // Schools without KS2/KS4 results (special post-16 institutions, sixth-form // centres, PRUs) used to 500 in the API — NaN GIAS fields broke JSON diff --git a/nextjs-app/components/SchoolDetailView.tsx b/nextjs-app/components/SchoolDetailView.tsx index e81143a..796efa6 100644 --- a/nextjs-app/components/SchoolDetailView.tsx +++ b/nextjs-app/components/SchoolDetailView.tsx @@ -269,9 +269,24 @@ export function SchoolDetailView({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [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 oeifAllSameGrade = (() => { - if (!ofsted || ofsted.framework === 'ReportCard') return false; + if (!ofsted || isReportCard) return false; const subs = [ ofsted.quality_of_education, ofsted.behaviour_attitudes, @@ -507,10 +522,10 @@ export function SchoolDetailView({ {ofsted && (

- {ofsted.framework === 'ReportCard' ? 'Ofsted Report Card' : 'Ofsted Rating'} - {ofsted.inspection_date && ( + {isReportCard ? 'Ofsted Report Card' : 'Ofsted Rating'} + {ofstedInspectedDate && ( - Inspected {new Date(ofsted.inspection_date).toLocaleDateString('en-GB', { day: 'numeric', month: 'long', year: 'numeric' })} + Inspected {new Date(ofstedInspectedDate).toLocaleDateString('en-GB', { day: 'numeric', month: 'long', year: 'numeric' })} )}

- {ofsted.framework === 'ReportCard' ? ( + {isReportCard ? ( /* ── New Report Card layout ── */ <>

diff --git a/nextjs-app/components/SecondarySchoolDetailView.tsx b/nextjs-app/components/SecondarySchoolDetailView.tsx index 58b33db..9b8ee98 100644 --- a/nextjs-app/components/SecondarySchoolDetailView.tsx +++ b/nextjs-app/components/SecondarySchoolDetailView.tsx @@ -186,9 +186,23 @@ export function SecondarySchoolDetailView({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [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 + ); + // 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; + // ── Ofsted: detect if all OEIF sub-grades match the overall ─────────── const oeifAllSameGrade = (() => { - if (!ofsted || ofsted.framework === 'ReportCard') return false; + if (!ofsted || isReportCard) return false; const subs = [ ofsted.quality_of_education, ofsted.behaviour_attitudes, @@ -332,10 +346,10 @@ export function SecondarySchoolDetailView({ {ofsted && (

- {ofsted.framework === 'ReportCard' ? 'Ofsted Report Card' : 'Ofsted Rating'} - {ofsted.inspection_date && ( + {isReportCard ? 'Ofsted Report Card' : 'Ofsted Rating'} + {ofstedInspectedDate && ( - {' '}Inspected {new Date(ofsted.inspection_date).toLocaleDateString('en-GB', { day: 'numeric', month: 'long', year: 'numeric' })} + {' '}Inspected {new Date(ofstedInspectedDate).toLocaleDateString('en-GB', { day: 'numeric', month: 'long', year: 'numeric' })} )}

- {ofsted.framework === 'ReportCard' ? ( + {isReportCard ? ( <>

From November 2025, Ofsted replaced single overall grades with Report Cards rating schools across several areas.