docs: refine frontend conventions and routing
Frontend security / security (push) Successful in 8s
Frontend verify / verify (push) Successful in 3m56s
Frontend security / security (pull_request) Successful in 9s
Frontend verify / verify (pull_request) Successful in 3m41s
Conventional commits / commits (pull_request) Successful in 19s
Frontend security / security (push) Successful in 8s
Frontend verify / verify (push) Successful in 3m56s
Frontend security / security (pull_request) Successful in 9s
Frontend verify / verify (pull_request) Successful in 3m41s
Conventional commits / commits (pull_request) Successful in 19s
This commit is contained in:
+7
-21
@@ -1,23 +1,9 @@
|
|||||||
# Frontend router
|
# Frontend conventions: вход
|
||||||
|
|
||||||
Читай строки, которые активирует задача, затронутый путь **или** обнаруженная
|
Актуальный routing по задаче, пути и boundary находится в [AGENTS](../AGENTS.md).
|
||||||
граница. Объединяй совпадения; якорь разрешает чтение только нужного раздела.
|
Выбирай только активированные темы; при новой границе повтори routing.
|
||||||
Не загружай все conventions из-за одного frontend-файла.
|
[Architecture](architecture.md) объясняет mental model, [SOURCES](SOURCES.md)
|
||||||
|
ведёт к системным фактам, specifications и ADR по конкретному вопросу.
|
||||||
|
|
||||||
| Решение / сигнал | Читать до решения |
|
Этот адрес сохранён для существующих входящих ссылок; второй копии правил
|
||||||
| --- | --- |
|
и таблицы маршрутов здесь нет.
|
||||||
| Создать/перенести 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.
|
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
| Что нужно установить | Владелец |
|
| Что нужно установить | Владелец |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| Какое implementation-решение принять | [CONVENTIONS](CONVENTIONS.md) |
|
| Какое implementation-решение принять | [AGENTS](../AGENTS.md) |
|
||||||
| HTTP wire contract и направление генерации | Backend-owned contract → provided snapshot → [Источник HTTP-контракта](../../docs/architecture/http-api.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) |
|
| 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) |
|
| Auth/session, token storage и refresh | [Аутентификация](../../docs/domains/authentication/overview.md), [подключение брокера](../../docs/integrations/tbank.md) |
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# Frontend mental model
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
`widgets` не используется. Pages владеют route composition и локальным поведением;
|
||||||
|
features — самостоятельными возможностями пользователя; entities — устойчивыми
|
||||||
|
доменными понятиями; shared — техническими primitives без domain responsibility.
|
||||||
|
Правила выделения owner и public `index.ts` — в
|
||||||
|
[FSD convention](conventions/frontend-architecture.md).
|
||||||
|
|
||||||
|
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/frontend-data.md) задаёт auth/cache границы.
|
||||||
|
|
||||||
|
`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](CONVENTIONS.md).
|
||||||
@@ -27,20 +27,17 @@ Slice именуется по ответственности, а не `helpers`,
|
|||||||
|
|
||||||
Cross-slice consumer использует `@/` и `index.ts` владельца, например
|
Cross-slice consumer использует `@/` и `index.ts` владельца, например
|
||||||
`@/entities/portfolio`. Alias Vite и TypeScript согласован. Другой slice не
|
`@/entities/portfolio`. Alias Vite и TypeScript согласован. Другой slice не
|
||||||
импортирует внутренний файл напрямую. Направление зависимостей проверяет
|
импортирует внутренний файл напрямую. Slices `pages`, `features`, `entities` не зависят от соседнего slice своего слоя;
|
||||||
|
композиция принадлежит вышележащему owner. Existing меж-slice исключения
|
||||||
|
не являются разрешением добавлять новые.
|
||||||
|
|
||||||
|
Направление зависимостей и public imports частично проверяет
|
||||||
`npm run deps`; Knip проверяет использование exports, а не смысл placement.
|
`npm run deps`; Knip проверяет использование exports, а не смысл placement.
|
||||||
|
|
||||||
## UI foundation
|
## UI foundation
|
||||||
|
|
||||||
Общий visual foundation принадлежит `shared/theme`. `tokens.json` — переносимый
|
Mental model, theme и data flow — в [architecture](../architecture.md).
|
||||||
WealthLedger v1 source; `theme.ts` адаптирует доверенный JSON в Mantine без runtime
|
При изменении foundation прочитай [design system](../../../docs/design/overview.md).
|
||||||
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).
|
|
||||||
|
|
||||||
## Примеры
|
## Примеры
|
||||||
|
|
||||||
@@ -52,5 +49,7 @@ designTokens, color roles, CSSVariablesResolver и base styles. Внешний c
|
|||||||
|
|
||||||
После placement/import change: `npm run deps`, `npm run typecheck`, `npm run knip`;
|
После placement/import change: `npm run deps`, `npm run typecheck`, `npm run knip`;
|
||||||
после собственного behavior — применимые tests. Финальный gate указан в AGENTS.
|
после собственного behavior — применимые tests. Финальный gate указан в AGENTS.
|
||||||
Review отдельно проверяет смысл owner: разрешённый dependency graph не доказывает
|
Existing dependency config не запрещает все same-layer imports: их и смысл
|
||||||
|
owner проверяет review. Не объявляй Steiger установленным; graph проверяет
|
||||||
|
текущий dependency-cruiser. Review отдельно проверяет смысл owner: разрешённый dependency graph не доказывает
|
||||||
правильный выбор слоя.
|
правильный выбор слоя.
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ type User = paths["/api/v1/auth/me"]["get"]["responses"][200]["content"]["applic
|
|||||||
```
|
```
|
||||||
|
|
||||||
Domain/UI type допустим, когда он отличается по смыслу, а не копирует transport DTO.
|
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
|
backend checkout и не проверяют backend runtime OpenAPI; изменение wire
|
||||||
shape/status/route сохраняет
|
shape/status/route сохраняет
|
||||||
[границу HTTP-контракта](../../../docs/architecture/http-api.md#источник-контракта).
|
[границу HTTP-контракта](../../../docs/architecture/http-api.md#источник-контракта).
|
||||||
@@ -44,11 +46,29 @@ session-scoped cache. При auth/token change читай [security](../../../ba
|
|||||||
GET subtree; ошибка mutation не меняет generation/cache. Это отдельная граница
|
GET subtree; ошибка mutation не меняет generation/cache. Это отдельная граница
|
||||||
от обычного фонового refetch: [connection policy](../../../docs/integrations/tbank.md#граница-финансового-кэша).
|
от обычного фонового 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 определяются существующей
|
Default focus refetch выключен; freshness и retry определяются существующей
|
||||||
[query policy](../../../docs/adr/007-react-query-fetch-policy.md) и feature source.
|
[query policy](../../../docs/adr/007-react-query-fetch-policy.md) и feature source.
|
||||||
При обычном refetch сохраняй Client Cached Snapshot, при session/connection
|
При обычном refetch сохраняй Client Cached Snapshot, при session/connection
|
||||||
смене не показывай данные прежнего владельца. UI presentation — в UI convention.
|
смене не показывай данные прежнего владельца. 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;
|
[usePortfolio](../../src/entities/portfolio/api/usePortfolio.ts) — entity query;
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
# Frontend testing
|
# Frontend testing
|
||||||
|
|
||||||
|
Перед нетривиальными тестами сформулируй
|
||||||
|
[общий Test Intent](../../../.ai/conventions/testing.md); отдельный документ
|
||||||
|
не требуется. Trivial low-risk изменения обходятся без него.
|
||||||
|
|
||||||
## Выбор проверки
|
## Выбор проверки
|
||||||
|
|
||||||
Читай при изменении тестов, доступа, денежных расчётов, восстановления
|
Читай при изменении тестов, доступа, денежных расчётов, восстановления
|
||||||
@@ -11,6 +15,7 @@
|
|||||||
| Чистое domain преобразование/денежный расчёт | Unit case с граничными значениями |
|
| Чистое domain преобразование/денежный расчёт | Unit case с граничными значениями |
|
||||||
| Форма, route access, recovery, UI-state selection | RTL component/route test через видимое поведение |
|
| Форма, route access, recovery, UI-state selection | RTL component/route test через видимое поведение |
|
||||||
| Headers, refresh/retry, query invalidation | MSW на transport boundary |
|
| 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 без собственной логики не нужен |
|
| Только declarative props/Storybook fixture | Type/build + semantic/visual review; unit test без собственной логики не нужен |
|
||||||
|
|
||||||
Tests colocated с source (`*.test.tsx` для UI, существующий `*.test.ts` для pure code).
|
Tests colocated с source (`*.test.tsx` для UI, существующий `*.test.ts` для pure code).
|
||||||
|
|||||||
@@ -11,6 +11,11 @@ Mantine component и общую тему. Для выпадающего выбо
|
|||||||
ограничения JSDOM решай в test setup, сохраняя production UI.
|
ограничения JSDOM решай в test setup, сохраняя production UI.
|
||||||
Семантический HTML для структуры страницы остаётся уместным.
|
Семантический HTML для структуры страницы остаётся уместным.
|
||||||
|
|
||||||
|
Icons ищи у существующего owner (например,
|
||||||
|
[BankOperationIcon](../../src/entities/bank-card/ui/BankOperationIcon.tsx));
|
||||||
|
общая icon library сейчас не принята. Не добавляй пакет ради одного символа. Business/domain
|
||||||
|
components живут у feature/entity/page, даже при повторном использовании.
|
||||||
|
|
||||||
Wrapper нужен для проектной ответственности, а не ради нового имени
|
Wrapper нужен для проектной ответственности, а не ради нового имени
|
||||||
для Button. Локальный CSS module принадлежит component; общий token вводи для
|
для Button. Локальный CSS module принадлежит component; общий token вводи для
|
||||||
реально общей роли через [design system](../../../docs/design/overview.md).
|
реально общей роли через [design system](../../../docs/design/overview.md).
|
||||||
@@ -23,9 +28,11 @@ Wrapper нужен для проектной ответственности, а
|
|||||||
| Локальное открытие, focus, rendering fallback | React UI state |
|
| Локальное открытие, focus, rendering fallback | React UI state |
|
||||||
| Значение, вычисляемое из уже доступных inputs | Вычисление при render/в VM, без копирования через effect в state |
|
| Значение, вычисляемое из уже доступных inputs | Вычисление при render/в VM, без копирования через effect в state |
|
||||||
|
|
||||||
Effect нужен для синхронизации с внешней системой; действие пользователя
|
State, который нужен в ссылке, back/forward или после navigation, хранится в URL
|
||||||
обрабатывай в handler. Не добавляй store, memoization или abstraction без текущей
|
через existing router search params. Остальной UI state остаётся у ближайшего
|
||||||
потребности. Inspect ближайший component до создания нового pattern.
|
owner. Shared client store нужен только нескольким consumers без подходящего
|
||||||
|
общего owner; наличие двух компонентов само по себе его не требует.
|
||||||
|
Hooks/effects/derived state — по [TypeScript/React](typescript-react.md#hooks-и-derived-state).
|
||||||
|
|
||||||
## Container, VM и view
|
## Container, VM и view
|
||||||
|
|
||||||
@@ -76,71 +83,12 @@ Button loading при mutation и компактный cached-snapshot progress
|
|||||||
|
|
||||||
## Код и components
|
## Код и components
|
||||||
|
|
||||||
Style/type automation задают configs. Используй `import type`; `enum` и
|
Types, props, hooks и memoization — в [TypeScript/React](typescript-react.md).
|
||||||
`namespace` в frontend не применяются. Named components — const-valued; Biome
|
|
||||||
проверяет declarations, no-default-export и exports-last в своём scope.
|
|
||||||
|
|
||||||
Обязательный review: `FC<Props>` и named props рядом/в types-файле; без inline
|
|
||||||
props typing и destructuring в параметрах. Принимай `props`, destructure в body.
|
|
||||||
Только children — `FC<PropsWithChildren>`; дополнительные 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.
|
|
||||||
|
|
||||||
## Domain literals and magic values
|
## Domain literals and magic values
|
||||||
|
|
||||||
Finite business vocabulary must be modeled explicitly; incidental implementation
|
Правило и generated API exception — в
|
||||||
literals stay inline. Это относится к error codes, statuses, operation/account/
|
[TypeScript/React](typescript-react.md#domain-literals-and-magic-values).
|
||||||
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 нет такого встроенного правила, новый инструмент не добавляем.
|
|
||||||
|
|
||||||
## Строки и валидация
|
## Строки и валидация
|
||||||
|
|
||||||
@@ -154,5 +102,5 @@ incidental значения с объяснением, например пере
|
|||||||
|
|
||||||
Type/style: `npm run typecheck`, `npm run lint`. Для risk behavior/изменения тестов
|
Type/style: `npm run typecheck`, `npm run lint`. Для risk behavior/изменения тестов
|
||||||
используй [Testing](frontend-testing.md). UI: keyboard, narrow viewport,
|
используй [Testing](frontend-testing.md). UI: keyboard, narrow viewport,
|
||||||
loading/error/empty/loaded и recovery по применимости. [Storybook](../../README.md#ui-каталог-storybook)
|
loading/error/empty/loaded и recovery по применимости. [Storybook](storybook.md) задаёт выбор stories и изоляцию каталога;
|
||||||
уже установлен; используй его как reference, но сверяй UI с задачей отдельно от build.
|
сверяй UI с задачей отдельно от build.
|
||||||
|
|||||||
@@ -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](frontend-testing.md). Визуальное соответствие задаче,
|
||||||
|
keyboard и узкий viewport проверяются отдельно от успешной сборки.
|
||||||
@@ -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<Props>`; named props находятся рядом или
|
||||||
|
в types-файле. Принимай `props`, destructure в body, без inline props typing.
|
||||||
|
Только children — `FC<PropsWithChildren>`; дополнительные 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](frontend-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](frontend-data.md#errors).
|
||||||
|
|
||||||
|
`noMagicNumbers` в Biome — error; fixtures/tests/stories исключены только
|
||||||
|
из этого правила. Policy получает имя и единицы; подавление допустимо для
|
||||||
|
incidental value с объяснением. Обычный UI-текст не превращай в constant:
|
||||||
|
его owner — [i18n](frontend-ui.md#строки-и-валидация).
|
||||||
|
Semantic magic strings и props profile требуют review.
|
||||||
|
|
||||||
|
## Проверка
|
||||||
|
|
||||||
|
`npm run typecheck` и `npm run lint`; behavior проверяй по
|
||||||
|
[Testing](frontend-testing.md), placement — по [FSD](frontend-architecture.md).
|
||||||
@@ -1,36 +1,26 @@
|
|||||||
# Frontend
|
# Frontend router
|
||||||
|
|
||||||
React/Vite-клиент; общие правила работы — в root `AGENTS.md`.
|
Общие инструкции — [workspace AGENTS](../AGENTS.md). Объединяй triggers задачи,
|
||||||
До frontend-решения прочитай [router](.ai/CONVENTIONS.md) и
|
путей и обнаруженных boundaries; читай только нужные темы до решения.
|
||||||
[sources](.ai/SOURCES.md), затем только применимые документы/разделы.
|
[Sources](.ai/SOURCES.md) выбирает факты/spec/ADR по возникшему вопросу.
|
||||||
|
|
||||||
## Проверка
|
| Решение / сигнал | Читать |
|
||||||
|
| --- | --- |
|
||||||
|
| Первое знакомство, data flow, общая архитектура | [Mental model](.ai/architecture.md) |
|
||||||
|
| Создать/перенести component/module/slice, owner/FSD, cross-slice imports | [FSD](.ai/conventions/frontend-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/frontend-ui.md) |
|
||||||
|
| Только продуктовая строка / Zod message | [Строки](.ai/conventions/frontend-ui.md#строки-и-валидация) |
|
||||||
|
| Query/mutation, server cache, shared/api, generated types | [Data](.ai/conventions/frontend-data.md) |
|
||||||
|
| API/network/application errors, codes и сообщения | [Errors](.ai/conventions/frontend-data.md#errors) |
|
||||||
|
| Login/refresh/logout, token/cookie, session ownership | Data + [Security](../backend/.ai/conventions/security-auth.md) + [Testing](.ai/conventions/frontend-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/frontend-testing.md) |
|
||||||
|
| Storybook/stories/play, theme/tokens/shared UI | [Storybook](.ai/conventions/storybook.md), UI; design facts — через Sources |
|
||||||
|
| 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`.
|
Показ query state не означает изменение fetching. Изменение тестов активирует
|
||||||
После lockfile install `npm ci` полный gate — `npm run verify`.
|
Testing и без production diff. Повтори routing по итоговому diff, включая новые
|
||||||
Он проверяет OpenAPI drift, TypeScript, Biome, Vitest/coverage, Knip,
|
файлы. Migration backlog читается только при работе над соответствующим finding
|
||||||
dependency boundaries и сборки приложения/Storybook без backend.
|
через [plan](../.ai/migrations/conventions-v1/plan.md).
|
||||||
Точные scripts принадлежат `package.json`; targeted checks — у выбранного convention.
|
|
||||||
|
|
||||||
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 и его состояния определяет
|
|
||||||
задача, а визуальное соответствие проверяется отдельно от сборки.
|
|
||||||
|
|
||||||
## Ветки и релизы
|
|
||||||
|
|
||||||
Работа — в отдельной ветке от `develop`, затем PR в `develop`; в `main` напрямую
|
|
||||||
не пушить. Release/hotfix и синхронизация веток — по [root README](../README.md#релизы-и-ветки).
|
|
||||||
Commits и заголовки PR — Conventional Commits; для спецификаций допустим `spec:`.
|
|
||||||
Перед подготовкой релиза прочитай root README; версии вручную не повышать.
|
|
||||||
|
|||||||
Reference in New Issue
Block a user