docs(ai): route frontend conventions by task

This commit is contained in:
2026-09-09 16:22:47 +03:00
parent 26f229ba3e
commit bcc034f198
14 changed files with 333 additions and 210 deletions
+99
View File
@@ -0,0 +1,99 @@
# Frontend UI
## Выбор компонента и state
Используй существующий Mantine component и общую тему, если они выражают нужное
поведение. Wrapper нужен для проектной ответственности, а не ради нового имени
для Button. Локальный CSS module принадлежит component; общий token вводи для
реально общей роли через [design system](../../../docs/development/design-system.md).
| Состояние | Владелец |
| --- | --- |
| Ответ сервера / query lifecycle | TanStack Query, [data rules](frontend-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.
## Container, VM и view
Для data-driven finance page container вызывает один page VM и выбирает
Loading/Error/Empty/Loaded view. VM владеет data/auth/route inputs, state selection,
retry callbacks, domain grouping и derived presentation data. View получает props.
| Файл | Ответственность |
| --- | --- |
| `Component.tsx` | Container или публичный component |
| `Component.vm.ts` | Page VM, данные и действия |
| `Component.view.tsx` | Presentation |
| `Component.types.ts` | Переиспользуемые props/local types, когда отдельный owner полезен |
| `Component.module.css` | Локальные стили |
Во view нет data/auth/router/navigation/VM hooks и вычислений по domain collection.
Допустимы `useTranslation`, form-context hooks, UI-only state (например failed
image fallback) и formatter одного переданного значения. Группировка/доли
коллекции готовятся в VM. Перенос fetch во view ради меньшего container нарушает границу.
Отдельный state/section с самостоятельной responsibility выноси в flat
`components/*.view.tsx`. Небольшой tightly-coupled renderer оставляй рядом.
Не создавай folder/barrel/test/CSS для каждого view и не дроби по line count.
Пример: [AccountIncomePage](../../src/pages/account-income/ui/AccountIncomePage.tsx)
и [его VM](../../src/pages/account-income/ui/AccountIncomePage.vm.ts).
## Формы
Используй `react-hook-form`, `zodResolver`, `FormProvider`. Container/VM создаёт
form state, view получает `onSubmit`/`disabled`, fields используют `useFormContext`.
Во время submit блокируй все действия формы. Zod errors показывай у поля;
network error — отдельным Alert с сохранением ввода. Невалидная форма не отправляет request.
## Состояния и recovery
Без успешных данных выбирай loading → error → empty → loaded. При наличии query
данных сохраняй их во время refetch и его ошибки: progress, локальное предупреждение,
retry. Это Client Cached Snapshot, отдельный от server stale/fallback.
Known API errors принадлежат local view; непойманные route errors — route-keyed
ErrorBoundary. Сброс query cache после смены session/connection задают data rules.
Initial loading использует page-owned structural skeleton по semantic regions,
не одиночный spinner/StateView. Partial loading принадлежит section. Busy-область
доступна assistive technology, shapes декоративны, без данных, интерактивности
и focus. Route fallback сохраняет shell; до auth ready — neutral shell skeleton.
Button loading при mutation и компактный cached-snapshot progress остаются
контекстными indicators. Проверяй keyboard access к actions и безопасный retry.
## Код и components
Style/type automation задают configs. Используй `import type`; `enum` и
`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.
## Строки и валидация
Продуктовый текст хранится в `src/shared/i18n/locales/ru.ts`, читается через
`useTranslation()`. В Zod используй `validationMessage(key)` из i18n/zod, без
пользовательских literal messages. Это mandatory semantic review: общего запрета
непереведённых JSX-строк в Biome/TypeScript нет. Synthetic story fixtures и
описания историй допустимы; они не заменяют продуктовую локализацию.
## Проверка
Type/style: `npm run typecheck`, `npm run lint`. Для risk behavior/изменения тестов
используй [Testing](frontend-testing.md). UI: keyboard, narrow viewport,
loading/error/empty/loaded и recovery по применимости. [Storybook](../../../docs/development/ui-catalog.md)
уже установлен; используй его как reference, но сверяй UI с задачей отдельно от build.