# Редизайн страницы «Сделки» — дизайн

**Дата:** 2026-05-17
**Статус:** дизайн утверждён заказчиком; спека на ревью.
**Макет:** `web/v8/deals-final.html` (вариант A, финал). Сравнение вариантов — `web/v8/deals-variants.html`.

## 1. Контекст и проблема

Текущая страница «Сделки» (`resources/js/views/DealsView.vue`) построена как обычная CRM: ручное создание сделки, корзина, mock-данные, колонки «Менеджер»/«Стоимость». Это неверно: лиды поступают в портал **только** от поставщика crm.bp по заказанным в проектах источникам (B1/B2/B3 × тип сигнала). Сделку нельзя создать вручную и нельзя удалить — у неё есть только статус.

Конкретные ошибки текущей реализации:

- Кнопка «Новая сделка» + `NewDealDialog` — [DealsView.vue:542-551](../../../app/resources/js/views/DealsView.vue#L542-L551), [:741](../../../app/resources/js/views/DealsView.vue#L741).
- Режим «Корзина» (soft-delete) — [DealsView.vue:524-532](../../../app/resources/js/views/DealsView.vue#L524-L532).
- Mock-данные — `mockDeals.ts` ([DealsView.vue:18](../../../app/resources/js/views/DealsView.vue#L18)).
- Колонки «Менеджер»/«Стоимость» и две дублирующие фильтр-панели — [DealsTable.vue:57-64](../../../app/resources/js/components/deals/DealsTable.vue#L57-L64), [DealsView.vue:559-687](../../../app/resources/js/views/DealsView.vue#L559-L687).

## 2. Утверждённый дизайн

Страница — **реестр лидов, поставленных crm.bp**. Ручного создания и корзины нет.

**Шапка:** заголовок «Сделки» + статистика + кнопка «Обновить».

**Панель экспорта:** диапазон дат поставки (`от` — `до`) + кнопки «Экспорт в Excel» и «Экспорт в CSV». Диапазон фильтрует таблицу и задаёт окно экспорта.

**Фильтры:** поиск по телефону, Статус, Проект, Город.

**Выбор числа строк:** 10 / 20 / 50 — между фильтрами и таблицей.

**Таблица** (колонки): чекбокс · Телефон · Источник · Город · Статус · Напоминание · Комментарий · Поставлен.

- **Телефон** — только номер (ФИО убрано).
- **Источник** — название проекта + тип сигнала (Звонки/Сайт/СМС); B1/B2/B3 не показываются.
- **Город** — см. §4 (открытый вопрос источника).
- **Статус** — одна из 5 (см. §3).
- Колонка «Ответственный» убрана; «Отгружен» переименован в «Поставлен».

**Панель сделки (master-detail):** клик по строке открывает панель с деталями сделки **справа от списка**. Список при этом **сжимается влево**, освобождая место — панель **не перекрывает** таблицу (в отличие от текущего overlay-дровера). Повторный клик / кнопка ✕ — закрыть. Содержимое панели: телефон, статус, источник (проект+тип), город, дата поставки, напоминание, комментарий, история сделки.

**Футер:** пагинация.

**Сайдбар** (отдельная правка, уже внесена): из nav убраны ссылки «Импорт данных» и «Отчёты» — `AppSidebar.vue`, тест `AppLayout.spec.ts` обновлён.

## 3. Воронка статусов: 14 → 5

Заказчик пересматривает воронку: вместо 14 статусов — **5**:

1. **Новая сделка** — присваивается при поступлении лида от crm.bp.
2. **Просмотрено**
3. **В работе**
4. **Сделка** — успешно закрыта.
5. **Не реализовано** — закрыта без результата.

Это самый крупный затрагиваемый блок (work-stream B, см. §6). Каскад:

- Схема: `lead_statuses` / воронка (`db/schema.sql` р-н строки 2076) — изменение справочника статусов; миграция с записью в `db/CHANGELOG_schema.md`.
- `composables/leadStatuses.ts` + `stores/leadStatuses` — новый набор.
- `mockDeals.ts` `DEALS_TABS` — срезы по статусам.
- Страница **Канбан** — колонки строятся из статусов → 5 колонок вместо 14.
- Фильтр по статусу + массовая смена статуса (`DealsBulkBar`, `DealBulkActionController@transition`, `dealsApi.transitionDeals`).
- `StatusRuToSlugMapper` (Sprint 4, импорт исторических лидов) — маппинг русских статусов поставщика на slug'и пересматривается под 5.

**Предлагаемый маппинг старых 14 → новых 5** (на подтверждение в фазе плана):

| Старый | Новый |
|---|---|
| Новый | Новая сделка |
| Просмотрено | Просмотрено |
| Проработан, База, Недозвон, Переговоры, Ожидаем оплаты, Партнёрка, Тест-драйв, Горячий, На замену, Конечный недозвон | В работе |
| Оплачено | Сделка |
| Закрыто и не реализовано | Не реализовано |

Слуги новых статусов и точный маппинг — фиксируются в плане после явного подтверждения заказчиком.

## 4. Открытый вопрос — источник данных для колонки «Город»

crm.bp **не передаёт** город/регион ни по одному каналу ингеста:

- вебхук `SupplierWebhookController@receive` валидирует только `vid/project/phone/time/tag/phones`;
- CSV-импорт (`Services/Import/*`) гео-полей не читает.

Поставщик доработку делать не будет. Из телефона регион не вывести (`PhonePrefixService` для мобильных 9XX → «неизвестно»). В схеме `deals.region_code` + `deals.city` есть (Биз-23), но заполнять нечем.

**Решение:** заказчик определит источник заполнения «Города» отдельно. До этого колонка в реализацию не берётся (рендерится пустой/`—`). Вопрос зафиксирован: memory `project_deals_page_redesign.md`, напоминание 18.05.

## 5. Затрагиваемые компоненты

**Frontend:**

- `views/DealsView.vue` — убрать `NewDealDialog`/«Новая сделка», trash-режим, дублирующую `.ld-filterbar`; перевести на реальный API; добавить панель экспорта по датам и выбор числа строк.
- `components/deals/DealsTable.vue` — новый набор колонок.
- `components/deals/DealsFilters.vue` — фильтры Статус/Проект/Город; убрать «Менеджер».
- `components/deals/DealsBulkBar.vue` — массовая смена статуса на 5 значений.
- `composables/mockDeals.ts` — снять mock-зависимость (реальные данные из API).
- `composables/leadStatuses.ts` / `stores/leadStatuses` — 5 статусов.
- `api/deals.ts` — список с фильтром по диапазону дат поставки, пагинацией `per_page`, полем города.
- `components/deals/DealDetailDrawer.vue` — переделать из overlay-дровера в боковую панель master-detail: список сжимается, панель встаёт рядом (flex split-pane), не перекрывает таблицу. Содержимое карточки переиспользуется.

**Backend:**

- `DealController@index` — пагинация `per_page` (10/20/50), фильтр `received_at BETWEEN`, фильтр по городу/статусу/проекту.
- `DealExportController` (`POST /api/deals/export`) — экспорт по диапазону дат, XLSX + CSV.
- `db/schema.sql` — справочник статусов воронки 14→5 (+ `db/CHANGELOG_schema.md`).
- `RouteSupplierLeadJob` — присваивает статус «Новая сделка» новым лидам (сейчас `'new'`).
- `StatusRuToSlugMapper` — маппинг под 5 статусов.

## 6. Декомпозиция и порядок

Два work-stream'а:

- **A — редизайн страницы** (frontend + `DealController`/`DealExportController`): колонки, фильтры, экспорт, пагинация, удаление ручного создания/корзины. Не зависит от B при условии, что A временно работает на текущем наборе статусов.
- **B — воронка 14→5** (схема + миграция + Канбан + маппинг импорта). Schema-change — гейт на явное подтверждение маппинга §3.

Рекомендация для плана: A и B — отдельными фазами; B стартует после подтверждения маппинга. Колонка «Город» — вне реализации до решения §4.

## 7. Вне scope

- Заполнение колонки «Город» данными — до решения по источнику (§4).
- Inline-редактирование напоминания/комментария в строке (было в варианте B макета) — вариант A оставляет их read-only.
- Содержимое карточки сделки (поля, история) не пересматривается — переиспользуется как есть; меняется только подача: overlay-дровер → боковая панель master-detail (§2, §5).

## 8. Тестирование

- TDD на каждый блок: Vitest (DealsView/DealsTable/DealsFilters/DealsBulkBar), Pest (`DealController@index` пагинация/фильтры, `DealExportController` диапазон дат, миграция статусов — db-тесты).
- После изменения схемы статусов — db-тесты + smoke.
- Регрессия Vitest + Pest перед коммитом.

## 9. Открытые вопросы для фазы плана

1. Источник данных для «Города» (§4) — ждём заказчика.
2. Точные slug'и 5 статусов и маппинг старых 14 (§3) — подтвердить.
3. ~~Судьба маршрутов `/import` и `/reports`~~ — **РЕШЕНО (2026-05-17): вариант А** — маршруты и вью остаются доступными по прямому URL; из nav-сайдбара ссылки убраны (уже сделано).
