Files
frontend/.ai/conventions/frontend-ui.md
T
ksv741 daf1dca666 feat: add all-accounts portfolio pages
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.
2026-09-09 20:36:49 +03:00

7.8 KiB
Raw Blame History

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.

Состояние Владелец
Ответ сервера / query lifecycle TanStack Query, data rules
Ввод и ошибки формы 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 и его VM.

Формы

Используй 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. UI: keyboard, narrow viewport, loading/error/empty/loaded и recovery по применимости. Storybook уже установлен; используй его как reference, но сверяй UI с задачей отдельно от build.