- Implemented tests for import polling functionality, ensuring correct behavior for active and terminal import statuses. - Created tests for the login page, validating user interactions and error handling. - Added unit tests for money utility functions, covering parsing, formatting, and summation of monetary values. - Developed tests for query keys, ensuring proper scoping and invalidation roots. chore: setup testing environment and configuration - Added setup file for testing with Vitest and Testing Library. - Configured TypeScript and Vite for the frontend project. - Created Docker and Nginx configurations for local development and production environments. - Established a PostgreSQL initialization script for setting up application roles. docs: add README and environment configuration for infra - Included README for the infra directory, detailing local development setup and service descriptions. - Created an example environment configuration file for local development with placeholders for sensitive information.
2.9 KiB
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
refetchIntervalcallback polling) - React Hook Form + Zod
- i18next (RU default, EN fallback)
- Vitest + Testing Library (jsdom)
- ESLint 9 (typescript-eslint, react-hooks, jsx-a11y)
Commands
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 (
<details>toggle) with captions and scoped headers; decorative graphics arearia-hidden. - Dense
<table>markup reflows into cards under 880px via thedata-labelpattern — no horizontal scrolling for data. - Skip link, focus-visible rings,
aria-livetoasts,role="alert"errors,prefers-reduced-motionsupport, AA-contrast palette.
No secrets are read from VITE_* — the app only ever talks to same-origin
/api/v1.