# Change Log для frontend-команды

## Паспорт документа

- Статус документа: living document
- Актуально на: 2 апреля 2026 года
- Владелец: backend/platform-команда, совместно с frontend-командой
- Пересмотр: при изменении backend/frontend handoff-процесса, ownership-модели или API change workflow
- Область применения: операционная модель взаимодействия backend/platform-команды и отдельной frontend-команды
- Связанные документы:
  - [Карта API-маршрутов](../architecture/api-routes.md)
  - [Текущее состояние](../project/current-state.md)
  - [Инженерные принципы](../governance/engineering-principles.md)

## Цель документа

Этот документ фиксирует change packages, которые backend/platform-команда передаёт отдельной frontend-команде. Его задача не заменить OpenAPI и не дублировать всю документацию, а дать короткий операционный слой:

- что уже готово на backend;
- какие endpoint-группы можно брать в работу;
- какие изменения важны для UI и integration layer;
- какие проверки нужны со стороны frontend после внедрения.

## Источники истины

- Канонический backend handoff: [frontend-handoff.md](./frontend-handoff.md)
- Актуальный OpenAPI runtime: `https://qadam.2fab.app/api/openapi.json`
- Swagger UI: `https://qadam.2fab.app/api/docs`
- Человекочитаемая карта API: [api-routes.md](../architecture/api-routes.md)
- Текущее состояние production: [current-state.md](../project/current-state.md)
- Статус платформенной реализации: [../../specs/qadam-platform/implementation.md](../../specs/qadam-platform/implementation.md)

## Правила публикации change package

Каждый новый backend change package, который влияет на frontend, должен обновлять этот документ.

Минимальный состав записи:

1. `Package ID`
2. `Статус`
3. `Что изменилось на backend`
4. `Какие endpoint'ы / контракты затронуты`
5. `Что должна сделать frontend-команда`
6. `Что считать готовностью`
7. `Есть ли breaking / migration note`
8. `Требуется ли действие frontend` на roadmap-портале

## Статусы пакетов

- `ready_for_frontend` — backend-контур готов, можно брать в работу на frontend
- `in_progress` — backend ещё меняется, фронту брать только после отдельного подтверждения
- `adopted_by_frontend` — пакет уже внедрён frontend-командой
- `superseded` — пакет исторический и заменён более новым change package

## Обратная связь от frontend на roadmap-портале

Главная страница `https://qadam-roadmap.2fab.app` автоматически собирает отдельный блок `Что сейчас должна сделать frontend-команда` из этого документа.

Правила такие:

- в блок попадают только пакеты со статусом `ready_for_frontend`;
- если пакет требует действий от frontend, на портале показываются кнопки `В работе`, `Сделано`, `Игнорировать`;
- если запись носит только информационный характер, в ней нужно явно указать строку `- Требуется действие frontend: нет`, и тогда кнопки подтверждения не показываются;
- если строка не указана, пакет по умолчанию считается требующим действия, когда у него есть статус `ready_for_frontend` и непустой блок `Что должна сделать frontend-команда`;
- feedback-статусы на портале являются operational overlay и не заменяют канонический changelog: backend-пакет остаётся источником истины, а портал только фиксирует факт реакции frontend-команды.

Допустимые значения обратной связи:

- `В работе` — frontend-пакет взят в реализацию;
- `Сделано` — frontend-команда подтверждает внедрение;
- `Игнорировать` — пакет сознательно не берётся в работу сейчас и не должен висеть как немая задача.

## Пакеты изменений

## FE-BE-2026-04-06-01 — Refresh cookie `qadam_rt` переведена на root path для server-side refresh

- Статус: `ready_for_frontend`
- Scope: web auth stability, full page reload after access token expiry, proxy refresh behavior

### Backend scope

- `POST /api/v1/auth/login`
- `POST /api/v1/auth/register`
- `POST /api/v1/auth/register/buyer`
- `POST /api/v1/auth/register/seller`
- `POST /api/v1/auth/refresh`
- `POST /api/v1/auth/reset-password`
- `POST /api/v1/auth/logout`

### Важные контрактные детали

- `qadam_rt` теперь выставляется с `path=/`, а не только с `path=/api/v1/auth`
- это сделано не ради нового security behavior, а чтобы server-side proxy на маршрутах вроде `/seller` и `/me` мог читать refresh cookie после истечения `qadam_at`
- backend при выдаче новой пары токенов дополнительно чистит legacy `qadam_rt` с `path=/api/v1/auth`, чтобы не оставлять два refresh-cookie с одним именем и разными path
- logout/clear tokens теперь тоже чистит обе версии refresh-cookie: legacy и новую root-scoped

### Что должна сделать frontend-команда

- Никакой новой интеграции не требуется
- Перестать считать logout after reload через ~15 минут “известным ограничением backend”
- При проверке stage/prod сценариев отдельно подтвердить: full page reload после истечения access token больше не должен выбрасывать пользователя на `/login`, если refresh token ещё жив

### Acceptance для frontend

- Web session переживает полную перезагрузку страницы после истечения `qadam_at`, если `qadam_rt` ещё валидна
- Proxy refresh не ломается только из-за cookie path mismatch

### Breaking / migration note

- Для уже существующих браузерных сессий, выпущенных до фикса, может понадобиться один новый login, потому что legacy refresh-cookie с `path=/api/v1/auth` уже была выдана ранее
- Требуется действие frontend: нет

## FE-BE-2026-04-02-05 — Owner-facing status-change notifications включены для seller staff flow

