diff --git a/.ai/CONVENTIONS.md b/.ai/CONVENTIONS.md deleted file mode 100644 index 5b5becd..0000000 --- a/.ai/CONVENTIONS.md +++ /dev/null @@ -1,23 +0,0 @@ -# Frontend router - -Читай строки, которые активирует задача, затронутый путь **или** обнаруженная -граница. Объединяй совпадения; якорь разрешает чтение только нужного раздела. -Не загружай все conventions из-за одного frontend-файла. - -| Решение / сигнал | Читать до решения | -| --- | --- | -| Создать/перенести module/component/slice; выбрать page/entity/feature/shared; cross-slice imports | [Architecture](conventions/frontend-architecture.md) | -| Component/props, container/VM/view, form, local state/effect, loading/error/retry, Mantine/UI | [UI](conventions/frontend-ui.md) | -| Только продуктовая строка или Zod message | [Строки](conventions/frontend-ui.md#строки-и-валидация) | -| TS declaration/import/export, domain literals, magic values | [Код](conventions/frontend-ui.md#код-и-components) + [Domain literals](conventions/frontend-ui.md#domain-literals-and-magic-values) | -| query/mutation, shared/api, server state/cache, generated types, Bearer/refresh | [Data](conventions/frontend-data.md) | -| Тесты или behavior с testing risk | [Testing](conventions/frontend-testing.md) | -| Новый abstraction/dependency, refactor или неоднозначный scope | [Общий выбор реализации](../../AGENTS.md) | -| Request/response field, route/status/error shape, OpenAPI snapshot/generated type | [Источник HTTP-контракта](../../docs/architecture/http-api.md#источник-контракта) | -| Login/refresh/logout, token/cookie, auth ownership | Data + [security](../../backend/.ai/conventions/security-auth.md) + Testing | -| Storybook/theme/tokens/shared UI | UI + точные [источники](SOURCES.md) дизайн-системы/каталога | - -UI-only props/presentation требуют UI, без API/structure/testing при отсутствии -их triggers. Показ query state не означает изменение data fetching. -Изменение тестов активирует Testing и без production diff. Нужные project facts -выбирай через SOURCES. Повтори routing при новой границе и по итоговому diff. diff --git a/.ai/SOURCES.md b/.ai/SOURCES.md deleted file mode 100644 index 916b004..0000000 --- a/.ai/SOURCES.md +++ /dev/null @@ -1,18 +0,0 @@ -# Источники frontend - -| Что нужно установить | Владелец | -| --- | --- | -| Какое implementation-решение принять | [CONVENTIONS](CONVENTIONS.md) | -| HTTP wire contract и направление генерации | Backend-owned contract → provided snapshot → [Источник HTTP-контракта](../../docs/architecture/http-api.md#источник-контракта) | -| Error codes/validation/error envelope | [docs: HTTP API](../../docs/architecture/http-api.md) | -| Auth/session, token storage и refresh | [Аутентификация](../../docs/domains/authentication/overview.md), [подключение брокера](../../docs/integrations/tbank.md) | -| Значение Money, cached snapshot, account/position/income | [docs: Domain](../../docs/glossary.md) | -| Mantine/theme/tokens и существующие stories | [Дизайн-система](../../docs/design/overview.md), [UI-каталог](../README.md#ui-каталог-storybook) | -| As-built behavior нужной feature | [docs: Features](../../docs/README.md#предметные-области) | -| Существующие behavioral requirements, если нужны по задаче | [specs index](../../specs/AI_INDEX.md) | -| Действующие versions/scripts/check scope | package.json, lockfile, Biome/TS/dependency configs | - -Открывай точный source только по возникшему вопросу. Code examples показывают -pattern, но не переопределяют контракт. Specs не являются editable OpenAPI; -SDD-процедура для обычной работы не нужна. При конфликте источников сначала -установи факты, не меняй generated types или security policy по догадке. diff --git a/.ai/architecture.md b/.ai/architecture.md new file mode 100644 index 0000000..1eb45fa --- /dev/null +++ b/.ai/architecture.md @@ -0,0 +1,82 @@ +# Frontend architecture + +## Устройство и data flow + +React + TypeScript + Vite SPA. Router задаёт navigation; providers в `app` +подключают auth, TanStack Query, i18n и Mantine. Vite использует React Compiler; +Vitest — отдельную JSDOM конфигурацию. Точные versions/scripts — в +[package.json](../package.json), реализация — source of truth. + +```text +app → pages → features → entities → shared +page container → page VM → entity/feature query → shared/api → backend /api/v1 + ↓ + props → state/section views +``` + +Server state и lifecycle запроса принадлежат TanStack Query; VM собирает данные +для view. Transport — `openapi-fetch`/`openapi-react-query`, типизированные из +предоставленного snapshot через `openapi-typescript`. Backend владеет wire +contract; frontend не читает его checkout для генерации и не вызывает T-Bank. +[Data convention](conventions/data.md) задаёт auth/cache границы. + +## Выбор владельца + +Сначала найди владельца поведения и ближайший пример. Расширяй existing slice, +если ответственность остаётся связной. Размер файла или возможный будущий reuse +не создают новую архитектурную границу. + +| Ответственность | Размещение | +| --- | --- | +| Bootstrap, глобальные providers, routes | `app` | +| Композиция маршрута и поведение, нужное только этому экрану | `pages/` | +| Самостоятельная пользовательская возможность с собственным смыслом | `features/` | +| Представление/поведение бизнес-сущности | `entities/` | +| Переиспользуемый код без WealthLedger domain meaning | `shared` | + +Локальный FSD profile: `app → pages → features → entities → shared`, без `widgets`. +Крупный уникальный блок остаётся в своей page. Form, mutation или click handler +сами по себе не требуют feature. Два callers не делают domain helper generic shared. +Slice именуется по ответственности, а не `helpers`, `hooks` или `types`. + +**Безопасный путь:** при сомнении оставь код у ближайшего допустимого owner. +Извлекай его, когда появилась самостоятельная ответственность или реальный reuse. +Не вводи новый layer и не обходи public API, чтобы устранить неудобный import. + +## Imports и public API + +Cross-slice consumer использует `@/` и `index.ts` владельца, например +`@/entities/portfolio`. Alias Vite и TypeScript согласован. Другой slice не +импортирует внутренний файл напрямую. Slices `pages`, `features`, `entities` не зависят от соседнего slice своего слоя; +композиция принадлежит вышележащему owner. Existing меж-slice исключения +не являются разрешением добавлять новые. + +Направление зависимостей и public imports частично проверяет +`npm run deps`; Knip проверяет использование exports, а не смысл placement. + +## UI foundation + +`shared/theme/tokens.json` — переносимый source design tokens; `theme.ts` +адаптирует доверенный JSON к Mantine без runtime schema. App и Storybook читают +public API `shared/theme`. Storybook показывает synthetic presentation states, +не использует application API. RTL/MSW проверяют поведение приложения отдельно. + +Existing route-aware AppShell расположен в shared/ui; это legacy placement, +а не образец нового shared primitive. Общий план улучшений читается только +при migration через [router](../AGENTS.md). +При изменении foundation прочитай [design system](../../docs/design/overview.md). + +## Примеры + +- Общая busy-область: существующий [LoadingSkeleton](../src/shared/ui/LoadingSkeleton.tsx), а не domain widget в shared. +- Account-income state views: [страница](../src/pages/account-income/ui/AccountIncomePage.tsx) и её flat `components/*.view.tsx`; новый page-local filter остаётся здесь. +- Portfolio query: [entity public API](../src/entities/portfolio/index.ts); новая UI-сводка с бизнес-смыслом не становится shared только из-за нескольких экранов. + +## Проверка + +После placement/import change: `npm run deps`, `npm run typecheck`, `npm run knip`; +после собственного behavior — применимые tests. Финальный gate указан в AGENTS. +Existing dependency config не запрещает все same-layer imports: их и смысл +owner проверяет review. Не объявляй Steiger установленным; graph проверяет +текущий dependency-cruiser. Review отдельно проверяет смысл owner: разрешённый dependency graph не доказывает +правильный выбор слоя. diff --git a/.ai/conventions/frontend-data.md b/.ai/conventions/data.md similarity index 67% rename from .ai/conventions/frontend-data.md rename to .ai/conventions/data.md index 1b1428b..b73d106 100644 --- a/.ai/conventions/frontend-data.md +++ b/.ai/conventions/data.md @@ -23,7 +23,9 @@ type User = paths["/api/v1/auth/me"]["get"]["responses"][200]["content"]["applic ``` Domain/UI type допустим, когда он отличается по смыслу, а не копирует transport DTO. -Generated TS вручную не исправляется. Frontend-команды не читают соседний +Generated TS вручную не исправляется. Не добавляй параллельный fetch/axios +client, runtime schema, hash/checksum registry или дополнительный drift checker +без отдельной текущей потребности; existing OpenAPI flow уже владеет генерацией. Frontend-команды не читают соседний backend checkout и не проверяют backend runtime OpenAPI; изменение wire shape/status/route сохраняет [границу HTTP-контракта](../../../docs/architecture/http-api.md#источник-контракта). @@ -44,17 +46,35 @@ session-scoped cache. При auth/token change читай [security](../../../ba GET subtree; ошибка mutation не меняет generation/cache. Это отдельная граница от обычного фонового refetch: [connection policy](../../../docs/integrations/tbank.md#граница-финансового-кэша). +Mutations используют существующий generated client и query-key owner; +после успеха обновляй/invalidируй только связанные данные. Optimistic update +нужен конкретному UX и обязан иметь rollback/error behavior. + Default focus refetch выключен; freshness и retry определяются существующей [query policy](../../../docs/adr/007-react-query-fetch-policy.md) и feature source. При обычном refetch сохраняй Client Cached Snapshot, при session/connection смене не показывай данные прежнего владельца. UI presentation — в UI convention. +## Errors + +Извлекай code через existing `apiErrorCode` из shared/api. Backend Problem +Details code задаёт machine behavior; user-facing текст принадлежит i18n. +Network/unknown error получает безопасное локальное сообщение и retry, +не raw exception text. Input/domain failure сохраняет ввод и показывает +ошибку у ответственного form/section owner; не маскируй её успешным результатом. + +Если одна группа API codes требует одинакового recovery в нескольких views, +сведи классификацию у ближайшего общего owner. Не размножай magic comparisons +и не объявляй закрытый frontend error enum: generated `code` пока `string`. +Неизвестный новый code должен сохранять fallback. Рефакторинг существующих +разных recovery paths требует behavior proof, а не общей обработки всех ошибок. + ## Примеры и проверка [usePortfolio](../../src/entities/portfolio/api/usePortfolio.ts) — entity query; [shared client](../../src/shared/api/client.ts) — transport/auth boundary. Изменяя header/retry/invalidation, проверяй наблюдаемое поведение через MSW и -[Testing](frontend-testing.md); не обращайся к реальному T-Bank. +[Testing](testing.md); не обращайся к реальному T-Bank. `npm run openapi:check`, `npm run typecheck` и targeted tests проверяют разные части решения. Для API semantics нужны exact docs и проверки наблюдаемого HTTP-поведения на стороне backend. diff --git a/.ai/conventions/frontend-architecture.md b/.ai/conventions/frontend-architecture.md deleted file mode 100644 index ec83d4a..0000000 --- a/.ai/conventions/frontend-architecture.md +++ /dev/null @@ -1,56 +0,0 @@ -# Frontend architecture - -## Выбор владельца - -Сначала найди владельца поведения и ближайший пример. Расширяй existing slice, -если ответственность остаётся связной. Размер файла или возможный будущий reuse -не создают новую архитектурную границу. - -| Ответственность | Размещение | -| --- | --- | -| Bootstrap, глобальные providers, routes | `app` | -| Композиция маршрута и поведение, нужное только этому экрану | `pages/` | -| Самостоятельная пользовательская возможность с собственным смыслом | `features/` | -| Представление/поведение бизнес-сущности | `entities/` | -| Переиспользуемый код без WealthLedger domain meaning | `shared` | - -Локальный FSD profile: `app → pages → features → entities → shared`, без `widgets`. -Крупный уникальный блок остаётся в своей page. Form, mutation или click handler -сами по себе не требуют feature. Два callers не делают domain helper generic shared. -Slice именуется по ответственности, а не `helpers`, `hooks` или `types`. - -**Безопасный путь:** при сомнении оставь код у ближайшего допустимого owner. -Извлекай его, когда появилась самостоятельная ответственность или реальный reuse. -Не вводи новый layer и не обходи public API, чтобы устранить неудобный import. - -## Imports и public API - -Cross-slice consumer использует `@/` и `index.ts` владельца, например -`@/entities/portfolio`. Alias Vite и TypeScript согласован. Другой slice не -импортирует внутренний файл напрямую. Направление зависимостей проверяет -`npm run deps`; Knip проверяет использование exports, а не смысл placement. - -## UI foundation - -Общий visual foundation принадлежит `shared/theme`. `tokens.json` — переносимый -WealthLedger v1 source; `theme.ts` адаптирует доверенный JSON в Mantine без runtime -schema. App и Storybook используют public API `shared/theme/index.ts`: theme, -designTokens, color roles, CSSVariablesResolver и base styles. Внешний consumer -читает JSON напрямую. Mantine defaults и локальные domain styles остаются у владельцев. -`shared/ui` содержит StateView/LoadingSkeleton. Existing route-aware AppShell также -лежит в shared/ui, но не является общим catalog primitive; его расположение не -служит основанием переносить новую domain/navigation логику в shared. -Изменяя foundation, прочитай [design system](../../../docs/design/overview.md). - -## Примеры - -- Общая busy-область: существующий [LoadingSkeleton](../../src/shared/ui/LoadingSkeleton.tsx), а не domain widget в shared. -- Account-income state views: [страница](../../src/pages/account-income/ui/AccountIncomePage.tsx) и её flat `components/*.view.tsx`; новый page-local filter остаётся здесь. -- Portfolio query: [entity public API](../../src/entities/portfolio/index.ts); новая UI-сводка с бизнес-смыслом не становится shared только из-за нескольких экранов. - -## Проверка - -После placement/import change: `npm run deps`, `npm run typecheck`, `npm run knip`; -после собственного behavior — применимые tests. Финальный gate указан в AGENTS. -Review отдельно проверяет смысл owner: разрешённый dependency graph не доказывает -правильный выбор слоя. diff --git a/.ai/conventions/storybook.md b/.ai/conventions/storybook.md new file mode 100644 index 0000000..62fb6b9 --- /dev/null +++ b/.ai/conventions/storybook.md @@ -0,0 +1,33 @@ +# Storybook + +## Когда нужна story + +- Shared UI/theme: показывай принятые visual roles и переиспользуемое поведение. +- Domain/page presentation: добавляй story, когда независимый visual state + нужен для разработки/review или является устойчивым UI pattern. +- Выбирай значимые состояния: default, loading, disabled, error, empty, + длинный контент и смысловые variants по применимости. Не строй декартово + произведение props и story для каждого trivial wrapper/helper. +- Fetch/auth/router container и VM проверяй application tests; story получает + synthetic props presentation component. Каталог не нужен каждому файлу. + +## Изоляция и interaction + +Используй CSF, `Meta`/`StoryObj`, `satisfies Meta`, локальные fixtures и actions. +Общие Mantine/theme/i18n/Dates providers уже есть в +[preview](../../.storybook/preview.tsx); не создавай второй theme source. +Stories не достигают application API, auth, VM и query providers даже транзитивно; +границу проверяет `deps:storybook`. Не добавляй MSW worker ради каталога. + +`play` через `storybook/test` нужен, когда interaction иллюстрирует собственный +контракт: keyboard activation, доступность действия, local callback. Не повторяй +RTL suite и не тестируй внутренности Mantine. Существующий +[Button](../../src/shared/ui/Button.stories.tsx) показывает themed control +без production wrapper; [LoadingSkeleton](../../src/shared/ui/LoadingSkeleton.stories.tsx) +— значимые busy states. + +`npm run typecheck`, `npm run deps:storybook`, `npm run build:catalog` проверяют +типы, изоляцию и сборку. Build не выполняет `play` как CI interaction suite; +для проверки interaction открой story в браузере, для regression используй +существующий [Testing](testing.md). Визуальное соответствие задаче, +keyboard и узкий viewport проверяются отдельно от успешной сборки. diff --git a/.ai/conventions/frontend-testing.md b/.ai/conventions/testing.md similarity index 88% rename from .ai/conventions/frontend-testing.md rename to .ai/conventions/testing.md index 143be13..8b3513a 100644 --- a/.ai/conventions/frontend-testing.md +++ b/.ai/conventions/testing.md @@ -1,5 +1,9 @@ # Frontend testing +Перед нетривиальными тестами сформулируй +[общий Test Intent](../../../.ai/conventions/testing.md); отдельный документ +не требуется. Trivial low-risk изменения обходятся без него. + ## Выбор проверки Читай при изменении тестов, доступа, денежных расчётов, восстановления @@ -11,6 +15,7 @@ | Чистое domain преобразование/денежный расчёт | Unit case с граничными значениями | | Форма, route access, recovery, UI-state selection | RTL component/route test через видимое поведение | | Headers, refresh/retry, query invalidation | MSW на transport boundary | +| Критический browser flow, который JSDOM не доказывает | Browser/e2e proof существующими доступными средствами; отдельный frontend e2e runner пока не принят | | Только declarative props/Storybook fixture | Type/build + semantic/visual review; unit test без собственной логики не нужен | Tests colocated с source (`*.test.tsx` для UI, существующий `*.test.ts` для pure code). diff --git a/.ai/conventions/typescript-react.md b/.ai/conventions/typescript-react.md new file mode 100644 index 0000000..4dd839c --- /dev/null +++ b/.ai/conventions/typescript-react.md @@ -0,0 +1,53 @@ +# TypeScript и React + +## Types и components + +- Сохраняй strict TS profile и `import type`. Для недоверенного значения используй + `unknown` с narrowing; `any` не заменяет DTO/validation. Локальное исключение + на нетипизированной boundary должно быть узким и объяснённым. +- `enum` и `namespace` не используются: frontend имеет `erasableSyntaxOnly`. + Для состояния с разными payload выбирай discriminated union; не добавляй + необязательные поля, допускающие невозможные комбинации. +- Named components — const-valued `FC`; named props находятся рядом или + в types-файле. Принимай `props`, destructure в body, без inline props typing. + Только children — `FC`; дополнительные props — named type + на основе `PropsWithChildren<{ … }>`; без props — `FC`. +- Обычные exports — нижний named block; barrel делает прямой re-export. + CSF сохраняет default meta и прямые story exports; configuration exceptions + заданы в [Biome](../../biome.json). Не распространяй их на application code. + +## Hooks и derived state + +Hooks вызываются без условий и ранних выходов перед ними; custom hook нужен +связному поведению, а не новому имени над одним вызовом. Derived value вычисляй +при render/в VM. Пользовательское действие обрабатывай handler; `useEffect` +оставляй синхронизации с внешней системой с корректным cleanup/dependencies. + +`useMemo`, `useCallback`, `memo` нужны измеренной стоимости или конкретному +контракту стабильной identity; не добавляй их ко всем значениям/handlers. +React Compiler включён в Vite, но не доказывает выигрыш и не заменяет effect +correctness. Не удаляй существующую memoization без понимания её consumers. +Container/VM/view и state owners — в [UI](ui.md). + +## Domain literals and magic values + +Общее правило ownership и символических значений — +[Domain values](../../../.ai/conventions/code-health.md#domain-values). +Backend vocabulary бери из generated `paths`, не создавай ручную копию enum. +Текущий `schema.d.ts` содержит только types: typed wire literals допустимы, +когда generator не предоставляет runtime values. + +`ProblemDetailsDto.code` сейчас `string`. Не объявляй закрытый frontend error +enum и не превращай неизвестный code в известный через cast. Повторяющуюся +обработку своди у ответственного owner; [errors](data.md#errors). + +`noMagicNumbers` в Biome — error; fixtures/tests/stories исключены только +из этого правила. Policy получает имя и единицы; подавление допустимо для +incidental value с объяснением. Обычный UI-текст не превращай в constant: +его owner — [i18n](ui.md#строки-и-валидация). +Semantic magic strings и props profile требуют review. + +## Проверка + +`npm run typecheck` и `npm run lint`; behavior проверяй по +[Testing](testing.md), placement — по [FSD](../architecture.md). diff --git a/.ai/conventions/frontend-ui.md b/.ai/conventions/ui.md similarity index 52% rename from .ai/conventions/frontend-ui.md rename to .ai/conventions/ui.md index c211935..1f98622 100644 --- a/.ai/conventions/frontend-ui.md +++ b/.ai/conventions/ui.md @@ -11,21 +11,28 @@ Mantine component и общую тему. Для выпадающего выбо ограничения JSDOM решай в test setup, сохраняя production UI. Семантический HTML для структуры страницы остаётся уместным. +Icons ищи у существующего owner (например, +[BankOperationIcon](../../src/entities/bank-card/ui/BankOperationIcon.tsx)); +общая icon library сейчас не принята. Не добавляй пакет ради одного символа. Business/domain +components живут у feature/entity/page, даже при повторном использовании. + Wrapper нужен для проектной ответственности, а не ради нового имени для Button. Локальный CSS module принадлежит component; общий token вводи для реально общей роли через [design system](../../../docs/design/overview.md). | Состояние | Владелец | | --- | --- | -| Ответ сервера / query lifecycle | TanStack Query, [data rules](frontend-data.md) | +| Ответ сервера / query lifecycle | TanStack Query, [data rules](data.md) | | Ввод и ошибки формы | React Hook Form + Zod, container/VM | | Параметр навигации, который должен переживать переход/ссылку | Router/URL | | Локальное открытие, focus, rendering fallback | React UI state | | Значение, вычисляемое из уже доступных inputs | Вычисление при render/в VM, без копирования через effect в state | -Effect нужен для синхронизации с внешней системой; действие пользователя -обрабатывай в handler. Не добавляй store, memoization или abstraction без текущей -потребности. Inspect ближайший component до создания нового pattern. +State, который нужен в ссылке, back/forward или после navigation, хранится в URL +через existing router search params. Остальной UI state остаётся у ближайшего +owner. Shared client store нужен только нескольким consumers без подходящего +общего owner; наличие двух компонентов само по себе его не требует. +Hooks/effects/derived state — по [TypeScript/React](typescript-react.md#hooks-и-derived-state). ## Container, VM и view @@ -76,71 +83,12 @@ Button loading при mutation и компактный cached-snapshot progress ## Код и components -Style/type automation задают configs. Используй `import type`; `enum` и -`namespace` в frontend не применяются. Named components — const-valued; Biome -проверяет declarations, no-default-export и exports-last в своём scope. - -Обязательный review: `FC` и named props рядом/в types-файле; без inline -props typing и destructuring в параметрах. Принимай `props`, destructure в body. -Только children — `FC`; дополнительные props — named type -на основе `PropsWithChildren<{ ... }>`. Без props — `FC`. -Своди обычные exports к читаемому нижнему named block; barrel делает прямой -re-export без промежуточного import. - -CSF `*.stories.tsx` сохраняют default meta и прямые story exports для indexer. -Vite/Vitest configs и `.storybook/main.ts`/`preview.tsx` имеют только -конфигурационное default-export exception. Scope исключений принадлежит biome.json. +Types, props, hooks и memoization — в [TypeScript/React](typescript-react.md). ## Domain literals and magic values -Finite business vocabulary must be modeled explicitly; incidental implementation -literals stay inline. Это относится к error codes, statuses, operation/account/ -instrument/transaction types, permissions, roles, feature states и бизнес-режимам. - -Перед новым определением найди existing owner/type/value. В branching и comparisons -используй символическое значение владельца; не повторяй raw domain literals. -Храни словарь у domain/module/entity с соблюдением границ слоёв, без глобальной -свалки `constants.ts`. Сохраняй принятый pattern; для нового project-owned словаря -без подходящего аналога используй `as const` с derived union: - -```ts -const BankErrorCode = { - InvalidDate: "BANK_INVALID_DATE", - ImmutableOperation: "BANK_IMMUTABLE_OPERATION", -} as const; -type BankErrorCode = (typeof BankErrorCode)[keyof typeof BankErrorCode]; - -// Вместо code === "BANK_INVALID_DATE": -if (code === BankErrorCode.InvalidDate) { /* existing handling */ } -``` - -Значимые numeric policies (timeout, retry, page size, threshold, coefficient) -называй по смыслу и единицам: `REQUEST_TIMEOUT_MS`, `CACHE_TTL_SECONDS`, -`MAX_RETRY_COUNT`, `PAGE_SIZE`. Оставляй inline UI labels в принятом месте, -log messages, индексы, `index + 1`, `Math.max(value, 0)`, `page ?? 1`, -`percentage * 100` и очевидные локальные детали без semantic benefit. -При неясном owner/смысле оставь значение и укажи ограничение в review. - -Не мигрируй корректные existing enum/union styles массово. Не добавляй ради -правила factories, registries, schemas, loaders, abstraction layers, dependencies, -custom lint/AST rules, generation/validation scripts или тесты существования -констант. Semantic review проверяет ownership, единое написание и неизменность -wire values; обычные behavior tests проверяют поведение. - -Backend API vocabulary бери из generated OpenAPI types/client, runtime values — -когда генератор их предоставляет. Не создавай ручную копию backend enum/object. -Текущий `schema.d.ts` предоставляет только types: используй indexed-access type -из `paths`; typed literals в запросах/сравнениях допустимы при отсутствии -runtime values. `ProblemDetailsDto.code` сейчас `string`: не выдумывай закрытый -frontend error enum. Развитие API source — отдельное contract-решение. -Frontend `enum` по-прежнему запрещён локальным TS/style profile. - -`noMagicNumbers` включён как error в Biome. Fixtures, tests и stories исключены -из этого правила; остальные проверки для них сохраняются. Используй имена -политик/единиц и existing values. Точечное подавление допустимо для очевидного -incidental значения с объяснением, например перевода доли в проценты; не подавляй -скрытые бизнес-лимиты. Semantic magic strings проверяются conventions/review: в -установленном Biome нет такого встроенного правила, новый инструмент не добавляем. +Правило и generated API exception — в +[TypeScript/React](typescript-react.md#domain-literals-and-magic-values). ## Строки и валидация @@ -153,6 +101,6 @@ incidental значения с объяснением, например пере ## Проверка Type/style: `npm run typecheck`, `npm run lint`. Для risk behavior/изменения тестов -используй [Testing](frontend-testing.md). UI: keyboard, narrow viewport, -loading/error/empty/loaded и recovery по применимости. [Storybook](../../README.md#ui-каталог-storybook) -уже установлен; используй его как reference, но сверяй UI с задачей отдельно от build. +используй [Testing](testing.md). UI: keyboard, narrow viewport, +loading/error/empty/loaded и recovery по применимости. [Storybook](storybook.md) задаёт выбор stories и изоляцию каталога; +сверяй UI с задачей отдельно от build. diff --git a/AGENTS.md b/AGENTS.md index f215523..709069b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,36 +1,39 @@ -# Frontend +# Frontend router -React/Vite-клиент; общие правила работы — в root `AGENTS.md`. -До frontend-решения прочитай [router](.ai/CONVENTIONS.md) и -[sources](.ai/SOURCES.md), затем только применимые документы/разделы. +Общие инструкции — [workspace AGENTS](../AGENTS.md). Объединяй triggers задачи, +путей и обнаруженных boundaries; читай только нужные темы до решения. +Системные факты/spec/ADR выбирай в разделе [Источники](#источники) по вопросу. -## Проверка +| Решение / сигнал | Читать | +| --- | --- | +| Устройство/data flow, component/module/slice, owner/FSD, cross-slice imports | [Architecture](.ai/architecture.md) | +| TS types/props/imports/exports, hooks/effects, derived state, memoization, domain literals | [TypeScript/React](.ai/conventions/typescript-react.md) | +| UI/Mantine, form, container/VM/view, state owner, loading/error/recovery | [UI](.ai/conventions/ui.md) | +| Только продуктовая строка / Zod message | [Строки](.ai/conventions/ui.md#строки-и-валидация) | +| Query/mutation, server cache, shared/api, generated types | [Data](.ai/conventions/data.md) | +| API/network/application errors, codes и сообщения | [Errors](.ai/conventions/data.md#errors) | +| Login/refresh/logout, token/cookie, session ownership | Data + [Security](../backend/.ai/conventions/security-auth.md) + [Testing](.ai/conventions/testing.md) | +| Request/response field, route/status/error shape, OpenAPI | Data + [HTTP contract source](../docs/architecture/http-api.md#источник-контракта) | +| Tests или behavior с regression risk | [Testing](.ai/conventions/testing.md) | +| Storybook/stories/play, theme/tokens/shared UI | [Storybook](.ai/conventions/storybook.md), UI; design facts — в разделе Источники | +| Helper/dependency/новый pattern, refactor/scope | [Code health](../.ai/conventions/code-health.md), для пакета — [Dependencies](../.ai/conventions/dependencies.md) | +| Выбор gate, tooling, завершение | [Guardrails](../.ai/conventions/guardrails.md) | -Перед Node-командами: `nvm use --silent` по `.nvmrc`. -После lockfile install `npm ci` полный gate — `npm run verify`. -Он проверяет OpenAPI drift, TypeScript, Biome, Vitest/coverage, Knip, -dependency boundaries и сборки приложения/Storybook без backend. -Точные scripts принадлежат `package.json`; targeted checks — у выбранного convention. +Показ query state не означает изменение fetching. Изменение тестов активирует +Testing и без production diff. Повтори routing по итоговому diff, включая новые +файлы. Migration backlog читается только при работе над соответствующим finding +через [plan](../.ai/migrations/conventions-v1/plan.md). -HTTP contract changes используют предоставленный OpenAPI snapshot; frontend -локально проверяет только соответствие generated types этому snapshot: -[Источник HTTP-контракта](../docs/architecture/http-api.md#источник-контракта). -Generated types редактируются через их source; T-Bank доступен только через backend. -Frontend dependencies используют exact versions. Pre-commit запускает staged -formatting и FSD checks; сообщения commits — Conventional Commits. -Если у задачи есть связанная issue этого репозитория, добавляй её номер -в конец заголовка: `ci: run full frontend verification #1`. -Без связанной issue номер необязателен: `chore(deps): update non-major dependencies`. -Формат Conventional Commits проверяет `commitlint.config.cjs`. +## Источники -Code/style checks автоматические только в scope конфигурации. i18n, props, -semantic placement и выбор полезных тестов обязательны при review. -Storybook уже доступен как reference; ожидаемый UI и его состояния определяет -задача, а визуальное соответствие проверяется отдельно от сборки. +Открывай только источник по возникшему вопросу; примеры кода не переопределяют +контракт. Specs задают behavior, а editable OpenAPI принадлежит backend. -## Ветки и релизы - -Работа — в отдельной ветке от `develop`, затем PR в `develop`; в `main` напрямую -не пушить. Release/hotfix и синхронизация веток — по [root README](../README.md#релизы-и-ветки). -Commits и заголовки PR — Conventional Commits; для спецификаций допустим `spec:`. -Перед подготовкой релиза прочитай root README; версии вручную не повышать. +| Вопрос | Источник | +| --- | --- | +| HTTP contract, validation/error codes | [HTTP API](../docs/architecture/http-api.md) | +| Auth/session/refresh, подключение брокера | [Authentication](../docs/domains/authentication/overview.md), [T-Bank](../docs/integrations/tbank.md) | +| Money, account/position/income/cache meanings | [Глоссарий](../docs/glossary.md) | +| Theme/tokens, UI-каталог | [Дизайн-система](../docs/design/overview.md), [Storybook](README.md#ui-каталог-storybook) | +| Поведение области / требования | [Области docs](../docs/README.md#предметные-области), [Specs index](../specs/AI_INDEX.md) | +| Versions/scripts/check scope | Manifest/lockfile, Biome/TypeScript/dependency configs | diff --git a/README.md b/README.md index ae92092..661e561 100644 --- a/README.md +++ b/README.md @@ -59,11 +59,11 @@ document и затем выполните `npm run openapi:generate`. ## Документация -- [Router conventions](.ai/CONVENTIONS.md) и [источники](.ai/SOURCES.md) -- [Выбор owner/FSD](.ai/conventions/frontend-architecture.md) -- [UI, state, формы и компоненты](.ai/conventions/frontend-ui.md) -- [Data/API/cache](.ai/conventions/frontend-data.md) -- [Testing](.ai/conventions/frontend-testing.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) - [Обзор поставленной системы](../docs/overview.md) - [Карта проектных фактов](../docs/README.md)