'use client'; /** * DestinationsView — the interactive body of both destination sections. * * Three question cards over one bar, with the cards acting as a lens on the * bar rather than a summary beside it: focusing a card dims everything the * card is not made of, so the grouping we chose is inspectable rather than * asserted. * * Everything here defers to lib/destinations.ts for what may be shown. In * particular the bar is rendered only when canRenderBar() allows it: the * destination categories sum to the cohort, so a bar drawn from the published * segments leaves a gap whose width IS the withheld figure. * * The one client component in this directory besides AdmissionsViewToggle. * The all-pupils view is what the server renders into the HTML; the switch and * the hover linkage are the only parts that need the browser. */ import { useState } from 'react'; import type { DestinationPhase, DestinationGroupPayload } from '@/lib/types'; import { CARD_GROUPS, CARD_QUESTIONS, CATEGORY_LABELS, CATEGORY_ORDER, aggregateCells, canRenderBar, toBarSegments, cardGroupFor, type CardGroup, type DestinationCell, type DestinationGroup, type PupilGroup, } from '@/lib/destinations'; import styles from './destinations.module.css'; const GROUP_LABELS: Record = { all: 'All pupils', disadvantaged: 'Disadvantaged', other: 'All other pupils', }; const GROUP_ORDER: PupilGroup[] = ['all', 'disadvantaged', 'other']; function toGroup(payload: DestinationGroupPayload): DestinationGroup { return { cohort: payload.cohort ?? 0, cells: payload.categories, aggregates: payload.aggregates, }; } function cellsFor(group: DestinationGroup, card: CardGroup): DestinationCell[] { const wanted = new Set(CARD_GROUPS[card]); return group.cells.filter(c => wanted.has(c.category)); } export function DestinationsView({ destinations, phase, }: { destinations: DestinationPhase; phase: 'ks4' | 'ks5' }) { const available = GROUP_ORDER.filter( g => (destinations.groups[g]?.categories?.length ?? 0) > 0, ); const [selected, setSelected] = useState(available[0] ?? 'all'); const [focused, setFocused] = useState(null); const payload = destinations.groups[selected]; if (!payload) return null; const group = toGroup(payload); const barDrawable = canRenderBar(group); const segments = barDrawable ? toBarSegments(group) : []; const withheld = group.cells.filter(c => c.status === 'suppressed'); const dimmed = (card: CardGroup | null) => focused !== null && focused !== card; return (
{available.length > 1 && (
Show
{available.map(g => ( ))}
)}
{(Object.keys(CARD_GROUPS) as CardGroup[]).map(card => { const cells = cellsFor(group, card); if (cells.length === 0) return null; const total = aggregateCells(cells, group.cohort); const { question, hint } = CARD_QUESTIONS[card]; const keys = cells.map(c => ( )); if (total === null) { return (
{question} Not published Too few pupils went to {hint} for the Department for Education to release a figure. {keys}
); } return ( ); })}
{barDrawable ? (
{segments.map(seg => { const card = cardGroupFor(seg.category); return (
{seg.widthPct >= 9 ? `${seg.labelPct}%` : ''}
); })}
0%25%50%75%100%
) : (
No breakdown chart for this group

{withheld.length === 1 ? 'One of the destinations is withheld' : `${withheld.length} of the destinations are withheld`} {' '}because too few pupils went there. These destinations add up to the whole year group, so drawing the rest as a chart would give the withheld figures away. The table below shows what was published, and nothing more.

)}
{CATEGORY_ORDER.flatMap(category => { const cell = group.cells.find(c => c.category === category); if (!cell) return []; const card = cardGroupFor(category); const isWithheld = cell.status === 'suppressed'; return [( )]; })}
Destination Pupils Share
{CATEGORY_LABELS[category]} {isWithheld ? withheld : cell.pupils} {isWithheld || cell.percentage === null ? withheld : `${Math.round(cell.percentage)}%`}

Shares are rounded and may not add up to 100%. A pupil counted under a school sixth form may have moved to a different school's sixth form — the published data records the type of place, not which one.

); }