- Статус: `ready_for_frontend`
- Scope: seller notification settings copy, seller/staff leads UX, operational expectations around owner alerts

### Backend scope

- `GET /api/v1/seller/leads`
- `PUT /api/v1/seller/leads/:id/status`
- `GET /api/v1/seller/notification-settings`
- `PATCH /api/v1/seller/notification-settings`

### Важные контрактные детали

- Owner-facing status-change notifications больше не являются purely forward-compatible: если статус лида меняет активный `SELLER_STAFF`, backend отправляет owner-уведомление по каналам `Telegram -> Email fallback`
- Список статусов для таких уведомлений не захардкожен во frontend и может меняться через runtime env `SELLER_STATUS_CHANGE_NOTIFY_STATUSES`; дефолтный набор на backend сейчас `CONTACTED`, `ENROLLED`, `REJECTED`
- Если статус меняет сам owner (`SELLER`), backend не шлёт ему уведомление о его же собственном действии
- Публичный HTTP-контракт notification settings не менялся: новых полей и новых endpoint'ов нет

### Что должна сделать frontend-команда

- Не считать `notifyStatusChangeTelegram` purely informational toggle: на окружениях с включённым seller notifications runtime он уже управляет live owner-facing уведомлениями для staff-driven status changes
- Если в seller/staff UX есть текст, будто status-change alerts "появятся позже", обновить его под фактическое поведение backend

### Acceptance для frontend

- Seller settings UI не вводит пользователя в заблуждение относительно live status-change alerts
- Seller/staff lead UX не обещает owner-уведомления для self-update сценария, которого backend сознательно не делает

### Breaking / migration note

- Это изменение поведения доставки без расширения публичного API-контракта
- Требуется действие frontend: нет

## FE-BE-2026-04-02-04 — Seller email fallback подготовлен на backend

- Статус: `ready_for_frontend`
- Scope: seller notification settings copy, operational expectations around email channel

### Backend scope

- `POST /api/v1/leads`
- `GET /api/v1/seller/notification-settings`
- `PATCH /api/v1/seller/notification-settings`

### Важные контрактные детали

- Для новых лидов backend теперь не ограничивается Telegram-only попыткой доставки: при недоступном Telegram и включённом `notifyNewLeadEmail` он умеет перейти на SMTP-capable email fallback
- `sellerEmail` в `GET /seller/notification-settings` теперь резолвится из seller profile email с fallback на account email
- Публичный HTTP-контракт не менялся: новых endpoint'ов и новых полей нет
- Email fallback реально срабатывает только на тех окружениях, где backend/platform-команда настроила `SMTP_*` runtime env
- `notifyStatusChangeTelegram` теперь уже управляет live owner-facing уведомлениями в non-owner staff flow; отдельный пакет ниже фиксирует это как поведенческое изменение без расширения API-контракта

### Что должна сделать frontend-команда

- Не считать `notifyNewLeadEmail` purely forward-compatible флагом на окружениях, где backend подтвердил SMTP runtime
- Если в UI есть жёсткий текст "email ещё не работает", убрать его только после подтверждения, что конкретный target runtime уже получил `SMTP_*` конфиг
- Показывать `sellerEmail` как фактический email-адрес доставки, а не только как account email из auth-контекста

### Acceptance для frontend

- Settings screen не вводит seller в заблуждение относительно email-канала
- UI понимает, что email delivery зависит не только от toggle, но и от runtime readiness конкретного окружения

### Breaking / migration note

- Migration касается поведения доставки, а не формы API-ответа
- Требуется действие frontend: нет

## FE-BE-2026-04-02-03 — Seller Telegram onboarding bot flow подготовлен для frontend

- Статус: `ready_for_frontend`
- Scope: seller notifications UX, Telegram connect/disconnect CTA, onboarding follow-up

### Backend scope

- `GET /api/v1/seller/telegram/connect-link`
- `POST /api/v1/seller/telegram/verify`
- `DELETE /api/v1/seller/telegram`

### Важные контрактные детали

- Backend теперь отдаёт seller-facing deep link:
  - `botUsername: string`
  - `connectUrl: string`
  - `expiresAt: string`
- `connectUrl` уже готов для открытия в Telegram и ведёт в bot `/start` flow; frontend не должен собирать deep link вручную
- `GET /seller/telegram/connect-link` может вернуть `TELEGRAM_NOT_CONFIGURED`, если seller-bot не настроен на конкретном окружении; это operational state, а не generic network error
- `POST /seller/telegram/verify` теперь явно возвращает `ALREADY_VERIFIED`, если Telegram уже подключён к seller
- `DELETE /seller/telegram` теперь явно возвращает `NOT_CONNECTED`, если seller пытается отключить Telegram без текущей привязки
- Internal route `POST /api/v1/internal/telegram/webhook` предназначен для Telegram и не требует frontend-работ

### Что должна сделать frontend-команда

- Добавить CTA/кнопку подключения Telegram через `GET /seller/telegram/connect-link`
- Открывать `connectUrl` как внешний переход в Telegram, а не через локальную строковую сборку bot URL
- На verify/disconnect корректно обрабатывать `ALREADY_VERIFIED` и `NOT_CONNECTED` как продуктовые состояния, а не как generic error
- Синхронизировать seller notification settings screen с новым connect flow, чтобы `telegramConnected` и `telegramUsername` обновлялись после успешной привязки

### Acceptance для frontend

