import { useState, useEffect } from 'react' import StateSelector from './components/StateSelector.jsx' import DistrictSearch from './components/DistrictSearch.jsx' import LoadingAnimation from './components/LoadingAnimation.jsx' import ChartPanel from './components/ChartPanel.jsx' import Footer from './components/Footer.jsx' import { fetchDistrictEstimates } from './hooks/useApi.js' // Two-letter USPS state code. The deep-link value flows into API request URLs // and into the Hugging Face parquet shard path duckdb-wasm reads, so it gets // validated here at the boundary rather than propagated verbatim. const STATE_CODE_PATTERN = /^[A-Z]{2}$/ /** * CRDC Arrests API Demo App — main router. * Flow: state → district search (with interesting suggestions) → loading animation → charts */ export default function App() { const [step, setStep] = useState('state') // 'state' | 'search' | 'loading' | 'results' const [selectedState, setSelectedState] = useState(null) const [district, setDistrict] = useState(null) // On first load: check URL for ?leaid= and state to allow deep-linking useEffect(() => { const params = new URLSearchParams(window.location.search) const leaid = params.get('leaid') const rawState = params.get('state') const stateParam = rawState ? rawState.trim().toUpperCase() : null if (rawState && !STATE_CODE_PATTERN.test(stateParam)) { // Malformed deep link — drop it and start at the state selector rather // than passing an arbitrary string into fetch URLs and shard paths. console.warn('Ignoring deep link: `state` is not a two-letter state code.') return } if (leaid && stateParam) { // Deep link (including browser back/forward landing on this URL): resolve // the district name via a real estimates row, keyed by LEAID. The previous // version called searchDistricts(leaid, ...), a name/text search — passing // an LEAID as search text never matches, so it always fell back to "Unknown // District". setSelectedState(stateParam) fetchDistrictEstimates(leaid, { model: 'unified_m2_mod', year: '21-22' }).then(rows => { const row = rows?.[0] setDistrict(row ? { leaid: row.leaid, lea_name: row.lea_name, state: row.state } : { leaid, lea_name: 'Unknown District', state: stateParam }) setStep('loading') }).catch(() => { // Fallback on error setDistrict({ leaid, lea_name: 'Unknown District', state: stateParam }) setStep('loading') }) } }, []) const handleStateChange = (stateCode) => { setSelectedState(stateCode) setStep('search') } const handleDistrictSelect = (dist) => { // Update URL for shareability const params = new URLSearchParams({ leaid: dist.leaid, state: selectedState }) window.history.replaceState(null, '', `?${params}`) setDistrict(dist) setStep('loading') } return (