Reuse account sections and group securities with per-account holdings. Use Mantine Select to preserve filters and reset pagination on scope changes. Document component reuse guardrails and cover routes, grouping and scope switching.
108 lines
7.8 KiB
Markdown
108 lines
7.8 KiB
Markdown
# Frontend UI
|
||
|
||
## Выбор компонента и state
|
||
|
||
Сначала переиспользуй подходящий component проекта; иначе используй готовый
|
||
Mantine component и общую тему. Для выпадающего выбора используй `Select` или
|
||
`MultiSelect`, а не браузерный `<select>` / `NativeSelect` по умолчанию.
|
||
Нативный control или собственная реализация допустимы, когда готовые components
|
||
не покрывают конкретное продуктовое требование или доступность; укажи причину
|
||
в результате работы. Краткость кода и удобство тестирования не являются причиной:
|
||
ограничения JSDOM решай в test setup, сохраняя production UI.
|
||
Семантический HTML для структуры страницы остаётся уместным.
|
||
|
||
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.
|