- Seller может запустить Telegram connect flow из кабинета без ручного знания bot username или start token
- UI различает состояния `подключено`, `уже подключено`, `не подключено`
- После успешного bot flow seller возвращается в кабинет и может завершить привязку кодом без локальных manual types

### Breaking / migration note

- Это расширение существующего Telegram flow; старые settings endpoints не ломаются
- Frontend больше не должен считать, что seller сам знает, где взять verification code: source-of-truth теперь `GET /seller/telegram/connect-link`
- Требуется действие frontend: да

## FE-BE-2026-04-02-02 — Seller notifications baseline подготовлен для frontend

- Статус: `ready_for_frontend`
- Scope: seller settings page, lead notification preferences, seller onboarding follow-up

### Backend scope

- `GET /api/v1/seller/notification-settings`
- `PATCH /api/v1/seller/notification-settings`

### Важные контрактные детали

- Backend публикует новый seller-facing settings contract:
  - `notifyNewLeadTelegram: boolean`
  - `notifyNewLeadEmail: boolean`
  - `notifyStatusChangeTelegram: boolean`
  - `telegramConnected: boolean`
  - `telegramUsername: string | null`
  - `sellerEmail: string | null`
- Если запись `NotificationSettings` отсутствует у исторического seller-аккаунта, backend создаёт её автоматически с дефолтами
- После `POST /leads` backend уже запускает неблокирующую попытку Telegram-доставки продавцу, но email transport пока не включён: `notifyNewLeadEmail` уже существует как forward-compatible setting, а не как подтверждённый delivery channel
- Успешность или пропуск доставки не влияет на ответ создания лида: lead create остаётся продуктовым primary path

### Что должна сделать frontend-команда

- Добавить экран или секцию seller notification settings на `GET/PATCH /seller/notification-settings`
- Показывать состояние Telegram binding по `telegramConnected` и `telegramUsername`, а не по локальным эвристикам
- Не обещать пользователю живой email delivery, пока backend отдельно не подтвердит SMTP/provider package
- Использовать `sellerEmail` только как read-only operational hint в настройках, а не как отдельный источник истины о seller profile

### Acceptance для frontend

- Seller видит текущие notification settings и может менять их без ручных локальных типов
- UI корректно различает `Telegram подключён / не подключён`
- Lead settings screen не вводит пользователя в заблуждение относительно email delivery

### Breaking / migration note

- Это новый settings surface; существующие seller profile и lead flows не ломаются
- Email toggle уже появляется в контракте, но не означает, что email-канал backend уже доведён до production delivery
- Требуется действие frontend: да

## FE-BE-2026-04-02-01 — Auth contract получил change-password из кабинета

- Статус: `ready_for_frontend`
- Scope: buyer/seller account settings, password settings form, auth security UX

### Backend scope

- `POST /api/v1/auth/change-password`

### Важные контрактные детали

- Новый endpoint принимает:
  - `currentPassword: string`
  - `newPassword: string`
- `newPassword` использует те же password rules, что и registration/reset-password:
  - минимум 8 символов
  - минимум одна цифра
- Успешный ответ:
  - `{ message: string }`
- При неверном текущем пароле backend возвращает `INVALID_CREDENTIALS`
- После успешной смены пароля backend отзывает refresh token family, поэтому фронт не должен рассчитывать на долгоживущие старые сессии на других устройствах

### Что должна сделать frontend-команда

- Добавить форму смены пароля в buyer/seller кабинете на `POST /auth/change-password`
- Валидировать `newPassword` по тем же правилам, что и registration/reset-password
- Показать отдельное UX-сообщение для неверного текущего пароля вместо generic error toast
- После успешной смены пароля корректно показать success-state и быть готовыми к тому, что старые refresh-сессии на других устройствах больше невалидны

### Acceptance для frontend

- Из кабинета можно сменить пароль без flow `forgot-password`
- Неверный текущий пароль отображается как ожидаемая продуктовая ошибка
- Успешная смена пароля показывает явный success-state по `message` из backend

### Breaking / migration note

- Это новый endpoint, а не замена `forgot-password` / `reset-password`
- Сброс пароля по коду и смена пароля из авторизованного кабинета теперь должны сосуществовать как два разных UX-потока
- Требуется действие frontend: да

## FE-BE-2026-03-31-03 — Public seller profile начал отдавать backend-generated SEO metadata

- Статус: `ready_for_frontend`
- Scope: public seller profile SSR page и metadata layer должны перейти с ad-hoc сборки SEO на backend-generated contract

### Backend scope

- `GET /api/v1/sellers/:id`

### Важные контрактные детали

- Public seller profile response теперь публикует не только витринные поля seller и список опубликованных айтемов, но и explicit `seo` block:
  - `title`
  - `description`
  - `canonicalUrl`
  - `openGraph`
  - `jsonLd`
- Public response очищен от seller-only внутреннего контекста: backend больше не должен отдавать приватный seller profile shape как есть
- Непубличный seller должен резаться как `404`; contract для public page теперь рассчитан только на `ACTIVE` seller profile
- Hidden addresses не должны попадать в public payload даже в обнулённом виде

### Что должна сделать frontend-команда

- Перевести public seller page metadata на backend-generated `seo` block вместо локальной ad-hoc сборки
- Использовать `seo.canonicalUrl`, `seo.openGraph` и `seo.jsonLd` как канонический источник для SSR metadata и structured data
- Не предполагать, что hidden seller addresses приходят в payload: если массив `addresses` пуст, секция не должна рендериться
- Обрабатывать `404` как нормальный SEO-safe сценарий для non-active / missing seller profile

