refactor: enforce frontend view model boundaries
Frontend verify / commits (pull_request) Successful in 28s
Frontend verify / security (pull_request) Successful in 10s
Frontend verify / verify (pull_request) Successful in 8m19s

This commit is contained in:
2026-09-28 18:48:23 +03:00
parent 5b69003516
commit ca0ad4f824
287 changed files with 8558 additions and 4538 deletions
+29 -11
View File
@@ -11,13 +11,11 @@ Mantine component и общую тему. Для выпадающего выбо
ограничения JSDOM решай в test setup, сохраняя production UI.
Семантический HTML для структуры страницы остаётся уместным.
Icons ищи у существующего owner (например,
[BankOperationIcon](../../src/entities/bank-card/ui/BankOperationIcon/BankOperationIcon.tsx));
статичные Add/Edit/Delete доступны в `@wealthledger/icons`. Их SVG декоративны
по умолчанию; имя action принадлежит кнопке. Standalone meaningful icon получает
`aria-hidden={false}`, `role="img"` и доступное имя. Category/kind mappings
и анимации остаются в entity. Не добавляй пакет ради одного символа. Business/domain
components живут у feature/entity/page, даже при повторном использовании.
Icons импортируй из `@wealthledger/icons`; геометрия, SVG assets и animation
lifecycle принадлежат этому пакету. Domain/category lookup возвращает параметры
иконки из model сущности. SVG декоративны по умолчанию; доступное имя action
принадлежит кнопке. Meaningful icon получает `aria-hidden={false}`, `role="img"`
и доступное имя. Приложение и Storybook подключают CSS пакета.
Wrapper нужен для проектной ответственности, а не ради нового имени
для Button. Локальный CSS module принадлежит component; общий token вводи для
@@ -51,10 +49,26 @@ retry callbacks, domain grouping и derived presentation data. View получа
| `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 нарушает границу.
View получает готовые данные и именованные действия. Container вызывает VM;
VM владеет запросами, формой, состоянием, обработчиками и derived values.
Во view нет data/auth/router/navigation hooks и domain grouping. Data VM
вызывает container; presentation hook не должен скрывать fetching во view.
Presentation VM для form-context/UI-only state может использоваться автономно
в каталоге: её граф зависимостей не достигает application data/API.
`useTranslation` и декларативный вывод коллекции допустимы во view.
JSX props получают ссылки на функции, включая `onClick`, render props,
`dateParser` и callbacks внутри объектов props. Для React Hook Form предпочитай
`useController` в VM вместо inline `Controller.render`. Значение prop с условием
сначала получает предметное имя: `projectedAmount`, `archiveActionLabel`.
Проверяй narrowing при переносе: выражение не должно читать отсутствующие данные
до ветки, которая раньше защищала доступ; обработчик сохраняет актуальный snapshot.
Внутри компонента нет именованных helpers. Чистые функции форматирования и
адаптации UI inputs выноси в `shared/lib`, передавая явные аргументы. Domain
преобразование остаётся в model своего owner; shared не импортирует entities/pages.
Closure над состоянием, mutations и refs является действием VM, а не shared helper.
Render-helper замени самостоятельным presentation component у того же owner.
Отдельный state/section с самостоятельной responsibility выноси в
`components/` своего component. Небольшой tightly-coupled renderer оставляй
@@ -87,6 +101,10 @@ image fallback) и formatter одного переданного значени
## Формы
Схемы Zod объявляй в `*.schema.ts`, включая `.refine`/`.superRefine`.
Зависимая от текущих данных схема — фабрика с явными inputs в том же файле;
VM вызывает фабрику. Сохраняй актуальность данных и deferred validation messages.
Используй `react-hook-form`, `zodResolver`, `FormProvider`. Container/VM создаёт
form state, view получает `onSubmit`/`disabled`, fields используют `useFormContext`.
Во время submit блокируй все действия формы. Zod errors показывай у поля;