PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m14s
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 1m19s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 11s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 1m20s
Rewrites every visitor-facing string that used an em dash, choosing the punctuation for what the dash was doing: a colon before a list or explanation, a comma for an aside, a full stop between two thoughts, parentheses for an aside mid-sentence. Covers page titles and meta descriptions, the home and admissions guide copy, school page headings and notes, the compare page, metric labels and tooltips. Two rewrites also fix the sentence around them: the closure banner no longer repeats "proposed for closure", and the cut-off caveat's list of priorities now parses. A lone dash marking a missing value in a table cell stays: it is a data convention, not prose. A Jest guard walks the source with the TypeScript parser and fails on any other em dash in a string or JSX text node. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
66 lines
2.6 KiB
TypeScript
66 lines
2.6 KiB
TypeScript
// Server component: pure markup, no client state.
|
||
|
||
import styles from './HomeView.module.css';
|
||
|
||
interface EditorialSectionProps {
|
||
totalSchools: number | null;
|
||
localAuthorityCount: number;
|
||
latestYearLabel: string | null;
|
||
earliestYearLabel: string | null;
|
||
}
|
||
|
||
export function EditorialSection({
|
||
totalSchools,
|
||
localAuthorityCount,
|
||
latestYearLabel,
|
||
earliestYearLabel,
|
||
}: EditorialSectionProps) {
|
||
/*
|
||
* The coverage line replaces a six-row "Coverage at a glance" table.
|
||
*
|
||
* That table put six numbers on the page to say one thing, and the headline
|
||
* row was wrong: it read "24,000+" — its own hardcoded fallback — because
|
||
* DataInfoResponse declared a `total_schools` field the API does not send
|
||
* (it sends `unique_schools`). The fetch had succeeded; only that field was
|
||
* undefined, so the fallback rendered and nothing failed. Every figure here
|
||
* is now live, and any figure that isn't available is dropped rather than
|
||
* replaced with a guess.
|
||
*/
|
||
const coverage = [
|
||
totalSchools ? `${totalSchools.toLocaleString('en-GB')} schools` : null,
|
||
localAuthorityCount > 0 ? `${localAuthorityCount} local authorities` : null,
|
||
earliestYearLabel && latestYearLabel ? `${earliestYearLabel}–${latestYearLabel}` : null,
|
||
].filter(Boolean);
|
||
|
||
return (
|
||
// No class on the section: the page container owns the vertical rhythm
|
||
// now, and this band carries its own ground on the card inside it.
|
||
<section>
|
||
<div className={styles.editorialCard}>
|
||
<div className={styles.sectionHead}>
|
||
<p className={styles.sectionKicker}>About school data</p>
|
||
<h2 className={styles.sectionHeading}>
|
||
Making England's school performance data actually readable
|
||
</h2>
|
||
</div>
|
||
<div className={styles.editorialText}>
|
||
<p>
|
||
School performance data in England is rich but fragmented. The Department for Education and Ofsted
|
||
publish Key Stage 2 SATs, GCSE attainment, inspection outcomes, progress scores, admissions figures
|
||
and demographics, each in its own table with its own jargon.
|
||
</p>
|
||
<p>
|
||
schoolcompare brings it all into one place. Every school page shows performance against the national
|
||
average, explains what the numbers mean, and lets you compare schools side by side. Built for
|
||
parents, governors, journalists, and anyone who wants to understand a school without reading a
|
||
full inspection report.
|
||
</p>
|
||
</div>
|
||
{coverage.length > 0 && (
|
||
<p className={styles.coverageLine}>{coverage.join(' · ')}</p>
|
||
)}
|
||
</div>
|
||
</section>
|
||
);
|
||
}
|