# Finance frontend React + TypeScript SPA for the finance tracker: RU/EN, WCAG-aware, data-dense desktop tables that reflow into cards on mobile. ## Stack - Vite + React 19 + TypeScript (strict) - React Router (library mode) - TanStack Query v5 (space-scoped query keys, v5 `refetchInterval` callback polling) - React Hook Form + Zod - i18next (RU default, EN fallback) - Vitest + Testing Library (jsdom) - ESLint 9 (typescript-eslint, react-hooks, jsx-a11y) ## Commands ```bash npm install # install npm run dev # dev server on :5173, proxies /api -> http://localhost:8000 npm run build # tsc --noEmit && vite build npm run typecheck # tsc --noEmit npm run lint # eslint . npm test # vitest run npm test:watch # vitest npm run api:generate ``` `npm run api:generate` looks for an OpenAPI schema at `../backend/openapi.json` (or fetches `/api/v1/openapi.json` from a running backend) and emits `src/api/generated/schema.d.ts` via `openapi-typescript`. Until the backend schema exists the script exits with an explanation and the hand-written client remains the source of truth. ## API client `src/api/client.ts` is a small typed wrapper over `fetch` bound to same-origin `/api/v1` with `credentials: 'same-origin'`, a CSRF header read from the `csrf_token` cookie on mutating requests, and a single `ApiError` type carrying `status` + machine-readable `code` (translated via the `errors.*` i18n keys). `src/api/endpoints.ts` declares one function per endpoint with payload/return types from `src/api/types.ts`, so each group can be swapped one-for-one by OpenAPI-generated functions without touching UI code. Assumed endpoints (matching the approved plan): `/auth/login`, `/auth/logout`, `/auth/me`, `/auth/invitations/accept`, `/accounts`, `/operations`, `/budget/summary`, `/budget/categories` (+ close/reopen/copy-previous and per-category plan/fund actions), `/imports`, `/imports/{id}` (+ confirm / reparse / revert / row decisions), `/analytics/{category-spend|plan-fact|cash-flow|balances}`, `/exports`, `/spaces/current/deletion`. Amounts are integer kopecks everywhere. ## Query keys All space-data keys are rooted at `['space', ...]` (`src/api/queryKeys.ts`); mutations invalidate by prefix (e.g. an applied import invalidates the whole space root). Import list/detail queries poll with the v5 `refetchInterval` callback and stop automatically in terminal states (`ready`, `review_required`, `confirmed`, `failed`). ## Accessibility & responsiveness - Every chart figure ships a real data table (`
` toggle) with captions and scoped headers; decorative graphics are `aria-hidden`. - Dense `` markup reflows into cards under 880px via the `data-label` pattern — no horizontal scrolling for data. - Skip link, focus-visible rings, `aria-live` toasts, `role="alert"` errors, `prefers-reduced-motion` support, AA-contrast palette. No secrets are read from `VITE_*` — the app only ever talks to same-origin `/api/v1`.