### Acceptance для frontend

- Public seller page использует backend-generated SEO metadata без ручного дублирования формул title/description/canonical
- JSON-LD на seller page строится из `seo.jsonLd`, а не из разрозненных local selectors
- Non-active / missing seller profile даёт корректный 404 page flow без пустого `200`

### Breaking / migration note

- Это не просто cosmetic addition: contract public seller profile стал более строгим и более пригодным для SSR/SEO
- Frontend не должен опираться на скрытые seller-only поля, если они раньше случайно приезжали в response
- Требуется действие frontend: да

## FE-BE-2026-03-31-02 — Seller review surface и complaint-driven moderation готовы для frontend

- Статус: `ready_for_frontend`
- Scope: seller review dashboard, seller reply/complaint UX, buyer/public/admin review screens должны учесть seller reply и complaint-driven moderation

### Backend scope

- `GET /api/v1/seller/reviews`
- `PATCH /api/v1/seller/reviews/:id/reply`
- `POST /api/v1/seller/reviews/:id/complaint`
- `GET /api/v1/me/reviews`
- `GET /api/v1/catalog/items/:slug/reviews`
- `GET /api/v1/admin/moderation/reviews/:id`

### Важные контрактные детали

- `GET /seller/reviews` возвращает список отзывов по айтемам продавца c полями:
  - `reviewId`
  - `status`
  - `rating`
  - `text`
  - `sellerReply`
  - `sellerReplyAt`
  - `moderationNote`
  - `canReply`
  - `canEditReply`
  - `activeComplaint`
  - `item`
  - `buyer`
- `PATCH /seller/reviews/:id/reply` доступен только для `PUBLISHED`-отзывов
- После первого ответа продавца backend разрешает редактировать reply только в пределах 48 часов; затем возвращает `REPLY_EDIT_WINDOW_EXPIRED`
- `POST /seller/reviews/:id/complaint` доступен только для `PUBLISHED`-отзывов и переводит отзыв в `PENDING_MODERATION`
- Повторная жалоба на отзыв с активной нерешённой complaint запрещена и возвращает `COMPLAINT_ALREADY_EXISTS`
- Buyer/public/admin review payloads теперь могут содержать seller reply и active complaint context:
  - `GET /me/reviews` публикует `sellerReply` и `sellerReplyAt`
  - `GET /catalog/items/:slug/reviews` публикует seller reply только для `PUBLISHED`-отзывов
  - `GET /admin/moderation/reviews/:id` публикует `sellerReply`, `sellerReplyAt` и `activeComplaint`

### Что должна сделать frontend-команда

- Построить seller review list/dashboard на `GET /seller/reviews`
- Добавить seller reply editor с state-driven блокировкой по `canReply` / `canEditReply`
- Обработать `REVIEW_NOT_PUBLISHED`, `REPLY_EDIT_WINDOW_EXPIRED`, `COMPLAINT_ALREADY_EXISTS` как ожидаемые продуктовые состояния, а не generic error
- Добавить seller complaint UX и после успешной жалобы переводить review-card в состояние `PENDING_MODERATION`
- Показывать seller reply в buyer cabinet и public review blocks там, где он приходит из backend
- В admin review detail показывать active complaint context, если отзыв попал на повторную модерацию

### Acceptance для frontend

- Seller видит список отзывов по своим айтемам и различает `PUBLISHED / PENDING / REJECTED / PENDING_MODERATION`
- Seller может ответить на опубликованный отзыв и видит, когда reply уже нельзя редактировать
- Seller может отправить complaint на опубликованный отзыв и после этого UI показывает review как `PENDING_MODERATION`
- Buyer/public/admin review screens корректно отображают seller reply и complaint-driven moderation state

### Breaking / migration note

- Это расширение review-контракта и seller dashboard surface, а не изолированный новый экран
- Frontend больше не должен считать seller review interaction "локальной заметкой": reply и complaint влияют на public/admin review state machine
- Требуется действие frontend: да

## FE-BE-2026-03-31-01 — Backend review status model и admin review moderation baseline

- Статус: `ready_for_frontend`
- Scope: buyer reviews cabinet, public item reviews и admin review moderation

### Backend scope

- `POST /api/v1/me/reviews`
- `GET /api/v1/me/reviews`
- `GET /api/v1/catalog/items/:slug/reviews`
- `GET /api/v1/admin/moderation/reviews`
- `GET /api/v1/admin/moderation/reviews/:id`
- `PATCH /api/v1/admin/moderation/reviews/:id`

### Важные контрактные детали

- Review flow на backend больше не предполагает мгновенную публикацию: новые отзывы создаются в `PENDING`
- Исторические существующие отзывы мигрированы в `PUBLISHED`, чтобы не обнулить текущий public/social proof
- `GET /me/reviews` теперь возвращает для каждого отзыва:
  - `reviewId`
  - `itemId`
  - `itemName`
  - `itemSlug`
  - `rating`
  - `text`
  - `status`
  - `moderationNote`
  - `createdAt`
- Публичный `GET /catalog/items/:slug/reviews` и rating/reviews aggregates теперь учитывают только `PUBLISHED`
- Появился новый admin moderation surface для отзывов:
  - `GET /admin/moderation/reviews`
  - `GET /admin/moderation/reviews/:id`
  - `PATCH /admin/moderation/reviews/:id`
- `PATCH /admin/moderation/reviews/:id` принимает:
  - `decision: PUBLISHED | REJECTED`
  - `note?: string`
