Commit Graph
31 Commits
Author SHA1 Message Date
jared 2cb04ad9c1 docs: add design spec for empirical draw distributions via duckdb-wasm
Scopes replacing the analytic distribution approximation with real
posterior draws, fetched client-side from the Hugging Face parquet
dataset using duckdb-wasm — no new backend endpoint required.
2026-08-11 07:41:21 -04:00
jaredandClaude Sonnet 5 466ebdb9a6 Align modeled intervals with their observed points, use diamonds, name the model
Deploy to git-pages / deploy (push) Successful in 11s
The modeled point-range was dodged 18px to the side of its observed point,
which read as misaligned rather than paired. Removes the dodge so both sit
on the same x column - directly comparable at a glance, matching the
reference whitepaper figures' own convention.

Observed points were plain circles, inconsistent with every other chart in
the app where a diamond means "observed." Switches them to diamonds, drawn
last so they stay on top of the modeled marks sharing their column.

Adds a caption naming the model the modeled interval is drawn from (three-
year, no covariate / unified_m3_mod), sourced from ChartPanel's existing
WAVE_MODEL constant via the shared MODEL_QUADRANT_LABEL lookup rather than
hardcoded, so it can't drift if the model choice changes.

Also gives the rate-per-1k labels a paper-colored text halo (paintOrder:
stroke) so they stay legible now that they can sit directly over the
modeled whisker line instead of needing to dodge around it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 19:44:28 -04:00
jaredandClaude Sonnet 5 fa35d97269 Clean up the hero chart: remove watermark, fix label collisions, round axis ticks
Deploy to git-pages / deploy (push) Successful in 12s
The district-name watermark inside the arrests-over-time chart was
redundant with the page's own heading and was the direct cause of a label
collision (a high point's rate label rendered on top of it). Removes it.

Two more collisions, both edge cases the screenshot happened to hit: the
first wave's label sat close enough to the plot's top that it could
overlap the topmost gridline text, and a point sitting flush on the plot's
left edge had its center-anchored label bleed into the y-axis tick labels.
Fixes both with more vertical clearance and edge-aware text anchoring
(first point anchors right, last point anchors left, matching the same
fix already applied to chart 4's wave ticks before that chart was cut).

Adds a shared niceTicks() utility (Heckbert's nice-numbers algorithm) so
axis ticks read as round numbers (0/100/200) instead of arbitrary
fractions of the data max (0/113/226/339) — applied to all three charts
for consistency. Also fixes a legend/mark mismatch in the arrests-over-time
chart (the modeled series legend showed a diamond; the actual mark is a
dot) by adding a proper 'dot' shape to ChartLegend.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 19:37:02 -04:00
jaredandClaude Sonnet 5 a1ffa600b3 Reduce demo to 3 charts, fix back-button district name bug
Deploy to git-pages / deploy (push) Successful in 26s
Fixes the deep-link/back-button bug where the district name showed
"Unknown District" on return: App.jsx was passing the LEAID as a text
search query to searchDistricts() (a name search), which never matches a
numeric ID. Resolves it instead via fetchDistrictEstimates(leaid, ...) and
reads lea_name/state directly from the returned row - correct regardless
of how the page was reached (fresh load, refresh, or browser back/forward).

Cuts the demo from 6 charts to 3, per request: arrests over time (kept),
arrest rate by student group restructured into Female/Male box-and-whisker
panels (kept), and the posterior density ridge chart restructured from a
2x2 model-quadrant grid into a single selected model (dropdown, default
three-year + referral rate) with Female/Male ridge columns. Removes
DistrictVsNational, ModelDrawsComparison, and ExceedanceProbability
entirely, along with the student-group filter (no longer needed - the
remaining charts always show the full breakdown) and the national-rates
fetch/plumbing that only those removed charts used.

Also updates LoadingAnimation's copy and dedupes its STUDENT_GROUPS/
MODEL_QUADRANTS constants against the shared ones in useApi.js.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 19:27:24 -04:00
jaredandClaude Sonnet 5 cf8e65bb8e Fix chart bugs found on deployed site, redesign Chart 2 as box-and-whisker
Deploy to git-pages / deploy (push) Successful in 12s
Chart 4 (model predictions vs. observed) was picking the first row for a
given wave/model instead of summing across selected groups, so any district
whose first-returned group had zero counts (e.g. a suppressed race group)
showed a collapsed, flat modeled marker instead of the real aggregate
interval. Also fixes the rightmost wave label ("2021-22") clipping off the
edge of the small-multiple panels by anchoring edge ticks away from the
viewBox boundary instead of centering on it.

Chart 6's y-axis group labels (e.g. "American Indian / Alaska Native
Female") were wider than their margin and clipped past the left edge of the
SVG. Adds a shared shortGroupLabel() to colors.js and uses it here, matching
the convention already used in charts 2 and 5.

Chart 2 previously drew a plain bar to the modeled median next to an
observed diamond that often sat well past the bar's end, reading as if the
bar itself were an uncertainty range when it wasn't. Replaces it with an
actual horizontal box-and-whisker: whisker = the API's reported 90%
interval, box = the fitted approximation's 25th/75th percentiles (via a new
fit.quantile() inverse-CDF, exact round-trip of fitSkewedInterval's own
construction), median tick, observed diamond overlaid.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 18:58:35 -04:00
jaredandClaude Sonnet 5 67caa02fe2 Redesign chart visualizations to match Civilytics white-paper style
Deploy to git-pages / deploy (push) Successful in 18s
Rebuilds all 6 charts around a validated categorical race palette, row-based
sex encoding, and a consistent observed-vs-modeled mark convention (diamond
vs. filled bar/density) instead of ad hoc per-chart color schemes. Adds a
shared student-group filter (defaults to all 8 groups) that scopes every
chart's data from one place in ChartPanel.

Drops the D3 dependency entirely in favor of plain SVG, removing the
imperative-DOM bug class behind this app's repeated "fix the fix" commits.
Replaces the fake symmetric-normal posterior approximation with a skewed,
median-preserving fit to the API's interval bounds, clearly labeled as an
approximation. Fixes two broken SVG fill attributes, a decorative model
dropdown that never affected its chart, dead code, an orphaned component,
and a broken CSS token reference.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 18:45:37 -04:00
jared 6c8e1e8409 docs: refresh AGENTS.md, HANDOFF.md, and README with recent work
- Create AGENTS.md: comprehensive agent guide covering architecture,
  data flow, error bar convention (90% intervals), D3 usage patterns,
  null safety pitfalls, deployment checklist, and related repos
- Update HANDOFF.md: mark CORS as resolved, document chart improvements
  (Chart 5 ridgeline rewrite with synthetic draws + smooth rendering)
- Refresh README.md: accurate tech stack (D3 v7), updated file structure
  with new chart files, corrected API endpoint table
2026-08-10 17:10:35 -04:00
jared 3fe004356e Fix ridgeline chart to render smooth density curves instead of individual columns
Deploy to git-pages / deploy (push) Successful in 13s
- Rewrite RateDensityRidgeline.jsx with proper histogram-to-density conversion
- Build top-edge points from binned draws, then smooth with d3.curveBasis
- Construct complete area path: bottom edge + smoothed top + close
- Position observed rate diamonds above the ridge peak
2026-08-10 17:05:38 -04:00
jared fc0b7e9b6d Fix: remove broken .replace() call causing TypeError in RateDensityRidgeline
Deploy to git-pages / deploy (push) Successful in 12s
2026-08-10 16:56:29 -04:00
jared c32aa46f7c Fix: null check before accessing modelDraws[q.key]
Deploy to git-pages / deploy (push) Successful in 12s
2026-08-10 16:51:53 -04:00
jared c64682228d Fix: null checks for quadData in ChartPanel and child components
Deploy to git-pages / deploy (push) Successful in 14s
2026-08-10 16:43:30 -04:00
jared 83417368f2 Fix: handle null modelDraws gracefully
Deploy to git-pages / deploy (push) Successful in 13s
2026-08-10 16:37:40 -04:00
jared be647d0882 Fix: remove draws fetch (API endpoint not available for browser)
Deploy to git-pages / deploy (push) Successful in 12s
The /estimates/{leaid}/draws endpoint doesn't exist - only bulk Parquet
shard access is available. Falls back to interval-based rendering.
2026-08-10 16:32:40 -04:00
jared 54814564a9 Hook up raw posterior draws from API to ModelDrawsComparison
Deploy to git-pages / deploy (push) Successful in 11s
- Add fetchDistrictDraws call in ChartPanel for all 4 quadrant models
- Pass modelDraws to ModelDrawsComparison component
- Rewrite Chart 4 with D3 showing:
  - Density histogram from raw draws when available (per-year distribution)
  - Fall back to interval-based rendering otherwise
  - 90% credible intervals from quantile calculation
  - Legend explaining colors (red=observed, teal=one-year, navy=three-year)
2026-08-10 16:27:47 -04:00
jared f65a70fc3e Add D3 and new ridgeline chart matching R aesthetic
Deploy to git-pages / deploy (push) Successful in 19s
- Install d3@7
- Add fetchDistrictDraws API function (for future use with raw draws)
- Create RateDensityRidgeline component using D3.js
  - Density ridges showing posterior distributions per race group
  - Diamond markers for observed rates (matching R design)
  - Dropdown to switch between model specifications
  - Matches Civilytics color palette (navy fill, danger diamonds)
2026-08-10 16:15:33 -04:00
jared c4c0aa63bd Use three-year model (unified_m3_mod) to fetch observed data for all waves
Deploy to git-pages / deploy (push) Successful in 11s
One-year models don't return historical arrest data; three-year models
include observed counts across all CRDC waves (15-16, 17-18, 21-22).
2026-08-10 13:19:39 -04:00
jared 0fa5844a16 Replace logo with Civilytics wordmark from civilyticsR package
Deploy to git-pages / deploy (push) Successful in 11s
2026-08-10 12:54:55 -04:00
jared b135f480c0 Fix chart sizing, model predictions, 90% intervals, and deep link district name
Deploy to git-pages / deploy (push) Successful in 17s
- Increase SVG dimensions in all 6 charts to prevent label truncation
- Fix ModelDrawsComparison: pass quadData so predictions render for three-year models
- Change error bars from 95% to 90% (legend text + SD calculation)
- Widen chart viewport: max-width 70rem vs default 60rem text width
- Fix deep link: fetch district name from API instead of showing 'Loading...'
2026-08-10 12:50:19 -04:00
jared eb396abce7 Restore first three charts (ArrestsOverTime, RateByGroupBar, DistrictVsNational) that were accidentally removed in previous edit
Deploy to git-pages / deploy (push) Successful in 11s
2026-08-10 12:27:04 -04:00
jared 77757348f8 Switch ChartPanel from 3-column grid to single vertical stack
Deploy to git-pages / deploy (push) Successful in 10s
Charts and their titles/captions need more horizontal space for readability. Single column layout ensures each chart renders at full container width without cramped side-by-side placement.
2026-08-10 12:25:31 -04:00
jared 26f72da6db Disable source map generation for cleaner production builds
Deploy to git-pages / deploy (push) Successful in 17s
Source maps cause harmless but noisy 404 errors in browser dev tools when deployed to git-pages, since the hashed filenames change between local and CI builds. Disabling them eliminates these warnings without affecting app functionality.
2026-08-10 12:23:27 -04:00
jared dd69bb6f8f Fix JS syntax error in ObservedRateDensity.jsx — const declarations were embedded inside JSX
Deploy to git-pages / deploy (push) Successful in 11s
The obsWidth, lowerX, upperWidth, and medX variables were declared inline within the SVG <g> element's children, which is invalid JavaScript/JSX. This caused 'ReferenceError: obsWidth is not defined' at runtime when rendering Chart 5.

Fix: Moved all const declarations to the top of the map callback, before the return statement.
2026-08-10 12:21:00 -04:00
jared 116730b685 Fix national_rates.json deployment — copy to public/ directory
Deploy to git-pages / deploy (push) Successful in 19s
- Add missing data/national_rates.json fixture (was only in src/data/, not served by Vite)
- LoadingAnimation.jsx: Use import.meta.env.BASE_URL for national rates path
- Ensures file is available at /crdc-demo/data/national_rates.json on git-pages
2026-08-10 12:17:42 -04:00
jared 2c900b003f Fix static asset paths for /crdc-demo subdirectory deployment
Deploy to git-pages / deploy (push) Successful in 10s
- App.jsx: Use import.meta.env.BASE_URL prefix for civilytics-logo.svg (was hardcoded as '/civilytics-logo.svg' → 404 on pages.civilytics.org/crdc-demo/)
- ChartPanel.jsx: Same fix for /data/national_rates.json path — was causing JSON.parse errors because the 404 HTML response couldn't be parsed
2026-08-10 11:44:26 -04:00
jared 8ba7440a34 Fix routing, data fetching, and rendering issues in CRDC demo app:
Deploy to git-pages / deploy (push) Successful in 10s
- App.jsx: Fix 'search another district' button href to use /crdc-demo/ instead of / (root)
- LoadingAnimation.jsx: Replace broken inline fetch helper with centralized api client from useApi.js — was bypassing envelope unwrapping ({status:'success',data:[...]}), causing all 43 API calls to fail silently
- ChartPanel.jsx: Same fix — replace raw fetch() calls with api.fetchDistrictEstimates(), fix national_rates.json path (remove hardcoded /crdc-demo/ prefix that breaks in dev), parallelize wave/model fetches with Promise.all for faster loading

Root causes fixed:
1. Navigation button linked to site root instead of app subdirectory → users lost their way after selecting a district
2. Dual API client implementations — inline fetch helpers didn't unwrap the JSON envelope structure, causing data parsing failures that made the page appear stuck on 'Organizing data...'
3. Sequential fetches in ChartPanel caused slow loading; parallelized for better UX
2026-08-10 11:42:31 -04:00
jared c475bfe50b Add handoff notes for CORS deployment fix 2026-08-10 11:27:03 -04:00
jared 4ce64c7839 Fix CORS: add proxy support for browser-based API calls on git-pages
Deploy to git-pages / deploy (push) Successful in 20s
- Add VITE_PROXY_URL env var to route requests through a CORS proxy when
  deployed as static site (CRDC API doesn't send Access-Control-Allow-Origin)
- Update useApi.js, LoadingAnimation.jsx, and ChartPanel.jsx to respect proxy
- Add nginx.conf with /api/v1/ reverse proxy + CORS headers for Docker deployment
- Include proxy.php — simple PHP CORS proxy for git-pages hosts that support PHP
- Document CORS workaround in README

Without this fix, browser fetch() calls are blocked by same-origin policy and
the app shows a blank white screen despite serving correct HTML.
2026-08-10 11:22:44 -04:00
jared 194208d817 Fix base URL for subdirectory deployment on pages.civilytics.org
Deploy to git-pages / deploy (push) Successful in 10s
- Set vite.config.mjs base to '/crdc-demo/' so asset paths resolve correctly
  when served from a subdirectory (not root)
- Add favicon.svg in public/ folder
- Update index.html with correct absolute paths including /crdc-demo/ prefix
2026-08-10 11:13:16 -04:00
jared ff4b2ac690 Update README: document Gitea Actions pages workflow
Replaces the GitHub Actions reference with .gitea/workflows/pages.yml,
following the sln-school-comparison deployment pattern. Documents the
git-pages.civilytics.org/crdc-demo/ URL and token configuration.
2026-08-10 11:08:00 -04:00
jared 7cf9777673 Add Gitea Actions workflow for git-pages deployment
Deploy to git-pages / deploy (push) Successful in 10s
Adapts the sln-school-comparison pattern: builds Vite static site in CI,
then copies dist/ to a pages branch served by the git-pages webhook at
pages.civilytics.org/crdc-demo/
2026-08-10 10:57:00 -04:00
jared 52a0c77e31 Initial commit: CRDC Arrests API demo app
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
2026-08-10 10:54:51 -04:00