Фронтенд
Веб-приложение frontend/ — Next.js 16 (App Router) + React 19 + TypeScript + Tailwind CSS v4 +
shadcn/ui + TanStack Query, менеджер пакетов npm. Работает на app.optimawb.ru и ходит только в
API api.optimawb.ru. Решения — ADR-0008 (стек) и
ADR-0012 (app shell и навигация); публичный лендинг —
отдельное приложение landing/, а не часть frontend/.
Маршруты (App Router)
frontend/src/app/
(marketing)/ редирект `/` на лендинг optimawb.ru (NEXT_PUBLIC_LANDING_URL)
(auth)/ /login, /register
(app)/app/ аутентифицированная оболочка (AppShell)
settings/ кабинеты WB, профиль — не привязаны к кабинету
admin/ пользователи, доступы к брендам, аудит — только admin
wb/[accountId]/ дашборд и модули ТЗ 5.x — привязаны к кабинету
Ключевое решение ADR-0012 — кабинет в URL: /app/wb/:accountId/<модуль>. Ссылку можно
отправить коллеге, обновление страницы не теряет контекст, а уведомления и диплинки
(Уведомления) ведут сразу на нужный экран кабинета. Фильтр по
бренду — глобальный, в шапке (BrandScopeProvider).
Маршруты в app/ тонкие: страница модуля только выбирает компонент из features/ через
module-page-router.
App shell и реестр навигации
components/shell/—AppShell, боковое меню, шапка,PageHeader;features/shell/— провайдеры оболочки, командная палитра, гейты (AdminGate— страницы администратора).- Navigation registry —
src/config/navigation.ts— единственный источник бокового меню. Элемент описываетid, подпись (из локалиru), иконку, группу (overview,sales,pricing,advertising,communications,operations,ai,other,settings,admin), статус (live | soon | hidden), признакaccountScoped, функциюhref(ctx)и допустимые роли. Меню и командная палитра строятся из него; элементы для недоступной роли не показываются. - Новый модуль ТЗ = запись в реестре + папка
features/<slug>/+ тонкий route.
Структура features/
features/<slug>/
index.ts публичный API (страница + типы)
model/ типы, query keys, константы (KPI, подписи)
adapters/ DTO бэкенда → модели UI, параметры запросов
api/ функции запросов и TanStack Query
components/ страница, секции, таблицы, формы
hooks/ use-*-queries.ts (связки useQuery/useMutation)
lib/ форматтеры, экспорт
Правила: фичи не импортируют друг друга (общее — через features/shared и composition root
features/modules); бизнес-UI живёт в features/, а не в app/. Страницы модулей используют общий
каркас AccountModulePageShell (шапка «название · модуль · кабинет · статус», тулбар, алерт для
неактивного кабинета, KPI-сетка, кнопка «Обновить»). Эталоны — digitization, dashboard, prices,
repricer, autobidder, auto-replies. Фичи соответствуют модулям бэкенда: rnp, ai-agent,
content-generation, knowledge-base, notifications и др. (Обзор архитектуры).
Данные: TanStack Query и типизированный клиент
- TanStack Query — серверное состояние. Глобальные настройки (
components/providers.tsx):staleTime60 с, один повтор запроса; ошибки запросов и мутаций показываются тостом (если не отключено вmeta), а успех мутации — сообщением изmeta.successMessage; 401 тостом не дублируется. Фоновые данные обновляются опросом (например, счётчик уведомлений — раз в 60 с). - Клиент OpenAPI —
src/lib/api:openapi-fetch+schema.d.ts, сгенерированный изbackend/openapi.jsonкомандойnpm run api:generate. Любое изменение контракта бэкенда видно компилятором (npm run typecheck). getApiClient()добавляетAuthorization: Bearer, при401один раз обновляет токены и повторяет запрос, ошибки приводит кApiErrorс кодом и сообщением бэкенда.
Аутентификация и сессия
src/proxy.ts(Next.js proxy, бывший middleware) защищает/app/*,/login,/register: по наличию cookie-флагаoptima_sessionперенаправляет неавторизованных на вход. Cookie — только флаг, самих токенов в нём нет.- Access-токен — в памяти вкладки, refresh — в
localStorage(общий источник истины между вкладками: другая вкладка могла уже ротировать токен). Обновление —POST /auth/refresh; при обнаружении повторного использования токена бэкенд завершает всю сессию (Безопасность). AuthGateна клиенте дополнительно проверяет сессию для/app/*; страницы администратора закрытыAdminGateпо роли. Серверная проверка прав — всегда на бэкенде, интерфейс только скрывает недоступное.
Окружение и сборка
| Переменная | Назначение |
|---|---|
NEXT_PUBLIC_API_URL | адрес бэкенда (dev: http://localhost:3000) |
NEXT_PUBLIC_LANDING_URL | адрес лендинга для редиректа с / (dev: http://localhost:3002) |
Production-сборка — next build в режиме output: "standalone", образ собирает frontend/Dockerfile
и публикует GitHub Actions в GHCR optima-frontend (Развёртывание, CI/CD).
Локальный запуск — Локальная разработка. Gate 1 для фронтенда:
lint + typecheck + unit (Vitest) + build + e2e (Playwright). Подписи интерфейса — в src/locales/ru.