Initial commit: CRDC Arrests API demo app
Deploy to Git Pages / build-and-deploy (push) Failing after 19s
Deploy to Git Pages / build-and-deploy (push) Failing after 19s
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
This commit is contained in:
+94
@@ -0,0 +1,94 @@
|
||||
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'
|
||||
|
||||
/**
|
||||
* CRDC Arrests API Demo App — main router.
|
||||
* Flow: state → district search (with interesting suggestions) → loading animation → 6 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 stateParam = params.get('state')
|
||||
if (leaid && stateParam) {
|
||||
// Deep link: skip straight to loading → results
|
||||
setSelectedState(stateParam)
|
||||
setDistrict({ leaid, lea_name: 'Loading…', 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 (
|
||||
<div className="cv-app">
|
||||
{/* Header */}
|
||||
<header className="cv-header cv-wrap">
|
||||
<a href="/" aria-label="Civilytics" style={{ display: 'block' }}>
|
||||
<img
|
||||
src="/civilytics-logo.svg"
|
||||
alt="Civilytics — social science for the public good"
|
||||
className="cv-logo"
|
||||
onError={(e) => { e.target.style.display = 'none' }}
|
||||
/>
|
||||
</a>
|
||||
</header>
|
||||
|
||||
{/* Main content */}
|
||||
<main style={{ maxWidth: '60rem', margin: '0 auto', padding: '0 var(--space-3)' }}>
|
||||
{step === 'state' && (
|
||||
<StateSelector onNext={handleStateChange} />
|
||||
)}
|
||||
|
||||
{step === 'search' && selectedState && (
|
||||
<DistrictSearch
|
||||
state={selectedState}
|
||||
onSelect={handleDistrictSelect}
|
||||
onBack={() => setStep('state')}
|
||||
/>
|
||||
)}
|
||||
|
||||
{step === 'loading' && district && (
|
||||
<LoadingAnimation district={district} state={selectedState} />
|
||||
)}
|
||||
|
||||
{/* ChartPanel listens for data-ready event from LoadingAnimation */}
|
||||
{step === 'results' && district && (
|
||||
<ChartPanel district={district} state={selectedState} />
|
||||
)}
|
||||
|
||||
{(step === 'loading' || step === 'results') && (
|
||||
<button
|
||||
className="btn-outline"
|
||||
style={{ margin: 'var(--space-3) 0', display: 'block' }}
|
||||
onClick={() => { window.location.href = '/' }}
|
||||
>
|
||||
← Search another district
|
||||
</button>
|
||||
)}
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user