import { raceColor } from '../utils/colors.js' import { POOL_BY_SEX_ARREST_THRESHOLD } from '../utils/pooling.js' /** * The results page's first block: what was actually observed, before any * modelling. One row per student group plus a district total. * * The table doubles as the legend and the control for the density panel — each * row carries the checkbox that adds or removes that group's curve, and the * colour swatch is the same hue the curve is drawn in. That's deliberate: * a separate legend plus a separate group picker would make the reader hold * three mappings in their head instead of one. * * @param {{ * groups: Array<{key:string, race:string, sex:string|null, label:string, * enroll:number, observed:number, rate:number}>, * selectedKeys: string[], * onToggleGroup: (key: string) => void, * pooled: boolean, * autoPooled: boolean, * onPooledChange: (pooled: boolean) => void, * selectionIsEnrollmentFallback: boolean, * }} props */ export default function DistrictSummaryTable({ groups, selectedKeys, onToggleGroup, pooled, autoPooled, onPooledChange, selectionIsEnrollmentFallback, }) { if (!groups?.length) { return (

Reported arrests and enrollment

No student-group data is available for this district in 2021–22.

) } const selected = new Set(selectedKeys) const totalEnroll = groups.reduce((sum, g) => sum + g.enroll, 0) const totalObserved = groups.reduce((sum, g) => sum + g.observed, 0) const totalRate = totalEnroll > 0 ? (totalObserved / totalEnroll) * 1000 : 0 return (

Reported arrests and enrollment, 2021–22

Check a group to show its modelled arrest-rate distribution in the chart below.

{pooled && ( )} {selectionIsEnrollmentFallback && (

No group in this district reported an arrest in 2021–22, so the two largest groups by enrollment are shown by default.

)}
{groups.map((g) => ( onToggleGroup(g.key)} /> ))}
Students counted are those in the four modelled race groups (American Indian / Alaska Native, Black, Hispanic, White) — not the district’s total enrollment, which also includes groups this model does not estimate.
Student group Students Observed arrests Rate per 1,000
All modelled groups {totalEnroll.toLocaleString()} {totalObserved.toLocaleString()} {formatRate(totalRate)}
{!pooled && ( )}
) } function GroupRow({ group, checked, onToggle }) { const zero = group.observed === 0 const inputId = `group-toggle-${group.key}` return ( {group.enroll.toLocaleString()} {group.observed.toLocaleString()} {formatRate(group.rate)} ) } function PoolingBanner({ autoPooled, totalObserved, onPooledChange }) { return (

{autoPooled ? ( <> This district reported {totalObserved.toLocaleString()} arrests in total — fewer than{' '} {POOL_BY_SEX_ARREST_THRESHOLD} — so Female and Male students are combined within each race to give each estimate more data to stand on. ) : ( <>Female and Male students are combined within each race. )}

) } function formatRate(rate) { if (!(rate > 0)) return '0.0' return rate.toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 }) } const cardTitle = { fontSize: '0.85rem', marginBottom: 'var(--space-1)', color: 'var(--cv-ink-2)' } const tableStyle = { width: '100%', borderCollapse: 'collapse', fontSize: '0.85rem', fontVariantNumeric: 'tabular-nums', } const thStyle = { textAlign: 'right', padding: '0.35rem 0.5rem', borderBottom: '1px solid var(--cv-rule-strong)', fontSize: '0.72rem', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.06em', color: 'var(--cv-ink-3)', whiteSpace: 'nowrap', } const tdStyle = { padding: '0.35rem 0.5rem', borderBottom: '1px solid var(--cv-rule)', } const numStyle = { ...tdStyle, textAlign: 'right', whiteSpace: 'nowrap' } const bannerStyle = { background: 'var(--cv-paper-2)', border: '1px solid var(--cv-rule)', borderLeft: '3px solid var(--cv-accent)', borderRadius: 'var(--radius-md)', padding: 'var(--space-1) var(--space-2)', marginBottom: 'var(--space-2)', display: 'flex', flexDirection: 'column', gap: '0.4rem', } const toggleLabel = { display: 'inline-flex', alignItems: 'center', gap: '0.45rem', fontSize: '0.78rem', color: 'var(--cv-ink-2)', cursor: 'pointer', } const noteStyle = { fontSize: '0.78rem', fontStyle: 'italic', color: 'var(--cv-ink-3)', margin: '0 0 var(--space-2)', }