Files
crdc-demo/src/components/StateSelector.jsx
T
jared 52a0c77e31
Deploy to Git Pages / build-and-deploy (push) Failing after 19s
Initial commit: CRDC Arrests API demo app
React + Vite static site demonstrating the CRDC School Arrest Rate API.
Features 6 interactive charts comparing observed arrest data against Bayesian
model estimates across U.S. school districts, with Civilytics visual identity.

- State selector and district search with 'interesting' suggestions (top arrests)
- Animated histogram loading grid showing posterior draw progress
- Charts: time series, rate by group, district vs national, model comparison quadrants, density proxy, exceedance probability
- Static JSON fixture for national rates (no API changes needed)
- Dockerfile + docker-compose.yml for self-hosted deployment
- GitHub Actions workflow and _config.yml for Git Pages
2026-08-10 10:54:51 -04:00

138 lines
5.7 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useState } from 'react'
// All 50 states + DC, matching the API's ALLOWED_STATES
const STATES = [
'AL','AK','AZ','AR','CA','CO','CT','DE','DC','FL','GA','HI',
'ID','IL','IN','IA','KS','KY','LA','ME','MD','MA','MI','MN','MS','MO','MT',
'NE','NV','NH','NJ','NM','NY','NC','ND','OH','OK','OR','PA','RI','SC','SD',
'TN','TX','UT','VT','VA','WA','WV','WI','WY'
]
const STATE_NAMES = {
AL: 'Alabama', AK: 'Alaska', AZ: 'Arizona', AR: 'Arkansas', CA: 'California',
CO: 'Colorado', CT: 'Connecticut', DE: 'Delaware', DC: 'District of Columbia', FL: 'Florida',
GA: 'Georgia', HI: 'Hawaii', ID: 'Idaho', IL: 'Illinois', IN: 'Indiana', IA: 'Iowa',
KS: 'Kansas', KY: 'Kentucky', LA: 'Louisiana', ME: 'Maine', MD: 'Maryland', MA: 'Massachusetts',
MI: 'Michigan', MN: 'Minnesota', MS: 'Mississippi', MO: 'Missouri', MT: 'Montana',
NE: 'Nebraska', NV: 'Nevada', NH: 'New Hampshire', NJ: 'New Jersey', NM: 'New Mexico',
NY: 'New York', NC: 'North Carolina', ND: 'North Dakota', OH: 'Ohio', OK: 'Oklahoma',
OR: 'Oregon', PA: 'Pennsylvania', RI: 'Rhode Island', SC: 'South Carolina', SD: 'South Dakota',
TN: 'Tennessee', TX: 'Texas', UT: 'Utah', VT: 'Vermont', VA: 'Virginia',
WA: 'Washington', WV: 'West Virginia', WI: 'Wisconsin', WY: 'Wyoming'
}
/**
* Landing screen — prompts user to select a state.
* Matches the Civilytics visual style with eyebrow label, stat callout, and clean card layout.
*/
export default function StateSelector({ onNext }) {
const [query, setQuery] = useState('')
// Filter states by code or name as user types
const filtered = STATES.filter(
(code) =>
code.toLowerCase().includes(query.toLowerCase()) ||
STATE_NAMES[code].toLowerCase().includes(query.toLowerCase())
)
return (
<div style={{ padding: 'var(--space-5) 0 var(--space-7)' }}>
{/* Eyebrow */}
<span className="eyebrow">US Department of Education Civil Rights Data Collection</span>
{/* Hero */}
<h1 style={{ marginTop: 'var(--space-2)', marginBottom: 'var(--space-3)' }}>
Explore school-based arrest rates by district
</h1>
<p style={{ fontSize: '1.1rem', lineHeight: 1.7, maxWidth: '48rem' }}>
The Civil Rights Data Collection (CRDC) is the only source of data on school-related
arrests for all U.S. public schools and districts. Enter a state to see estimates for
any district — with Bayesian model comparisons that reveal how confident we can be in
each rate, even when arrests are rare events.
</p>
{/* Stat callout — key context */}
<div className="stat-callout" style={{ marginTop: 'var(--space-4)' }}>
<div>
<span className="stat-num">34,846</span>
<span className="stat-label">students arrested in 2021–22</span>
</div>
<div>
<span className="stat-num">0.72</span>
<span className="stat-label accent">arrests per 1,000 students (national rate)</span>
</div>
<div>
<span className="stat-num">11.6%</span>
<span className="stat-label">of districts reported &gt;0 arrests</span>
</div>
</div>
{/* Search input */}
<div style={{ marginTop: 'var(--space-5)' }}>
<label htmlFor="state-search" style={{ display: 'block', marginBottom: 'var(--space-2)', fontWeight: 600 }}>
Select a state to begin:
</label>
<input
id="state-search"
type="text"
placeholder="Type a state name or code (e.g., Texas, TX)…"
value={query}
onChange={(e) => setQuery(e.target.value)}
style={{
width: '100%', padding: 'var(--space-2)', fontSize: '1rem',
border: '1px solid var(--cv-rule)', borderRadius: 'var(--radius-md)',
fontFamily: 'var(--font-sans)'
}}
autoFocus
/>
{/* Filtered grid of states */}
{filtered.length > 0 && (
<div style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fill, minmax(120px, 1fr))',
gap: 'var(--space-1)',
marginTop: 'var(--space-3)'
}}>
{filtered.map((code) => (
<button
key={code}
className="cv-card"
style={{ textAlign: 'center', padding: 'var(--space-2)' }}
onClick={() => onNext(code)}
>
<span style={{ fontSize: '1.25rem', fontWeight: 700, color: 'var(--cv-navy-600)' }}>
{code}
</span>
<span className="stat-label" style={{ marginTop: '0.25rem' }}>
{STATE_NAMES[code]}
</span>
</button>
))}
</div>
)}
{filtered.length === 0 && (
<p style={{ color: 'var(--cv-ink-3)', marginTop: 'var(--space-2)' }}>
No states match "{query}". Try a full state name or two-letter code.
</p>
)}
</div>
{/* Context footer */}
<div style={{ marginTop: 'var(--space-5)', paddingBottom: 'var(--space-3)', borderBottom: '1px solid var(--cv-rule)' }}>
<span className="eyebrow">How it works</span>
<p style={{ fontSize: '0.9rem', color: 'var(--cv-ink-2)', marginTop: 'var(--space-1)' }}>
After selecting a state, you'll search for a school district and see arrest estimates from 10 Bayesian models —
including one-year vs. three-year specifications and baseline vs. referral-rate covariate models.
The app fetches data directly from the{' '}
<a href="https://crdc-api.civilytics.org/api/v1/" target="_blank" rel="noopener noreferrer">
CRDC Arrest Rate API
</a>. No account or key required.
</p>
</div>
</div>
)
}