Fix routing, data fetching, and rendering issues in CRDC demo app:
Deploy to git-pages / deploy (push) Successful in 10s
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
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import ChartPanel from './ChartPanel.jsx'
|
||||
import * as api from '../hooks/useApi.js'
|
||||
|
||||
// The 8 student groups we fetch for (race×sex combinations)
|
||||
const STUDENT_GROUPS = [
|
||||
@@ -102,18 +103,18 @@ export default function LoadingAnimation({ district, state }) {
|
||||
return renderError(error, district)
|
||||
}
|
||||
|
||||
// Build the grid of "bars" — one per API call needed
|
||||
const allBars = [
|
||||
...WAVES.map((y, i) => ({ label: `Wave ${y}`, subLabel: 'Time series', col: 0, row: i })),
|
||||
...STUDENT_GROUPS.map((sg, i) => ({ label: sg.label, subLabel: 'Rate by group', col: 1, row: i })),
|
||||
...MODEL_QUADRANTS.flatMap((q, qi) =>
|
||||
STUDENT_GROUPS.map((sg, si) => ({
|
||||
label: `${q.model.split('_m')[0].substring(0,3).toUpperCase()} M${qi+1}`,
|
||||
subLabel: sg.label,
|
||||
col: 2 + qi % 2, row: si, quadrant: q
|
||||
}))
|
||||
),
|
||||
]
|
||||
// Build the grid of "bars" — one per API call needed.
|
||||
// Total = WAVES (3) + STUDENT_GROUPS (8) + MODEL_QUADRANTS×STUDENT_GROUPS (4×8=32)
|
||||
const TOTAL_BARS = WAVES.length + STUDENT_GROUPS.length + (MODEL_QUADRANTS.length * STUDENT_GROUPS.length)
|
||||
|
||||
// Build bar metadata for rendering
|
||||
const allBars = Array.from({ length: TOTAL_BARS }, (_, i) => {
|
||||
if (i < WAVES.length) return { subLabel: 'Time series', group: 'waves' }
|
||||
if (i < WAVES.length + STUDENT_GROUPS.length) return { subLabel: STUDENT_GROUPS[i - WAVES.length].label, group: 'groups' }
|
||||
const quadIdx = Math.floor((i - WAVES.length - STUDENT_GROUPS.length) / STUDENT_GROUPS.length)
|
||||
const sgIdx = (i - WAVES.length - STUDENT_GROUPS.length) % STUDENT_GROUPS.length
|
||||
return { subLabel: STUDENT_GROUPS[sgIdx].label, group: 'models', modelIdx: quadIdx }
|
||||
})
|
||||
|
||||
return (
|
||||
<div style={{ padding: 'var(--space-5) 0 var(--space-7)' }}>
|
||||
@@ -148,7 +149,7 @@ export default function LoadingAnimation({ district, state }) {
|
||||
))}
|
||||
|
||||
{/* Grid of animated bars — each represents one API call */}
|
||||
{allBars.slice(0, totalCalls || allBars.length).map((bar, i) => {
|
||||
{allBars.map((bar, i) => {
|
||||
const filled = i < loadedCount
|
||||
// Random height for "data viz" aesthetic (deterministic via seed = index)
|
||||
const heightSeed = ((i * 37) % 100) + 20 // 20–120px range
|
||||
@@ -201,21 +202,10 @@ export default function LoadingAnimation({ district, state }) {
|
||||
)
|
||||
}
|
||||
|
||||
// ——— Inline fetch helper with CORS proxy support ———
|
||||
const PROXY_URL = import.meta.env.VITE_PROXY_URL || ''
|
||||
|
||||
// ——— API helper using the centralized client from useApi.js ———
|
||||
// This ensures consistent error handling, envelope unwrapping, and CORS proxy support.
|
||||
async function fetchDistrictEstimatesBatch(leaid, year, race = null, sex = null, model = 'unified_m2_mod') {
|
||||
const params = new URLSearchParams({ leaid, year, model })
|
||||
if (race) params.set('race', race)
|
||||
if (sex) params.set('sex', sex)
|
||||
|
||||
// Route through proxy to bypass CORS restrictions when deployed on pages.civilytics.org
|
||||
const targetPath = `/api/v1/estimates/${leaid}?${params}`
|
||||
const url = PROXY_URL ? `${PROXY_URL}?target=${encodeURIComponent(targetPath)}` : `https://crdc-api.civilytics.org/api/v1/estimates/${leaid}?${params}`
|
||||
|
||||
const res = await fetch(url)
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`)
|
||||
return res.json()
|
||||
return api.fetchDistrictEstimates(leaid, { year, model, race, sex })
|
||||
}
|
||||
|
||||
function getStateName(code) {
|
||||
|
||||
Reference in New Issue
Block a user