Files
school_compare/nextjs-app/components/school/InclusionSection.tsx
T
TudorandClaude Opus 5 a7f6ff4035 refactor(detail): extract sections as server components
Moves ~1,300 lines of section markup out of the two client views into
components/school/, mirroring the components/compare/ layout. Twelve section
components plus shared primitives, all server components. The only client
file is AdmissionsViewToggle, which owns the hidden/aria-pressed state and
receives both views as server-rendered children.

JSX was extracted mechanically rather than retyped, so the markup the CSS
modules depend on is verbatim.

Sharing follows measured similarity, not assumption:
- Finances (91%) shared. The secondary premises-cost card is gated behind a
  prop so primary pages are unchanged; enabling it is a one-line follow-up.
- Ofsted (80%) shared, but behind a variant prop. The headline similarity hid
  a real fork: on a school with no overall grade the primary page shows a
  "Not rated" badge while the secondary shows a four-area OEIF panel, and the
  disclaimer copy differs. Both preserved exactly; reconciling them is a
  human decision, not a side effect of a move.
- Admissions (14%) and History (40%) kept separate.

Not yet wired up -- the old views still render.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 21:33:28 +01:00

103 lines
4.5 KiB
TypeScript

/**
* InclusionSection — pupil characteristics and gender split. Primary pages.
* Server component.
*/
import type { SchoolCensus, SchoolResult } from '@/lib/types';
import { formatPercentage } from '@/lib/utils';
import { MetricTooltip } from '../MetricTooltip';
import { DeltaChip } from '../DeltaChip';
import { Section, sectionStyles as styles } from './sectionShared';
export function InclusionSection({
latestResults, census, hasGenderSplit, primaryAvg,
}: {
latestResults: SchoolResult | null;
census: SchoolCensus | null;
hasGenderSplit: boolean;
primaryAvg: Record<string, number>;
}) {
return (
<section id="inclusion" className={styles.card}>
<h2 className={styles.sectionTitle}>Pupils &amp; Inclusion</h2>
<div className={styles.heroStatGrid}>
{latestResults?.disadvantaged_pct != null && (
<div className={styles.heroStatCard}>
<div className={styles.heroStatLabel}>Eligible for pupil premium</div>
<div className={styles.heroStatValue}>
{formatPercentage(latestResults.disadvantaged_pct)}
{primaryAvg.disadvantaged_pct != null && (
<DeltaChip value={latestResults.disadvantaged_pct} baseline={primaryAvg.disadvantaged_pct} unit="pts" size="sm" />
)}
</div>
<div className={styles.heroStatHint}>Pupils from disadvantaged backgrounds{primaryAvg.disadvantaged_pct != null ? ` · England avg: ${primaryAvg.disadvantaged_pct.toFixed(0)}%` : ''}</div>
</div>
)}
{latestResults?.eal_pct != null && (
<div className={styles.heroStatCard}>
<div className={styles.heroStatLabel}>
English as an additional language
<MetricTooltip metricKey="eal_pct" />
</div>
<div className={styles.heroStatValue}>
{formatPercentage(latestResults.eal_pct)}
{primaryAvg.eal_pct != null && (
<DeltaChip value={latestResults.eal_pct} baseline={primaryAvg.eal_pct} unit="pts" size="sm" />
)}
</div>
{primaryAvg.eal_pct != null && (
<div className={styles.heroStatHint}>England avg: {primaryAvg.eal_pct.toFixed(0)}%</div>
)}
</div>
)}
{latestResults?.sen_support_pct != null && (
<div className={styles.heroStatCard}>
<div className={styles.heroStatLabel}>
Pupils receiving SEN support
<MetricTooltip metricKey="sen_support_pct" />
</div>
<div className={styles.heroStatValue}>
{formatPercentage(latestResults.sen_support_pct)}
{primaryAvg.sen_support_pct != null && (
<DeltaChip value={latestResults.sen_support_pct} baseline={primaryAvg.sen_support_pct} unit="pts" size="sm" />
)}
</div>
{primaryAvg.sen_support_pct != null && (
<div className={styles.heroStatHint}>England avg: {primaryAvg.sen_support_pct.toFixed(0)}%</div>
)}
</div>
)}
{hasGenderSplit && (() => {
const female = census!.female_pupils!;
const male = census!.male_pupils!;
const girlsPct = Math.round((female / (female + male)) * 100);
const boysPct = 100 - girlsPct;
return (
<div className={styles.heroStatCard}>
<div className={styles.heroStatLabel}>Boys and girls</div>
<div className={styles.genderSplitValue}>
<span className={styles.genderSplitGirls}>{girlsPct}%</span>
<span className={styles.genderSplitLabel}>girls</span>
<span className={styles.genderSplitSep}>·</span>
<span className={styles.genderSplitBoys}>{boysPct}%</span>
<span className={styles.genderSplitLabel}>boys</span>
</div>
<div
className={styles.genderBar}
role="img"
aria-label={`Gender split: ${girlsPct}% girls, ${boysPct}% boys`}
>
<span className={styles.genderBarGirls} style={{ width: `${girlsPct}%` }} />
<span className={styles.genderBarBoys} style={{ width: `${boysPct}%` }} />
</div>
<div className={styles.heroStatHint}>
{female.toLocaleString()} girls, {male.toLocaleString()} boys
</div>
</div>
);
})()}
</div>
</section>
);
}