- При `REJECTED` moderation note обязателен
- `PENDING_MODERATION` уже существует в contract-layer как допустимый `ReviewStatus`, но complaint-driven переход в этот статус будет отдельным follow-up пакетом

### Что должна сделать frontend-команда

- Добавить buyer-facing UI для review status badge вместо скрытого допущения "отзыв всегда сразу опубликован"
- Показывать `moderationNote`, если отзыв отклонён или имеет seller-visible moderation пояснение
- В public item reviews и rating widgets больше не ожидать, что новые только что отправленные отзывы появятся мгновенно
- Построить admin review moderation queue/detail на новых `/admin/moderation/reviews*` endpoints
- Обработать `INVALID_STATUS_TRANSITION` для race-safe admin moderation UI

### Acceptance для frontend

- Buyer cabinet показывает `PENDING / PUBLISHED / REJECTED / PENDING_MODERATION` как отдельные UI-состояния
- Public item reviews и rating counters не завышаются за счёт `PENDING` отзывов
- Admin может открыть очередь review moderation, посмотреть карточку отзыва и перевести отзыв в `PUBLISHED` или `REJECTED`

### Breaking / migration note

- Это контрактное изменение поведения, а не только добавление admin endpoints
- Старое frontend-допущение "отзыв после submit сразу виден в public UI" теперь неверно
- Требуется действие frontend: да

## FE-BE-2026-03-30-05 — Admin seller operational surface доведён до рабочего контура

- Статус: `ready_for_frontend`
- Scope: admin seller directory, status management и seller-facing реакция на account status

### Backend scope

- `GET /api/v1/admin/sellers`
- `PATCH /api/v1/admin/sellers/:sellerId/status`
- seller-facing защищённые маршруты, которые проходят через `JwtAuthGuard`

### Важные контрактные детали

- Появился новый endpoint `GET /admin/sellers` с фильтрами `search`, `status`, `sellerType`, `page`, `limit`
- Ответ `GET /admin/sellers` имеет форму `{ items, total, page, limit, totalPages }`
- Каждый seller summary теперь содержит:
  - `sellerId`
  - `accountId`
  - `sellerType`
  - `accountStatus`
  - `displayName`
  - `phone`
  - `email`
  - `telegramConnected`
  - `totalItems`
  - `pendingItems`
  - `activeItems`
  - `createdAt`
- `PATCH /admin/sellers/:sellerId/status` остаётся прежним по shape ответа, но теперь у admin есть полноценный list/search surface, из которого этот action можно вызывать без ручного знания `sellerId`
- После перевода seller в `UNDER_REVIEW` или `BLOCKED` защищённые seller-facing маршруты больше не отвечают generic `401 Invalid or expired token`
- Вместо этого backend возвращает явные domain errors:
  - `403 ACCOUNT_UNDER_REVIEW`
  - `403 ACCOUNT_BLOCKED`

### Что должна сделать frontend-команда

- Построить admin seller directory/list screen на `GET /admin/sellers`
- Подключить фильтры `search`, `status`, `sellerType`, `page`, `limit`
- Показывать seller summary и item counters прямо из backend response, а не собирать их вручную из разрозненных API
- Использовать `PATCH /admin/sellers/:sellerId/status` из этого списка как основной status-management action
- В seller-facing UI перестать трактовать response после status change как token expiry: `ACCOUNT_UNDER_REVIEW` и `ACCOUNT_BLOCKED` должны обрабатываться как account state

### Acceptance для frontend

- Admin может найти seller по имени/телефону/email и отфильтровать список по `ACTIVE / UNDER_REVIEW / BLOCKED`
- Admin seller list показывает текущее состояние аккаунта и item counters без дополнительных ad-hoc запросов
- После перевода seller в `UNDER_REVIEW` или `BLOCKED` seller-facing UI показывает корректный state-driven экран/ошибку, а не generic auth expiry

### Breaking / migration note

- Нового breaking route change нет
- Это operational contract package: статус account management теперь считается полноценным рабочим срезом, а не isolated action endpoint
- Требуется действие frontend: да

## FE-BE-2026-03-30-04 — Seller item moderation state machine доведён до рабочего вида

- Статус: `ready_for_frontend`
- Scope: seller item dashboard и admin moderation должны перейти на явный moderation lifecycle вместо старых неявных переходов

### Backend scope

- `GET /api/v1/seller/items`
- `POST /api/v1/seller/items`
- `GET /api/v1/seller/items/:id`
- `PUT /api/v1/seller/items/:id`
- `POST /api/v1/seller/items/:id/submit`
- `POST /api/v1/seller/items/:id/withdraw`
- `POST /api/v1/admin/moderation/items/:id/approve`
- `POST /api/v1/admin/moderation/items/:id/reject`
- `GET /api/v1/admin/moderation/items`
- `GET /api/v1/admin/moderation/items/:id`

### Важные контрактные детали

- Item status-модель теперь включает `DRAFT`, `PENDING`, `ACTIVE`, `REJECTED`
- Новый item после `POST /seller/items` создаётся в `DRAFT`, а не уходит на модерацию автоматически
- `PUT /seller/items/:id` больше не разрешён для `PENDING` айтемов; backend отдаёт `400 ITEM_PENDING`
- `POST /seller/items/:id/submit` теперь валидирует обязательные поля и может вернуть `ITEM_ALREADY_PENDING`, `ITEM_ALREADY_ACTIVE`, `ITEM_MISSING_REQUIRED_FIELDS`
- Появился новый endpoint `POST /seller/items/:id/withdraw`, который возвращает pending-айтем обратно в `DRAFT`
- `GET /seller/items` и `GET /seller/items/:id` теперь публикуют `latestModerationRecord`
- `GET /admin/moderation/items` публикует `latestModerationRecord`, а `GET /admin/moderation/items/:id` публикует `latestModerationRecord` и `moderationHistory`
- `approve` и `reject` теперь работают только для `PENDING` айтемов и отдают `409 ITEM_NOT_PENDING`, если item уже промодерирован

