frontend: scaffold Vite + React 19 + TS + Tailwind v4 + TanStack Query
Some checks failed
ci/woodpecker/push/woodpecker Pipeline was canceled

Bare-minimum SPA that wires up to the FastAPI backend:

- Vite 6 + React 19 + TS strict, alias @/* to src/*
- Tailwind v4 via @tailwindcss/vite (no postcss)
- TanStack Query v5 with sane defaults (30s staleTime, no auto-refetch)
- React Router 7 for routing
- ECharts + Recharts available (charts land in Phase 1a)
- Vitest + @testing-library/react for tests
- Dev proxy /api → http://localhost:8080 (FastAPI)

Pages:
- Dashboard — pulls /networth, shows total + per-account cards.
  No chart yet (Phase 1a). Empty/error states for "no data" cases
  point users to the ingest CLI.

Header shows live API health (queue depth from /healthz). 274 KB JS
gzipped to 87 KB. typecheck + build pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Viktor Barzin 2026-05-09 21:53:59 +00:00
parent ee6ed1d3c4
commit f4539f9e6d
16 changed files with 6145 additions and 0 deletions

36
frontend/src/App.tsx Normal file
View file

@ -0,0 +1,36 @@
import { useQuery } from '@tanstack/react-query';
import { Route, Routes, Link } from 'react-router-dom';
import { api } from '@/api/client';
import { Dashboard } from '@/pages/Dashboard';
export function App() {
const health = useQuery({ queryKey: ['health'], queryFn: api.health });
return (
<div className="min-h-screen flex flex-col">
<header className="border-b border-slate-200 bg-white">
<div className="max-w-7xl mx-auto px-6 py-4 flex items-center justify-between">
<Link to="/" className="text-xl font-semibold tracking-tight">
fire-planner
</Link>
<div className="text-xs text-slate-500">
api:{' '}
<span className={health.data ? 'text-emerald-600' : 'text-amber-600'}>
{health.isLoading
? '…'
: health.data
? `ok (queue=${health.data.queue_depth})`
: 'unreachable'}
</span>
</div>
</div>
</header>
<main className="flex-1 max-w-7xl w-full mx-auto px-6 py-8">
<Routes>
<Route path="/" element={<Dashboard />} />
</Routes>
</main>
</div>
);
}