FinData OS — web console
Private operator console for the FinData OS lakehouse. Next.js 15 (App Router) + TypeScript + Tailwind CSS v4, built as a static export and served by the Python API — no Node process in production.
Develop
cd apps/web
pnpm install
pnpm dev # http://localhost:3000 — /api/* is proxied to http://127.0.0.1:8600 (next.config.ts rewrites, dev only)
pnpm typecheck # tsc --noEmitStart the API locally (uv run uvicorn findata_api.main:app --port 8600) before using the dev server; the console authenticates with the API's cookie session and sends X-CSRF-Token on every non-GET request.
Build & serve
pnpm build # → apps/web/out/ (index.html + one directory per route, trailing slashes)services/api/src/findata_api/main.py mounts apps/web/out (or FINDATA_WEB_DIST) with StaticFiles(html=True), so /datasets/ serves out/datasets/index.html, and the console is same-origin with /api/v1. The CSP is default-src 'self' (inline styles allowed, inline scripts not); the bundle contains no inline scripts, external fonts or CDNs.
Detail pages use query strings instead of dynamic segments (static export cannot pre-render unknown ids): /datasets/detail/?id=…, /symbols/detail/?symbol=….
Layout
src/lib/api.ts fetch wrapper (same-origin cookies, CSRF header, error normalisation) + API types
src/lib/auth.tsx /auth/me context, redirects to /login/ (401) or the MFA step
src/lib/hooks.ts useAsync, useDebounced, useQueryParam, useInterval, useLocalStorage
src/lib/format.ts bytes, thin-space numbers, YYYY-MM-DD HH:mm:ssZ, CSV
src/components/ Shell (sidebar ≥1024px, bottom tabs + drawer on mobile), DataTable (cards <640px), charts, dialogs, toast
src/app/<route>/page.tsx one client page per routeRoutes: / overview · /login/ · /datasets/ (+detail/) · /symbols/ (+detail/) · /coverage/ · /bars/ · /ingestion/ · /quality/ · /storage/ · /keys/ · /playground/ · /exports/ · /fundamentals/ · /docs/ · /audit/ · /settings/.
Owner-only pages (ingestion, storage, keys, audit, environment settings) are hidden from the navigation for other roles and show a notice if opened directly; the API enforces the admin scope regardless.
Design
Brand-neutral palette validated with the dataviz skill (categorical blue/orange/aqua, sequential blue ramp for the coverage heatmap, fixed status colours with icon + label), light and dark via prefers-color-scheme, system font stack. Charts: recharts (one axis per chart, 2 px lines, ≤24 px bars, hover tooltips) and plain SVG for the heatmap.