Bera
initial deploy
14356bb
import { createContext, useContext, useEffect, useState, useCallback } from 'react'
const DataContext = createContext(null)
const KEYS = [
'models', 'recs', 'ms', 'vol_salience', 'val_share',
'freq_anchors', 'ppa_mt', 'ppa_tt', 'comp_ms',
'vtm', 'interaction', 'growth_decomp', 'pgi', 'stats',
'trend', 'recs_full',
]
async function fetchKey(key) {
// 1. Try FastAPI backend (/api/data/{key}) — live pipeline-computed data
try {
const res = await fetch(`/api/data/${key}`)
if (res.ok) return res.json()
} catch (_) {
// upload server not reachable — fall through to static fallback
}
// 2. Fallback: static pre-generated file served by Vite from public/data/
// Works when upload server is not running (e.g. first open, view-only mode)
const res = await fetch(`/data/${key}.json`)
if (!res.ok) throw new Error(`Failed to load ${key}: ${res.status}`)
return res.json()
}
export function DataProvider({ children }) {
const [data, setData] = useState({})
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
const [progress, setProgress] = useState(0)
const loadAll = useCallback(async () => {
setLoading(true)
setProgress(0)
setError(null)
let cancelled = false
const results = {}
const errors = []
for (let i = 0; i < KEYS.length; i++) {
const key = KEYS[i]
try {
results[key] = await fetchKey(key)
} catch (e) {
results[key] = null
errors.push(`${key}: ${e.message}`)
}
if (!cancelled) setProgress(Math.round(((i + 1) / KEYS.length) * 100))
}
if (!cancelled) {
setData(results)
setLoading(false)
if (errors.length === KEYS.length) {
setError('All data failed to load. Is the server running?\n' + errors[0])
}
}
return () => { cancelled = true }
}, [])
useEffect(() => {
const cleanup = loadAll()
return () => { cleanup.then?.(fn => fn?.()) }
}, [loadAll])
return (
<DataContext.Provider value={{ ...data, loading, error, progress, reload: loadAll }}>
{children}
</DataContext.Provider>
)
}
export function useData() {
return useContext(DataContext)
}