Дата: 2026-05-04
Цель: закрыть разрыв между текущим кодом и two-storefronts-architecture.md: отчётность, UX регистрации, явный бренд заказа, выгрузки, контент/юридика, визуал.
Инварианты: один бэкенд; смешанной корзины нет; ЮKassa — разные shopId по брендам (уже поддерживается настройками).
- У каждого заказа в БД есть явный
brand(inner|sprint-power), выставляется при создании и используется в админке/экспортах без эвристики «по строкам». - При регистрации с уже существующим email — понятный сценарий: сообщение про сеть магазинов + ссылка на
/login(копирайт согласовать; опционально — ключ в content blocks). - В админке есть выгрузка с обоих брендов там, где нужна сводная отчётность (минимум: лиды; при необходимости — заказы/статистика — отдельное решение в рамках фазы 3).
- Юридические и ключевые статические страницы либо редактируются по бренду из админки, либо зафиксировано исключение (оставляем static + чеклист ручной синхронизации).
- В ЛК в списке заказов видно с какой витрины заказ (колонка/бейдж).
- Документация: STATUS.md, чеклист в two-storefronts-architecture.md обновлены после внедрения.
Задачи
- Зафиксировать список экранов и API, где бренд выводится из состава заказа (
order.service.ts, CRM, webhooks, письма). - Проверить создание платежа ЮKassa и webhook: везде ли передаётся/определяется
brandIdдля выбора credentials (уже частично есть — не сломать при добавленииOrder.brand).
Результат: короткая таблица «файл → сегодняшняя логика»; нет кода (или только комментарии в задаче).
Срез зафиксирован: order-brand-phase-0-snapshot.md.
Зачем: отчёты, экспорт, интеграции, защита от будущих изменений в составе.
Задачи
- Prisma: добавить в
Orderполеbrand(String, значения как уBrandId), индекс(brand, createdAt)при необходимости. - Миграция данных: для существующих заказов выставить
brandпо правилу, совпадающему с текущей эвристикой (есть позиция сproduct.brand === 'sprint-power'→sprint-power, иначеinner). Обработать пустые/аномальные случаи вручную или скриптом. - Создание заказа: при
POST /api/orders(и любых других путях создания) записыватьbrandиз контекста витрины / валидации корзины (все позиции одного бренда — уже должно выполняться). - Чтение:
getOrdersForAdmin, корзина удалённых, синк ЮKassa, выгрузки — фильтровать поorder.brand, а не только поitems. - Тесты: unit/integration на создание заказа и миграцию (где уместно).
Результат: одно поле истины; старые запросы по items удалены или оставлены только как fallback на период релиза (лучше без fallback после бэкфила).
Ориентир по файлам: nextjs-project/prisma/schema.prisma, nextjs-project/src/app/api/orders/route.ts, nextjs-project/src/services/order.service.ts, миграции в nextjs-project/prisma/migrations/.
Задачи
- API
POST /api/auth/register: при конфликте email возвращать структурированный ответ (напримерcode: 'EMAIL_ALREADY_REGISTERED') + человекочитаемое сообщение на русском (или i18n-ключ). - UI
/register: при этом коде показывать блок с текстом из ТЗ + кнопка «Войти» (/login), опционально сохранить email в query для/login?email=. - Тесты: обновить
route.test.tsдля регистрации.
Результат: поведение совпадает с разделом 2 архитектурного документа.
Файлы: src/app/api/auth/register/route.ts, src/app/register/page.tsx, тесты рядом.
Статус: реализовано (константы и текст — src/lib/auth/email-already-registered.ts, предзаполнение email на /login).
Задачи
- Лиды: в UI выгрузки лидов и в
GETAPI — параметр или чекбокс «все бренды»; сервисgetAllLeadsForExportпринимает режимall|inner|sprint-power(илиbrandId | nullгдеnull= все). - Заказы (опционально в этой же итерации): если CRM/статистика экспортирует CSV — добавить тот же паттерн; иначе завести подзадачу.
- Права: только роли с доступом к админке (как сейчас); при росте команды можно ограничить отдельной ролью позже.
Результат: отчётность на одном компьютере без двух ручных выгрузок (минимум — лиды).
Файлы: src/services/leads-export.service.ts, src/app/api/admin/leads/export/route.ts, src/app/admin/leads-export/page.tsx.
Статус: реализовано — allBrands=1|on, LeadsExportBrandScope, колонка «Витрина», имя файла leads-all-brands-*.csv при объединении.
Задачи
- В DTO/API списка заказов для аккаунта отдавать
brand(изOrder.brandпосле фазы 1). - В
AccountOrdersTable(или аналоге) — колонка или бейдж «Inner Health» / «Sprint Power».
Результат: пользователь видит, с какой витрины покупка, без открытия деталей.
Статус: реализовано — Order.brand в данных списка/детали; AccountOrdersTable: колонка «Витрина» (desktop) и бейдж в карточке (mobile); /account/orders/[id] — бейдж в шапке; account-order-storefront-badge.tsx.
Задачи
- Выбрать механику: content blocks по ключам страниц (
privacy,oferta, …) сbrand, либо отдельная сущность «LegalPage» — проще расширять таблицей ключей. - Миграция: опционально засидить текущий HTML/текст из кода как
brand_defaultдляinner, дляsprint-power— черновики или копии. - Storefront: страницы читают контент из БД с fallback на static только если нет записи (короткий переходный период).
- Обновить storefront-copy-ownership.md — строка Legal →
content blocks/ выбранная модель.
Результат: юристы/маркетинг правят тексты без релиза кода.
Статус: реализовано для /privacy и /oferta — страницы legal-privacy / legal-oferta в контент-блоках, LegalPageRichOrStatic + fallback на текущий статический JSX; storefront-copy-ownership.md обновлён.
Задачи
- Тема: свести цвета/лого/фавикон к токенам (
layout, CSS variables), убрать расхождения между страницами. - Внешние ссылки: решение продукта — оставить переход на
sprintpower.ruв блоках или вести всё на свою витрину; после решения — один PR на ссылки/копирайт. - Адаптив: по adaptive-pages-inventory.md —
SprintPowerBlockи приоритетные страницы Sprint (если отстают).
Результат: визуально цельная вторая витрина на уровне «перекрас + консистентность».
Статус: реализовано — data-brand на <html>, акцентные CSS-переменные для Sprint, фон body и превью Open Graph/Twitter по бренду; иконки в метаданных сайта — файлы из public/; блок Sprint на главной ведёт на /catalog (своя витрина); SprintPowerBlock на FluidGrid; инвентаризация адаптива обновлена.
Задачи
- Документировать в
docs/env's.md/ DEPLOY: как включить второй набор CDEK-ключей в настройках бренда (уже поддерживается схемой ключей — проверить все call sites используютbrandId). - Smoke: оформление заказа Sprint с теми же глобальными учётками СДЭК; при появлении второго договора — только смена значений в админке.
Результат: готовность к разводке без переделки архитектуры.
Статус: ключи cdek_* внесены в brand-scoped список; админка с выбранным брендом сохраняет brand:cdek_*, runtime читает scoped → глобальный fallback; описано в env's.md и two-storefronts-architecture.md §7. Smoke оформления Sprint — на стенде вручную.
Фаза 0 (срез) → Фаза 1 (Order.brand) → Фаза 2 (регистрация) → Фаза 3 (экспорт)
→ Фаза 4 (ЛК бейдж) → Фаза 5 (юридика) → Фаза 6 (визуал/ссылки) → Фаза 7 (СДЭК-док)
Фазы 2 и 3 можно параллелить разными исполнителями после фазы 1, если фаза 1 не блокирует экспорт (экспорт заказов по order.brand логично делать сразу после фазы 1).
| Риск | Митигация |
|---|---|
Бэкфил Order.brand расходится с реальностью |
Скрипт отчёта «аномалии» до применения миграции; ручная правка единиц. |
| Webhook ЮKassa без brand | Явно искать заказ по yookassaPaymentId и дальше использовать order.brand для credentials. |
| Дубли SEO | Редакционная политика + разный контент в блоках/постах по brand (не задача одного PR). |
- Обновить two-storefronts-architecture.md — таблицу «расхождения с кодом».
- Обновить STATUS.md — дата и краткий список «сделано по второму бренду».