### Что должна сделать frontend-команда

- Добавить явный seller badge/UX для статуса `DRAFT`
- Перестать считать, что create/update автоматически отправляют айтем на модерацию
- Добавить UX для `withdraw` перед редактированием pending-айтема
- Показывать `latestModerationRecord.reason/comment` в seller item dashboard и detail screen
- Обработать новые item error codes: `ITEM_PENDING`, `ITEM_ALREADY_PENDING`, `ITEM_ALREADY_ACTIVE`, `ITEM_MISSING_REQUIRED_FIELDS`, `ITEM_NOT_PENDING`
- Обновить admin moderation UI под новый race-safe сценарий, где non-pending item больше нельзя approve/reject

### Acceptance для frontend

- Seller item dashboard корректно показывает `DRAFT / PENDING / ACTIVE / REJECTED`
- Seller может открыть причину последнего reject через `latestModerationRecord`
- Редактирование pending-айтема не делается в обход backend, а требует сначала `withdraw`
- Admin moderation UI корректно обрабатывает `409 ITEM_NOT_PENDING` как race-condition, а не как неизвестную ошибку

### Breaking / migration note

- Это контрактное изменение поведения, а не только новый endpoint
- Старое frontend-допущение “create/update автоматически отправляют item на модерацию” теперь неверно
- Требуется действие frontend: да

## FE-BE-2026-03-30-03 — Upload URL стал storage-agnostic

- Статус: `ready_for_frontend`
- Scope: подготовить frontend к object storage без ломки API-контракта upload endpoint

### Backend scope

- `POST /api/v1/upload/image`

### Важные контрактные детали

- Shape ответа не меняется: upload endpoint по-прежнему возвращает `{ url: string }`
- Значение `url` теперь нужно считать opaque public URL
- При локальном storage это может быть `/uploads/images/<fileName>`
- При S3/CDN storage это может быть абсолютный URL вида `https://cdn.example.com/images/<fileName>`
- В текущем production для product media уже используется абсолютный URL из `DigitalOcean Spaces`
- Frontend не должен собирать upload URL вручную и не должен предполагать same-origin относительный путь

### Что должна сделать frontend-команда

- Использовать `url` из backend response как готовый `src`/asset URL без преобразований
- Не завязывать upload UI на префикс `/uploads/images/`
- Проверить, что seller logo/photo и item image flows не ломаются от абсолютного CDN/S3 URL

### Acceptance для frontend

- Upload UI и asset forms сохраняют в модель ровно тот `url`, который вернул backend
- Отрисовка изображений работает как для относительных, так и для абсолютных URL
- В коде нет ручной конкатенации same-origin upload paths

### Breaking / migration note

- Формально breaking route change нет
- Семантически это важное contract clarification: `url` больше не должен считаться обязательно локальным path

## FE-BE-2026-03-30-02 — OpenAPI response schemas для seller items и admin

- Статус: `ready_for_frontend`
- Scope: убрать manual response layer на seller item dashboard и admin slices; активный backend-blocker backlog по OpenAPI coverage теперь равен `0`

### Backend scope

- `GET /api/v1/seller/items`
- `POST /api/v1/seller/items`
- `GET /api/v1/seller/items/:id`
- `PUT /api/v1/seller/items/:id`
- `DELETE /api/v1/seller/items/:id`
- `POST /api/v1/seller/items/:id/submit`
- `POST /api/v1/seller/items/:id/archive`
- `GET /api/v1/admin/stats`
- `GET /api/v1/admin/leads`
- `GET /api/v1/admin/moderation/items`
- `GET /api/v1/admin/moderation/items/:id`
- `POST /api/v1/admin/moderation/items/:id/approve`
- `POST /api/v1/admin/moderation/items/:id/reject`

### Важные контрактные детали

- Seller item dashboard теперь публикует точные response schemas для списка, detail, create, update, delete, submit и archive flows
- `admin/leads` и `admin/moderation/items` описаны как массивы, а не как ad-hoc обёртки `{ items: ... }`
- `admin/moderation/items/:id/approve` и `reject` возвращают `{ id, name, slug, moderationStatus, isVisible }`
- Активный frontend-значимый OpenAPI gap backlog теперь составляет `0`

### Что должна сделать frontend-команда

- Перегенерировать `openapi/openapi.json` и contract layer
- Убрать ручные response-типы на seller item dashboard и admin slices
- Перевести item/admin queries и mutations на generated response types без локальных transport-заглушек
- Считать оставшиеся ручные response-интерфейсы в этих зонах техническим долгом, а не допустимым baseline

### Acceptance для frontend

- Seller item dashboard использует generated response types для list/detail/create/update/delete/submit/archive
- Admin dashboard и moderation UI используют generated response types без ручного response typing
- После синхронизации frontend больше не держит manual response layer на `seller items` и `admin` slices

### Breaking / migration note

