Files
frontend/docs/UI-PATTERN.md
T
ksv741 c97f62bd98 refactor: align frontend ownership boundaries
Close audit gaps across API drift, query policy, UI, and tests.
2026-09-06 18:20:05 +03:00

4.2 KiB
Raw Blame History

UI Pattern

Формы используют react-hook-form, zodResolver и FormProvider. Form state создаётся в контейнере или VM, чистое представление получает onSubmit и disabled, а поля подключает через useFormContext.

Во время submit все действия формы блокируются. Ошибки Zod отображаются у поля; ошибки сети сохраняют введённые значения и показываются отдельным Alert.

Запросные экраны без успешных данных выбирают состояние в порядке loading, error, empty, loaded. Если query уже содержит данные, они остаются видимыми при фоновом refetch и его ошибке: рядом показываются неблокирующий progress и локальное предупреждение с retry. Такое состояние называется Client Cached Snapshot; оно не заменяет доменное server-side stale/fallback состояние.

Initial loading без доступного результата использует page-owned structural skeleton, повторяющий semantic regions экрана (heading, cards, filters, summaries или rows), а не StateView или отдельный spinner. Partial loading принадлежит section view и занимает только ещё не загруженный блок. Skeleton region имеет доступное busy-обозначение, но его декоративные shapes не содержат данных, не интерактивны и не получают фокус. Route fallback сохраняет доступный application shell и skeleton-изирует content area; до auth ready public и protected guards показывают neutral shell skeleton. Button loading для mutation и compact progress при Client Cached Snapshot остаются контекстными indicators и не заменяются skeleton.

Для finance-экранов используется граница container -> ViewModel -> view. *.tsx без суффикса — container: он вызывает единственный page VM hook и выбирает top-level Loading/Error/Empty/Loaded view. *.vm.ts владеет data fetching, auth/route inputs, state selection, retry callbacks, domain grouping и derived presentation data. *.view.tsx и соседние presentation components получают props и рендерят JSX.

View modules не вызывают data/auth/router/navigation/VM hooks: никаких useBrokerage*, usePortfolio, useAuth, useParams, useNavigate или page VM hooks внутри *.view.tsx и components/*.view.tsx. View modules также не группируют domain collections и не считают derived collection values вроде долей Portfolio Position. Эти значения готовит VM.

Допустимые hooks во view: useTranslation() для labels, form-context hooks в form views и локальный UI-only state для rendering fallback, например failed image state. Простое форматирование одного переданного значения через существующий formatter остаётся presentation concern; вычисления из всей коллекции остаются во VM.

View files дробятся по ownership и смысловой нагрузке, а не по количеству строк. Самостоятельные top-level states и sections выносятся в flat components/*.view.tsx, если это делает principal component и границы изменений яснее. Маленькие tightly-coupled renderers остаются рядом с parent; механическое правило «один component — один file» и line-count gate не используются.

Known API errors, retry и stale snapshot остаются local views, а непойманные route errors обрабатывает общий route-keyed ErrorBoundary.