# 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 ```bash 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 --noEmit ``` Start 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 ```bash 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//page.tsx one client page per route ``` Routes: `/` 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.