- Breaking route change нет
- Это финальный contract-completeness package по `CP-104`: новые backend schema fixes для этих endpoint'ов больше не требуются
- `204 No Content` delete-маршруты остаются техническим хвостом OpenAPI-оформления, но не считаются blocker'ом для frontend
## FE-BE-2026-03-30-01 — OpenAPI response schemas для auth auxiliary, staff и reference

- Статус: `ready_for_frontend`
- Scope: убрать manual transport layer на auth auxiliary, staff и reference slices

### Backend scope

- `POST /api/v1/auth/forgot-password`
- `POST /api/v1/auth/verify-reset-code`
- `POST /api/v1/auth/reset-password`
- `POST /api/v1/auth/add-buyer-role`
- `GET /api/v1/seller/staff`
- `POST /api/v1/seller/staff`
- `PUT /api/v1/seller/staff/:id`
- `DELETE /api/v1/seller/staff/:id`
- `GET /api/v1/catalog/subjects`
- `POST /api/v1/catalog/subjects`
- `PUT /api/v1/catalog/subjects/:id`
- `DELETE /api/v1/catalog/subjects/:id`
- `GET /api/v1/catalog/locations`
- `POST /api/v1/catalog/locations`
- `PUT /api/v1/catalog/locations/:id`
- `DELETE /api/v1/catalog/locations/:id`
- `GET /api/v1/subjects`

### Важные контрактные детали

- Эти endpoint'ы теперь публикуют response `content` в OpenAPI и готовы к generated response types
- `reset-password` публикует тот же session response contract, что и login/register flows
- `add-buyer-role` возвращает `{ profile }`, а не отдельную ad-hoc форму
- `seller/staff` стабилизирован на форме `{ staff: ... }`
- `catalog/subjects`, `catalog/locations` и публичный `/subjects` отдают явные списки/объекты справочников без необходимости ручного frontend typing

### Что должна сделать frontend-команда

- Перегенерировать `openapi/openapi.json` и contract layer
- Убрать ручные response-типы на password reset auxiliary flow, seller staff и reference slices
- Использовать generated response layer для buyer-role bind flow
- Не держать custom parsers там, где shape теперь уже стабилизирован в OpenAPI

### Acceptance для frontend

- Password reset auxiliary flow использует generated response types
- Staff management UI использует generated response types без ручных transport interfaces
- Reference/admin reference screens используют generated response types для subjects/locations
- Public `/subjects` больше не типизируется вручную

### Breaking / migration note

- Breaking route change нет
- Это не новый endpoint package, а contract-completeness package: старые ручные response-типы теперь считаются техническим долгом и должны вытесняться generated contract layer

## FE-BE-2026-03-28-01 — OpenAPI и контрактный контур

- Статус: `ready_for_frontend`
- Scope: source-of-truth контракт и базовый workflow синхронизации frontend с backend

### Что сделано на backend

- Поднят Swagger UI: `GET /api/docs`
- Поднят runtime OpenAPI artifact: `GET /api/openapi.json`
- Версионируемый OpenAPI artifact хранится в `qadam-core/apps/api/openapi/openapi.json`
- Backend публикует точные response-схемы для ключевых auth/buyer/seller/public flows

### Что должна сделать frontend-команда

- Считать OpenAPI единственным источником истины по transport-типам
- Не поддерживать вручную параллельные request/response types, если endpoint уже описан в OpenAPI
- Для каждого backend-пакета сначала обновлять `openapi/openapi.json`, затем прогонять codegen

### Acceptance для frontend

- Обновлён `qadam-web/openapi/openapi.json`
- Выполнен `pnpm generate:api-contract`
- Нет ручного contract drift относительно backend artifact

### Breaking note

- Breaking change как таковой нет, но ручные transport-типы считаются legacy и подлежат вытеснению generated contract layer

## FE-BE-2026-03-28-02 — Auth registration и password reset

- Статус: `ready_for_frontend`
- Scope: новый registration API и восстановление пароля

### Backend scope

- `POST /api/v1/auth/check-availability`
- `POST /api/v1/auth/register/buyer`
- `POST /api/v1/auth/register/seller`
- `POST /api/v1/auth/forgot-password`
- `POST /api/v1/auth/verify-reset-code`
- `POST /api/v1/auth/reset-password`
- `POST /api/v1/auth/add-buyer-role`

### Важные контрактные детали

- Для duplicate validation используются отдельные ошибки `PHONE_TAKEN` и `EMAIL_TAKEN`
- Buyer может регистрироваться без email
- Seller registration требует полный seller payload, включая `sellerType`, profile fields и `subjectIds`
- Web-клиент продолжает жить на cookie auth (`qadam_at`, `qadam_rt`)
- Mobile-клиенты по-прежнему могут получать токены в body
- Password reset transport пока stub-овый: API создаёт токен/код корректно, но внешняя доставка пока не интегрирована с реальным SMS/email provider

### Что должна сделать frontend-команда

- Собрать новый registration wizard поверх `check-availability`, `register/buyer`, `register/seller`
- Нормально маппить `PHONE_TAKEN` и `EMAIL_TAKEN` в field-level ошибки
- Не строить отдельный frontend-only reset flow в обход backend-контракта
- Использовать новый `add-buyer-role` для multi-role сценария seller → buyer

### Acceptance для frontend

- Регистрация buyer проходит end-to-end на новых endpoint'ах
- Регистрация seller проходит end-to-end на новых endpoint'ах
- Ошибки availability и финального register показываются на конкретных полях
- Password reset UI использует backend token/code flow, а не локальные временные обходы

### Breaking / migration note

- Старый `POST /api/v1/auth/register` сохранён только как legacy route и не должен быть опорой новой frontend-разработки

