# WealthLedger frontend React/Vite-клиент WealthLedger. Клиент получает данные только через typed API backend; прямые вызовы T-Bank из frontend не допускаются. ## Быстрый старт Требуется Node 24 (`.nvmrc`). Создайте `.env` из примера и установите зависимости строго из lockfile: ```bash cp .env.example .env nvm use --silent npm ci npm run dev ``` `VITE_API_BASE_URL` по умолчанию указывает на `http://localhost:3000`. Для commits установите [Gitleaks](https://github.com/gitleaks/gitleaks/releases/tag/v8.30.1) 8.30.1 в `PATH` (macOS: `brew install gitleaks`). Pre-commit сначала проверяет staged diff с полной редакцией секретов, затем запускает formatting и FSD checks. Находка или отсутствие Gitleaks блокирует commit. Проверка этого поведения: `node --test scripts/test-pre-commit.mjs`. ## Контейнерный image `Dockerfile` имеет targets `development` (Vite HMR и proxy `/api/` во внутренний backend) и `production` (nginx на 8080, SPA fallback и same-origin API proxy). Node соответствует `.nvmrc`. Build context ограничен `.dockerignore`; `.env`, local DB и host node_modules не попадают в image. Изменение `packages/tokens` или `packages/ui` требует rebuild development image, если локальный запуск использует bind-mount только `src`/`public`. ```bash docker build --target development -t wealthledger-frontend:development . docker build --target production -t wealthledger-frontend:production . ``` Gitea `image.yml` запускается на защищённом release tag `vX.Y.Z`, проверяет package/lock version и canonical gates. Runner `ubuntu-latest` должен иметь Docker/buildx. Образы хранятся в Nexus: `nexus.personal.ksv741.ru/docker-hosted/wealthledger/frontend`. Пользователь `wealthledger-ci` задаётся Actions secrets `NEXUS_USERNAME`/`NEXUS_PASSWORD`; эта же учётная запись используется сервером для загрузки образов. Docker endpoint и token realm должны использовать HTTPS; в `docker-hosted` включается запрет redeploy. Прежние Gitea secrets сохраняются на время перехода и не передаются в Nexus. Публикуются semver tags, без `latest`; существующие release tags не перезаписываются. Production выбирает опубликованный version tag. Compose topology принадлежит workspace, эксплуатационная последовательность — [документация](https://git.personal.ksv741.ru/WealthLedger/docs), страница `architecture/deployment.md`. Проверка публикации требует настроенного registry/runner; локальный build не доказывает package permissions. ## Установка как приложение (PWA) Production-сборка поддерживает установку WealthLedger в отдельное окно через `vite-plugin-pwa`. Нужен HTTPS или localhost. В Chrome/Edge выберите «Установить WealthLedger» в меню браузера или значок установки в адресной строке; на iPhone и iPad — Safari → «Поделиться» → «На экран Домой». Доступность установки зависит от браузера и ОС. Service worker кеширует только статическую оболочку, включая страницы SPA. Вход, финансовые данные и сохранение изменений требуют сети: API, токены и ответы API не сохраняются в Cache Storage. Это не автономный финансовый клиент. Новая версия загружается в фоне и применяется после закрытия всех вкладок и окон WealthLedger и повторного открытия; открытые формы автоматически не перезагружаются. Локальная проверка: `nvm use --silent`, `npm run build`, `npm run preview`. В DevTools → Application проверьте Manifest, Service Workers и Cache Storage. В режиме `npm run dev` и в Storybook service worker не включается. ## Проверка ```bash nvm use --silent npm ci npx --no-install playwright install chromium npm run verify ``` Команда проверяет, что generated OpenAPI types соответствуют committed snapshot, затем запускает strict TypeScript, ESLint и проверку его конфигурации, один coverage-backed запуск Vitest, Knip, dependency rules приложения и stories, сборки приложения и Storybook. `npm run lint` запускает ESLint с `--max-warnings=0`; `npm run lint:fix` применяет автофиксы. [Конфигурация](eslint.config.mjs) подключает рекомендованные наборы всех установленных плагинов: TypeScript, React, Hooks, Refresh, Query, Unicorn, import-x, SonarJS, RegExp, Security, no-unsanitized, Perfectionist, Stylistic, Vitest, Testing Library, jest-dom и Storybook. Правила React, тестов и stories применяются к соответствующим файлам; generated/build artifacts исключены. Stylistic отвечает за форматирование JS/TS/TSX, Perfectionist — за сортировку. Сохраняются табы, двойные кавычки и точки с запятой. Biome удалён; Prettier не используется. CSS, JSON и Markdown не форматируются через ESLint. `lint-staged` исправляет JS/TS/TSX перед commit. `lint:eslint` сначала собирает private packages, чтобы анализ типов использовал актуальные declarations. `npm run test:lint` проверяет действующие presets и ошибочные примеры. `no-unsanitized` проверяет опасные DOM HTML-присваивания и вызовы; React `dangerouslySetInnerHTML` отдельно проверяется React-плагином. Точечные исключения в исходниках объясняют контракты и ограничения статического анализа; изменение nullable API fields, query keys или порядка race-операций ради линтера недопустимо. Архитектуру проверяет dependency-cruiser, поведение — Vitest/MSW и Chromium. Линтеры не заменяют semantic/security review. `jsx-a11y` без поддержки ESLint 10 остаётся отложенным в [workspace issue #18](https://git.personal.ksv741.ru/WealthLedger/main/issues/18). Vitest включает JSDOM-тесты и проверку геометрии секций позиций в headless Chromium при ширинах 375 и 1280 px. Browser project не использует JSDOM setup и его заглушки layout API. Только браузерная проверка: `npm run test:run -- --project='browser-*'`. После обновления Playwright повторите установку Chromium; CI устанавливает его с `--with-deps` для Linux. Отдельный CI workflow `Frontend security` на push, pull request и ежедневно проверяет полную доступную историю Gitleaks 8.30.1 и выполняет `npm audit --include=dev --audit-level=low`. Любой advisory блокирует security job; исключений frontend нет. Локально можно выполнить те же команды: `gitleaks git --log-opts=--all --redact=100 --no-banner` и `npm audit --include=dev --audit-level=low`. Audit требует доступа к npm registry; проверка секретов выполняется локально и не выводит их значения. TypeScript остаётся на 5.9.3: `openapi-typescript@7.13.0` требует peer `^5.x`. Обновление до TypeScript 7 из Renovate dashboard откладывается до совместимого релиза генератора; `--force`/`--legacy-peer-deps` для обхода этого ограничения не используются. Если backend-контракт изменился, обновите `src/shared/api/spec/openapi.json` копированием backend OpenAPI document и затем выполните `npm run openapi:generate`. ## SonarQube Проект `wealthledger-frontend` использует настройки из `sonar-project.properties`. Анализируются исходники `src`; тесты учитываются отдельно, а Storybook fixtures, test helpers, generated API types и OpenAPI snapshot исключены. `npm run test:coverage` (также входит в `npm run verify`) создаёт `coverage/lcov.info` для импорта покрытия. Для первого анализа установите SonarScanner CLI, совместимый с версией сервера, и выполните из корня frontend: ```bash nvm use --silent npm ci npm run test:coverage # SONAR_TOKEN передайте через окружение; не сохраняйте токен в репозитории. sonar-scanner ``` В Gitea откройте frontend → Settings → Actions → Secrets и добавьте `SONAR_TOKEN` с токеном анализа проекта. Переименовывать основную ветку в SonarQube не требуется; `sonar.branch.name` не задаётся. Используется Community Edition 9.9.8: CI отправляет только push в `develop`, после успешного `verify`, и ожидает результат Quality Gate. PR и остальные ветки проходят обычные проверки без отправки в SonarQube. Отрицательный Quality Gate или ошибка scanner завершает `verify` с ошибкой. Runner должен поддерживать Docker actions и иметь HTTPS-доступ к `https://sonarqube.personal.ksv741.ru`, GitHub и Docker Hub. Docker action SonarSource закреплён на commit версии 2.3.0; scanner запускается отдельно от Node 24, используемого для сборки. `tsconfig.sonar.json` содержит совместимые со старым анализатором TS настройки; приложение проверяется штатным TypeScript 5.9 через `npm run verify`. После обновления сервера пересмотрите scanner и возможность использовать основной tsconfig. Успешный локальный `verify` не доказывает доступность сервера и успешность анализа в CI. ## UI-каталог Storybook После `nvm use --silent` выполните `npm run dev:catalog`; статическая сборка — `npm run build:catalog`. Каталог использует общую тему приложения, вымышленные данные и локальные действия без application API. Примеры не доказывают восстановление реального запроса. Визуальные роли описаны в [UI convention](.ai/conventions/ui.md#визуальная-основа). ## Документация - [AGENTS: правила и источники](AGENTS.md) - [Выбор owner/FSD](.ai/architecture.md) - [UI, state, формы и компоненты](.ai/conventions/ui.md) - [Data/API/cache](.ai/conventions/data.md) - [Testing](.ai/conventions/testing.md) - [UI-каталог Storybook](#ui-каталог-storybook) Checkout frontend содержит все входы сборки и проверок. Публичная схема API поступает как отдельный artifact; команды frontend используют только её локальную копию и не обращаются к исходникам или CI сервера. Доставка новой схемы — явное обновление snapshot с review и `npm run openapi:generate`. Runtime URL API задаётся окружением, а не расположением другого репозитория. Разработка идёт напрямую по задаче пользователя; OpenSpec/SDD — по явному запросу. Knip исключает `conventional-changelog-conventionalcommits`: release-it загружает preset по имени из `.release-it.json`. Версия preset закреплена напрямую. ## Релизы Репозиторий выпускается независимо. `develop` — интеграция, `main` — принятые релизы. Работа: `codex/` от актуального `develop`, PR в `develop`. Обычные PR допускают squash; release и обратная синхронизация сохраняют историю. Commits и PR titles — Conventional Commits, включая `spec:`. Версии вручную не повышай. 1. Получи `main`, `develop` и теги: `git fetch origin --tags`. Убедись, что `develop` содержит последний релиз. Создай `release/` от проверенного `develop`; `nvm use --silent`, `npm ci`. Hotfix начинается от `main` в `release/hotfix-` и проходит тот же процесс. 2. На чистой ветке выполни `npm run release:preview`, просмотри версию/changelog, затем `npm run release:prepare`. Команда создаёт `chore(release): X.Y.Z`, обновляет manifest/lockfile/CHANGELOG; тег и push не выполняет. Не обходи guards ветки, чистого дерева, отсутствующих тегов или уже подготовленной версии. 3. Проверь diff и штатные gates этого репозитория. Отправь только release-ветку, открой PR в `main` с заголовком `chore(release): X.Y.Z`. После зелёных `verify`, `security`, `commits` выполни fast-forward-only merge. Если `main` изменился, согласуй историю и повтори проверки кандидата. 4. Получи удалённый `main`, сравни его HEAD с проверенным release SHA и версией manifest/lockfile. Создай аннотированный `vX.Y.Z` именно на этом SHA: `git tag -a vX.Y.Z -m "Release X.Y.Z"`, затем отправь только `git push origin refs/tags/vX.Y.Z`. Не запускай release-it повторно на `main`. 5. Верни release-коммит через PR `main → develop` без squash/rebase; проверь, что `develop` содержит тег. Опубликованные теги не перемещай. При сбое после merge создай отсутствующий тег без нового bump; до merge исправь и проверь существующий кандидат без повторного повышения версии. `feat:` повышает minor, `fix:`/`perf:` и прочие разрешённые типы — patch; `!`/`BREAKING CHANGE:` — major, включая версии до 1.0. `chore(release):` исключён из рекомендации и changelog. Конфигурация — `.release-it.json`. В Gitea для `main`/`develop` должны быть запрещены прямой/force push и обход администратором, обязательны `verify`, `security`, `commits`; теги `v*` защищены. Workflow сам не включает эти настройки. Отдельное review approval для одного владельца не требуется. Npm publishing, deploy и Gitea Releases не включены. ## Локальные пакеты `packages/tokens` — private npm workspace с единственным export `@wealthledger/tokens/tokens.json`. `packages/ui` экспортирует собранные `@wealthledger/ui/theme` (JS + declarations) и `@wealthledger/ui/styles.css`. Корневой export `@wealthledger/ui` содержит EntryCard, StateView и LoadingSkeleton; Потребители импортируют компоненты напрямую из `@wealthledger/ui`. CSS явно подключается после Mantine; providers остаются в приложении/Storybook. Тема доступна через `@wealthledger/ui/theme`; compatibility re-exports удалены. JSON не требует сборки. `npm ci` связывает workspaces, а существующие команды разработки, проверок и сборки сначала выполняют `npm run build:packages`. Library build использует отдельный Vite config без app plugins и TypeScript для declarations. После изменения package sources перезапустите `npm run dev` или `npm run dev:catalog`; отдельный watch-процесс пока не нужен. Начальная версия manifest взята из приложения; отдельный release-процесс и публикация не настроены. Private `@wealthledger/icons` экспортирует `AddIcon`, `EditIcon`, `DeleteIcon` и type `IconProps`. Единственный source рисунка — TSX в `packages/icons/src/svg`; поставка содержит ESM/declarations, без raw SVG, CSS, Mantine и motion. React — peer/external. Размер `size` по умолчанию 24, `strokeWidth` — 1.5; цвет наследуется через `currentColor`, SVG props можно передать явно. В action используйте декоративный ``, доступное имя задавайте кнопке. Standalone пример: ``. Поддерживается также стандартный `aria-labelledby`; подпись выбирает caller. Card/Cards используют пакетные Add/Edit/Delete с прежними размерами/strokeWidth. Restore/category SVG и предметный BankOperationIcon остаются в приложении.