feat(compare): at-a-glance, Ofsted, admissions and community sections
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0146VHeLAWjDVE2B5uU67jCB
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* Getting a place — admissions framed the way the expert review requires:
|
||||
* total applications are "named on N forms" (any preference rank, not
|
||||
* head-to-head), one consistent chip metric (first-preference success),
|
||||
* equal-preference and offers-vs-intake explanations up front.
|
||||
*/
|
||||
|
||||
'use client';
|
||||
|
||||
import { summariseAdmissions } from '@/lib/compareLogic';
|
||||
import type { ComparisonData, School } from '@/lib/types';
|
||||
import { CHART_COLORS } from '@/lib/utils';
|
||||
import { Cell, Chip, RowLabel, Section, SectionGrid, sectionStyles as s } from './sectionShared';
|
||||
|
||||
export function CompareAdmissions({
|
||||
schools,
|
||||
data,
|
||||
}: {
|
||||
schools: School[];
|
||||
data: Record<string, ComparisonData>;
|
||||
}) {
|
||||
const rows = schools.map((school) => data[String(school.urn)]?.admissions ?? null);
|
||||
const anyData = rows.some(Boolean);
|
||||
const entryYear = rows.find(Boolean)?.year;
|
||||
const entryLabel = entryYear
|
||||
? `September ${String(entryYear).slice(0, 4)} entry`
|
||||
: 'the most recent admissions round';
|
||||
|
||||
if (!anyData) {
|
||||
return (
|
||||
<Section title="Getting a place" how="No admissions data is available for these schools yet.">
|
||||
<></>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Section
|
||||
title="Getting a place"
|
||||
how={
|
||||
<>
|
||||
From the most recent admissions round ({entryLabel}). "First choice" means
|
||||
families who ranked the school top of their application form — officially a "first
|
||||
preference". Schools never see your ranking: places are decided only by the
|
||||
school's admission criteria, so listing a school lower down never hurts your chances.
|
||||
These are National Offer Day offers — waiting lists and appeals can change the final
|
||||
intake.
|
||||
</>
|
||||
}
|
||||
>
|
||||
<SectionGrid schools={schools}>
|
||||
<RowLabel tip="How many application forms named the school at any preference rank — not the number of families competing head-to-head for a place.">
|
||||
Interest in the school
|
||||
</RowLabel>
|
||||
{schools.map((school, i) => {
|
||||
const a = rows[i];
|
||||
return (
|
||||
<Cell key={school.urn} school={school} index={i}>
|
||||
{a?.total_applications != null && a?.places_offered != null ? (
|
||||
<>
|
||||
Named on <strong>{a.total_applications.toLocaleString('en-GB')}</strong> forms ·{' '}
|
||||
<strong>{a.places_offered.toLocaleString('en-GB')}</strong> places
|
||||
</>
|
||||
) : (
|
||||
<span className={s.small}>No data</span>
|
||||
)}
|
||||
</Cell>
|
||||
);
|
||||
})}
|
||||
|
||||
<RowLabel>First-choice families offered a place</RowLabel>
|
||||
{schools.map((school, i) => {
|
||||
const summary = summariseAdmissions(rows[i]);
|
||||
return (
|
||||
<Cell key={school.urn} school={school} index={i}>
|
||||
{summary.firstPrefPct != null ? (
|
||||
<>
|
||||
<strong>{summary.firstPrefPct}%</strong>{' '}
|
||||
{summary.chip && summary.chip.tone === 'warn' && (
|
||||
<Chip tone="warn">{summary.chip.text}</Chip>
|
||||
)}
|
||||
<span className={s.barMini}>
|
||||
<i
|
||||
style={{
|
||||
width: `${summary.firstPrefPct}%`,
|
||||
background: CHART_COLORS[i % CHART_COLORS.length],
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
<span className={s.small}>No data</span>
|
||||
)}
|
||||
</Cell>
|
||||
);
|
||||
})}
|
||||
|
||||
<RowLabel>What this means</RowLabel>
|
||||
{schools.map((school, i) => {
|
||||
const a = rows[i];
|
||||
const summary = summariseAdmissions(a);
|
||||
let text: string | null = null;
|
||||
if (summary.firstPrefPct != null) {
|
||||
if (summary.firstPrefPct >= 100) {
|
||||
text = `Every family who put ${school.school_name} first got a place.`;
|
||||
} else if (summary.firstPrefPct >= 90) {
|
||||
text = `Nearly every family who put ${school.school_name} first got a place.`;
|
||||
} else if (a?.oversubscribed) {
|
||||
text =
|
||||
'More first-choice applications than places — check the school’s admission criteria (for most non-faith primaries, distance decides).';
|
||||
} else {
|
||||
text = `${summary.firstPrefPct}% of first-choice families received an offer.`;
|
||||
}
|
||||
}
|
||||
return (
|
||||
<Cell key={school.urn} school={school} index={i}>
|
||||
{text ? <span className={s.small}>{text}</span> : <span className={s.small}>—</span>}
|
||||
</Cell>
|
||||
);
|
||||
})}
|
||||
</SectionGrid>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user