## FE-BE-2026-03-28-03 — Buyer profile, children, interests

- Статус: `ready_for_frontend`
- Scope: buyer cabinet и post-registration buyer flow

### Backend scope

- `GET /api/v1/me/profile`
- `POST /api/v1/me/profile`
- `PATCH /api/v1/me/profile`
- `PUT /api/v1/me/profile` — legacy alias
- `GET /api/v1/me/children`
- `POST /api/v1/me/children`
- `PATCH /api/v1/me/children/:studentId`
- `DELETE /api/v1/me/children/:studentId`
- `PATCH /api/v1/me/interests`

### Важные контрактные детали

- Канонический write-contract — `PATCH`, а не `PUT`
- Отсутствующий buyer profile должен обрабатываться как отдельное UI-состояние, а не как “тихий create”
- Buyer profile работает от account-centric модели
- Parent и student сценарии расходятся по доступным endpoint'ам и состояниям

### Что должна сделать frontend-команда

- Собрать UI-сценарий отсутствующего buyer profile
- Развести parent и student ветки кабинета
- Перевести buyer profile flow на `POST/PATCH /me/profile`
- Подключить children CRUD и interests update к новому контракту

### Acceptance для frontend

- Buyer profile create/edit не использует legacy assumptions
- Parent видит children list и children CRUD
- Student видит и обновляет interests
- UI устойчив к `BUYER_NOT_FOUND`, `PHONE_TAKEN`, `EMAIL_TAKEN`, `MAX_CHILDREN_REACHED`, `CHILD_NOT_FOUND`

### Breaking / migration note

- `PUT /me/profile` не должен использоваться как канонический путь для новой реализации

## FE-BE-2026-03-28-04 — Seller profile, addresses, Telegram, uploads

- Статус: `ready_for_frontend`
- Scope: seller onboarding/profile management и asset-related flows

### Backend scope

- `GET /api/v1/seller/profile`
- `POST /api/v1/seller/profile`
- `PATCH /api/v1/seller/profile`
- `PUT /api/v1/seller/profile` — legacy alias
- `GET /api/v1/seller/addresses`
- `POST /api/v1/seller/addresses`
- `PATCH /api/v1/seller/addresses/:addressId`
- `DELETE /api/v1/seller/addresses/:addressId`
- `PATCH /api/v1/seller/addresses/:addressId/set-primary`
- `POST /api/v1/seller/telegram/verify`
- `DELETE /api/v1/seller/telegram`
- `POST /api/v1/upload/image`

### Важные контрактные детали

- `PATCH /seller/profile` теперь возвращает специфичные `PHONE_TAKEN` и `EMAIL_TAKEN`, если contact phone/email конфликтуют с другим аккаунтом
- Telegram verify возвращает `{ success, username }`, где `username` может быть `null`
- Upload endpoint принимает `multipart/form-data`, файл в поле `file`
- Upload validation: `JPG/PNG/WebP`, max `5 MB`, min `200x200`
- Успешный upload возвращает `url` как готовый публичный URL; он может быть как локальным `/uploads/images/<fileName>`, так и абсолютным S3/CDN URL

### Что должна сделать frontend-команда

- Довести seller onboarding/profile UI до актуального backend-контракта
- Нормально обрабатывать field-level duplicate errors для contact phone/email
- Подключить address CRUD и primary switch к новому seller area
- Подключить Telegram verification/unbind без самодельного контракта
- Использовать backend upload endpoint для logo/photo/image flows

### Acceptance для frontend

- Seller profile edit работает через `PATCH`
- Address management использует backend CRUD напрямую
- Telegram binding flow использует `POST /seller/telegram/verify`
- Upload UI корректно обрабатывает `FILE_TOO_LARGE`, `INVALID_FORMAT`, `IMAGE_TOO_SMALL`

### Breaking / migration note

- `PUT /seller/profile` — только legacy alias, новые UI-сценарии должны строиться на `PATCH`

## FE-BE-2026-03-28-05 — Admin / governance hooks

- Статус: `ready_for_frontend`
- Scope: backend hooks, которые понадобятся admin-поверхности и governance-сценариям

### Backend scope

- `PATCH /api/v1/admin/sellers/:sellerId/status`

### Важные контрактные детали

- Валидные переходы состояний ограничены
- После смены статуса seller refresh sessions инвалидируются на backend
- Auth guard проверяет живой account status, а не только JWT payload

### Что должна сделать frontend-команда

- Использовать этот endpoint только в admin scope
- Явно закладывать UI для `INVALID_STATUS_TRANSITION`
- Не предполагать, что заблокированный seller сможет жить на старой refresh-сессии

### Acceptance для frontend

- Admin UI корректно меняет seller status и показывает новое состояние
- После блокировки/under_review seller-facing UI правильно реагирует на новые auth errors

## Что не считается frontend blocker прямо сейчас

- Stub transport у password reset не блокирует UI-интеграцию, если flow строится против реального API-контракта
- Локальный upload storage не блокирует frontend, если UI работает только через возвращаемый URL
- Legacy compatibility поля в Prisma не должны протекать во frontend как источник истины

## Как обновлять этот документ дальше

При каждом новом backend change package:

1. Добавить новый блок в этот changelog
2. Обновить `frontend-handoff.md`, если поменялся workflow handoff
3. Обновить `api-routes.md`, если появился новый endpoint или changed behavior
4. Обновить OpenAPI artifact
5. Только после этого передавать пакет frontend-команде
