Commit Graph

242 Commits

Author SHA1 Message Date
Дмитрий a2e73a3d4d feat(support): виджет онлайн-чата JivoSite (спит без ключа)
Accessibility (Pa11y live) / a11y (push) Has been cancelled
SAST — Semgrep / Semgrep SAST scan (push) Has been cancelled
- JivoWidget.vue: подгружает скрипт Jivo при заданном VITE_JIVO_WIDGET_ID
  (по образцу SmartCaptchaWidget); ключ пуст → ничего не грузит, виджет спит.
- Смонтирован в AppLayout (клиентский портал). +VITE_JIVO_WIDGET_ID в vite-env.d.ts.
- TDD: JivoWidget.spec (грузит при ключе / спит без) GREEN; AppLayout.spec 16/16; eslint 0.
- Активируется вставкой VITE_JIVO_WIDGET_ID (твой ID канала Jivo). На прод не выкачено.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 19:19:58 +03:00
Дмитрий eb1ab278fe fix(reports): сбрасывать зелёный «отчёт создан» при отмене задачи (ui-audit минор)
Accessibility (Pa11y live) / a11y (push) Has been cancelled
SAST — Semgrep / Semgrep SAST scan (push) Has been cancelled
- ReportsView.onCancel снимает submitSuccess — success-баннер больше не висит после отмены отчёта. TDD: тест «отмена задачи сбрасывает зелёный» (RED→GREEN), весь ReportsView.spec 22/22, eslint 0.
- docs: ui-audit-round2 — минор тоста помечен исправленным (открытых пунктов не осталось); stage5-checklist — supplier:rekey-orphans dry-run проверен на проде 22.06 («No orphan»), разовая миграция не нужна.
- На прод не выкачено.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 18:32:10 +03:00
Дмитрий 3630e71620 feat(projects): клиентская валидация формы нового проекта (F-NEWPROJECT-1) + синк статусов находок по проду
Accessibility (Pa11y live) / a11y (push) Has been cancelled
SAST — Semgrep / Semgrep SAST scan (push) Has been cancelled
- NewProjectDialog.submit(): обязательны название и источник (домен/номер/отправители по типу сигнала); ошибка показывается сразу, без обращения к серверу. +2 TDD-теста, два существующих теста обновлены под новый гейт. Полный фронт-сьют — без новых падений; проверено глазами в браузере на локалке.
- docs: пометки находок приёмки/UI/impersonation/ADR-018 приведены к фактическому прод-статусу (M-1, M-2 капча Yandex, FN-RESET/2/3/ENC, F-CSV, apiv1-rate, N-4, F-T1, F-P1 — на проде; failed_jobs очищены 494191->0; tenant 24 удалён soft-delete).
- решение владельца 22.06: admin-area доделки (saas-admin SSO, two-person approval, role-guard супер-админа, supplier fallback, обезличенный admin_user_id) сняты как отдельные задачи — доделать единым пакетом вместе с подключением Yandex SSO после ООО.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 16:43:10 +03:00
Дмитрий 8cc09d4509 fix(ui): биллинг — «Запас» по живому заказу, убрать дубль и строку-чтение
Accessibility (Pa11y live) / a11y (push) Has been cancelled
SAST — Semgrep / Semgrep SAST scan (push) Has been cancelled
Поправки после первой версии (по замечаниям владельца):
- «Запас» считаем по текущему заказу проектов (requiredLeadsPerDay), так же как
  BalanceCapacityIndicator: 0/день → ∞ (баланс не расходуется), N/день → лиды/N.
  Раньше брался бэкендный runway_days (историческая скорость за 30 дней) —
  давал «2192 дн.» рядом с «при 0 лидов в день», выглядело фейком/противоречиво.
- Убрана строка-чтение под рядом (дубль).
- Убран BalanceCapacityIndicator из BillingView: дублировал ряд и писал
  «по тарифу», хотя тарифов нет.
- Рамки трёх карточек выровнены по высоте (единый border/radius + flex-stretch,
  убран конфликтный height:100%).

Тесты BalanceCard/BillingView обновлены. vitest 19/19, vue-tsc и build — зелёные.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 13:20:14 +03:00
Дмитрий b21e07a2a7 feat(ui): биллинг — убрать «Тариф», ряд Деньги → Лиды → Запас
Accessibility (Pa11y live) / a11y (push) Has been cancelled
SAST — Semgrep / Semgrep SAST scan (push) Has been cancelled
Тарифов как сущности нет (цена за лид считается по объёму, 7 ступеней) —
карточка «Тариф» («не выбран» + вечно-неактивная кнопка) убрана. На её место —
«Запас» (runway_days): на сколько хватит баланса при текущем заказе. Ряд связан
стрелками + строка-чтение «N ₽ = M лидов = надолго / на K дн.». Число лидов
теперь с разделителем тысяч. Дубль «хватит на N дн.» убран из шапки.

Тесты BalanceCard.spec / BillingView.spec обновлены под новую структуру.
Сборка и vue-tsc — зелёные.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 12:58:57 +03:00
Дмитрий 173b089629 feat(G7-B): клиентская плашка impersonation + редирект/ключ в диалоге + leave 2026-06-19 17:09:59 +03:00
Дмитрий 4c3e57bf9b feat(G3): убрать преференцию «Напоминание» + хвосты фронт-тестов
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 11:29:46 +03:00
Дмитрий cbf8b4fb43 feat(G3): убрать next_reminder_at из фронт-слоя сделок
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 11:29:35 +03:00
Дмитрий a49b201d33 feat(G3): убрать экран «Напоминания» и раздел в карточке сделки
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 11:18:49 +03:00
Дмитрий 799d416b9a fix: дополнен мок DashboardSummary в DashboardView.spec (avg_lead_cost_rub) — type-check green
Пред-существующая type-ошибка (поле required в DashboardSummary отсутствовало в моке). Не связано с SP3a; убирает единственную ошибку vue-tsc. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 23:45:08 +03:00
Дмитрий bacc7c5e24 feat: G1/SP3a фронт входа — регистрация + подтверждение почты
Переработка register под новый бэкенд SP1 (код на почту), новый ConfirmEmailView, капча-шов, роут /confirm-email. Проверено Playwright: register→код→confirm→dashboard, негатив, fallback email. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 23:33:26 +03:00
Дмитрий 41adf00cba feat: G4 убрать неработающий push-канал из настроек уведомлений + находка G8 про сломанный фронт-тест-раннер
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 13:46:31 +03:00
Дмитрий 1cb3b56f70 fix(dashboard): верхняя строка дашборда — настоящие числа вместо заглушки — F5
Accessibility (Pa11y live) / a11y (push) Has been cancelled
SAST — Semgrep / Semgrep SAST scan (push) Has been cancelled
Строка-приветствие показывала захардкоженную рыбу: +3 новых лида с утра,
сегодня 11 / вчера 38, средняя стоимость 2 248 руб. Числа ни к чему не были
привязаны — остаток прототипа Sprint 4.

Бэкенд: DashboardController.summary отдаёт avg_lead_cost_rub — среднее
фактически списанных rub-сумм за окно периода: AVG price_per_lead_kopecks
WHERE charge_source rub делить на 100; null если в окне нет rub-списаний.
Тот же источник, что карточка сделки F2.

Фронт: DashboardPageHead принимает пропы сегодня/вчера/средняя; сегодня и
вчера берутся из activity.points последняя точка сегодня; средняя из
avg_lead_cost_rub, прочерк при null. Размытое +3 с утра убрано.

TDD: 2 Pest DashboardSummaryTest 10/10 + 4 vitest DashboardPageHead;
полная фронт-сюита 959 passed / 3 skipped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 16:02:04 +03:00
Дмитрий 6cc8cd86ef fix(billing): столбец «Операция» в обзоре — ярлык по типу операции — F4
Accessibility (Pa11y live) / a11y (push) Has been cancelled
SAST — Semgrep / Semgrep SAST scan (push) Has been cancelled
История транзакций в обзоре биллинга показывала пустой столбец «Операция»:
списания за лид LedgerService создаёт без description, а таблица выводила
поле как есть без запасного текста. Добавлен ярлык по типу операции
с приоритетом сохранённого description. Косметика отображения,
денежных значений не касается. TDD: 2 vitest, 955 passed / 3 skipped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 15:07:26 +03:00
Дмитрий 2a6b476d6d fix(billing): единый источник runway — дашборд = биллинг (F3)
Дашборд считал «хватит на дни» от legacy balance_leads (≈0 для рублёвых тенантов)
и расходился с биллингом. Введён общий RunwayCalculator; оба контроллера считают
runway от affordable leads (рубли→лиды по тарифу, BalanceToLeadsConverter). Фронт
DashboardView больше не режет число дней до 7 сегментов полосы. TDD: 4 Pest нового
сервиса + обновлён DashboardSummary + 1 vitest.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 14:07:22 +03:00
Дмитрий 4d8a1af099 feat(deals): карточка показывает реальную стоимость лида — F2
Backend: GET /api/deals/{id} отдаёт cost_kopecks — снимок rub-списания из
lead_charges по deal_id, либо null для prepaid/не списано. Frontend: ApiDeal.cost_kopecks
→ MockDeal.costKopecks → карточка DealDetailBody показывает formatCost(costKopecks/100)
либо прочерк вместо вводящего в заблуждение 0 рублей. TDD: 3 Pest + 4 vitest.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 13:30:46 +03:00
Дмитрий 2f8427091d feat(deals): карточка сделки показывает Город — F1
Поле Город добавлено в секцию Параметры DealDetailBody со значением deal.city,
прочерк при пустом. TDD: 2 теста в DealDetailBody.spec.ts. Чистое отображение,
денежных полей не касается.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 12:40:49 +03:00
Дмитрий a6aaaa5518 F-5 eslint: 0 problems
DealDetailDrawer: default для tenantId (require-default-prop). AdminPdSubjectRequestsView: v-slot:[...] в #[...] (v-slot-style, auto-fix). 2 region-спека: disable-комментарий no-explicit-any для VueWrapper-кастов F-3 - по конвенции 9 соседних тестов.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 07:22:08 +03:00
Дмитрий f94552d452 WIP чекпойнт: lead-region/supplier бэкенд + фронт-редизайн + Pint + тесты
92 файла одной пачкой. Исключены чужие зоны: CLAUDE.md, .claude/settings.json, docs/observer/.pii-counters.json.
gitleaks staged: no leaks found. Не верифицировано тестами - сохранение труда в историю.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 05:17:12 +03:00
Дмитрий a94e554a69 F-3 type: 3 vue-tsc TS2345 в region-тестах - cast findComponent к VueWrapper any + props - vue-tsc чисто
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 04:38:55 +03:00
Дмитрий 57f554ea7c F-2 a11y Tenants: label поиска по ИНН + AA-класс stat-success счётчика активных - 2 теста зелёные, регрессия 946 passed
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 03:17:32 +03:00
Дмитрий 6e2ad108de feat(slepok): Task 2.11 UI — applies_from toast «вступит в силу N.21:00 МСК»
После правки slepok-чувствительных полей проекта (regions / delivery_days_mask /
daily_limit_target / источник) backend возвращает ProjectResource.applies_from
= N.21:00 МСК (Task 2.11 backend slice, commit dd5954d8). Клиент Лидерры
теперь видит расширенный тост: «Сохранено. Изменения вступят в силу
DD.MM.YYYY в 21:00 МСК.» Когда правка не затронула slepok — обычное
«Сохранено.».

Изменения:
- composables/appliesFromMessage.ts — чистый форматтер (Moscow tz, не локаль клиента).
- ProjectDetailsDrawer / NewProjectDialog / EditProjectDialog — emit('saved', appliesFrom).
- ProjectsView — v-snackbar + onSaved/onDrawerSaved обработчики.
- tests/Frontend/appliesFromMessage.spec.ts — 5 invariant-кейсов.

Plan §Task 2.11 Step 5-6. Spec §4.2.5 UX block. R-15 + R-06..R-08 UX closure.
Vitest worktree-only 944/3sk GREEN, vue-tsc 3 pre-existing errors (вне диффа),
ESLint clean на затронутых файлах.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 09:52:42 +03:00
Дмитрий e1601e7862 feat(billing-v2-c): UI префлайт Task 1.10 — баннер заморозки, индикатор ёмкости, диалог перегрузки
Spec C §3.6/§6.2. Бэкенд: GET /api/billing/balance-status (frozen + capacity + required + дефицит ₽/leads), Pest 6. Фронт: BalanceFrozenBanner (в AppLayout, глобально), BalanceCapacityIndicator (в BillingView под балансом), ProjectLimitOverloadDialog (409-перехват в NewProjectDialog: save-blocked/set-zero), tenantStore + api getBalanceStatus. Vitest +18.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 20:39:21 +03:00
Дмитрий fdfaa956bd feat(ui): surface supplier-snapshot guard errors in ProjectDetailsDrawer + BulkActionsBar 2026-05-26 12:33:18 +03:00
Дмитрий 7cf9f06736 feat(admin): wire balance dialog into tenant list table 2026-05-23 20:02:39 +03:00
Дмитрий 6385e6fce6 feat(admin): TenantBalanceDialog + updateTenantBalance api client 2026-05-23 20:02:38 +03:00
Дмитрий ff2ee59e78 fix(billing-v2): regression — ESLint vuetify imports in new test specs 2026-05-23 18:46:23 +03:00
Дмитрий 476f1cf25b fix(billing-v2): ChargesTab — drop «Источник» filter/column, prepaid tooltip for history 2026-05-23 18:46:22 +03:00
Дмитрий 497415192b fix(billing-v2): InvoicesTable — append ₽ to amount_total 2026-05-23 18:46:21 +03:00
Дмитрий ba868e465c fix(billing-v2): TransactionsTable — drop refund tab, display_amount_rub, year in date 2026-05-23 18:46:20 +03:00
Дмитрий 52ace2863d feat(billing-v2): BillingView — embed TierPricesPanel 2026-05-23 18:46:20 +03:00
Дмитрий f1e8eaf40a feat(billing-v2): TierPricesPanel — 7-tier collapsed panel + current highlight 2026-05-23 18:46:19 +03:00
Дмитрий 27eba3c6db fix(billing-v2): BillingView — drop «лидов запас», wire new BalanceCard props 2026-05-23 18:46:19 +03:00
Дмитрий 383b105bf5 feat(billing-v2): BalanceCard — ≈ N лидов via affordable_leads, drop (ГЦК) 2026-05-23 18:46:18 +03:00
Дмитрий cfe94d9178 fix(projects): closable-chips на селекторах регионов — удаление по одному
Раньше чтобы убрать один регион из выбора, приходилось сбрасывать все
и выбирать заново. Добавлен closable-chips на v-autocomplete регионов в
трёх местах: карточка создания проекта (NewProjectDialog), панель
редактирования (ProjectDetailsDrawer) и массовое изменение регионов
(RegionsBulkDialog). Теперь у каждого чипа есть крестик.

Покрыто Vitest: closableChips=true на каждом селекторе.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 10:21:10 +03:00
Дмитрий 68f42ad385 feat(projects): информационный баннер о сроке изменений до 18:00 МСК
Закрывается крестиком, закрытие запоминается в localStorage. Чисто фронтенд (информация, без блокировок, без бэкенда). +3 Vitest.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 11:21:42 +03:00
Дмитрий 3bbd7787d8 feat(projects-ui): replace archive with delete, drop archived filter
- Remove archived_at from Project interface; rename store.archive → store.del
- BulkActionsBar: archive button → delete (testid, icon, confirm text)
- ProjectCard: archive menu item → delete (emit + icon)
- ProjectDetailsDrawer: confirm text + store.del call
- ProjectsView: @delete binding, remove 'Архивные' status filter entry
- vuetify.ts: add mdi-delete → Trash2 mapping
- All specs/stories updated: archived_at removed, archive → del renamed
- New test: del() calls DELETE /api/projects/{id}

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 08:37:26 +03:00
Дмитрий b7466ebfbd fix(admin): убраны захардкоженные mock-счётчики в админ-меню (Тенанты 142 / Инциденты 3)
Бейджи показывали фиксированные 142/3, расходящиеся с реальными данными
(5 тенантов, 0 открытых инцидентов) — вводили в заблуждение. Удалены; неверный
бейдж хуже отсутствия. Живые счётчики — отдельная фича. TDD: AdminLayout.spec.ts (RED→GREEN).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 19:24:17 +03:00
Дмитрий 17e3c04f24 fix(layout): topbar title из route.meta.title для страниц вне sidebar-nav
AppLayout брал заголовок топбара только из sidebar navItems → /reminders и
/import (которых нет в боковом меню) показывали fallback «Страница». Добавлен
fallback на route.meta.title перед «Страница». TDD: AppLayout.spec.ts (RED→GREEN).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 19:23:58 +03:00
Дмитрий ba49805689 fix(dashboard): приветствие по реальному имени пользователя + по времени суток
DashboardPageHead показывал захардкоженное «Доброе утро, Иван» любому
пользователю. Теперь имя берётся из auth-store (first_name), а приветствие —
по времени суток (ночь/утро/день/вечер). Fallback «коллега» при отсутствии user.
TDD: DashboardPageHead.spec.ts (RED→GREEN).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 19:23:43 +03:00
Дмитрий c7fd90c08d fix(deals): читать проекты из конверта { data } + чинить фикстуры LeadStatus
DealsView крашился (Cannot read properties of undefined reading 'map'): listProjects() читал data.projects, но ProjectController::index() отдаёт { data: [...] } после миграции на JsonResource — availableProjects=undefined ломал .map, фильтр «Проект» был пуст. Фикс: читать data.data ?? []. + deals-api.spec.ts тест на новый конверт + защитный []. + DealDetailHero.spec.ts: фикстуры LeadStatus (isSystem/sortOrder вместо order) — устранён pre-existing type-check error.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 15:20:53 +03:00
Дмитрий e35fc6c938 feat(projects): require region + explicit «Вся РФ» with warning gate
План 4 Task 4 эпика project-migration-redesign.

- NewProjectDialog: отдельный чекбокс «Вся РФ» (89 субъектов в autocomplete
  без sentinel сохранены) + inline v-alert предупреждение + подтверждение.
- Взаимоисключение: выбор субъектов снимает «Вся РФ» и наоборот.
- Гейт submit: блок если ни субъектов, ни подтверждённой «Вся РФ»
  (errors.regions = «Выберите регион...»); «Вся РФ» -> regions=[] на API.
- Лейбл autocomplete «Регионы» (убрано «(пусто = вся РФ)»).
- watch immediate:true — инициализация vsyaRf/edit-prefill при mount
  (чинит EditProjectDialog submit при модальном открытии).
- Vitest 3/3 новых + 22 passed соседних (NewProject/Edit/ProjectsView) без регрессий.
2026-05-20 14:34:27 +03:00
Дмитрий f1a3e9f02f feat(admin): supplier projects cleanup screen (list + bulk delete)
План 4 Task 3 эпика project-migration-redesign.

- AdminSupplierProjectsView.vue — v-data-table (источник/платформа/регион/
  лимит/кто заказывал/последняя поставка) + bulk-delete с v-dialog
  подтверждением + snackbar (deleted/failures).
- Роут /admin/supplier-projects (layout admin, requiresAuth, devIndex 31).
- AdminLayout nav-пункт «Проекты у поставщика».
- Vitest 3/3 (mount GET, bulk-delete confirm POST {ids}, disabled when empty).

NB: type-check имеет 3 pre-existing ошибки в DealDetailHero.spec.ts
(коммит 1412d3f, не Plan 4); файлы T3 type-check-чисты.
2026-05-20 14:34:25 +03:00
Дмитрий 01d292f5a9 feat(admin): supplier export-mode toggle (online|batch) endpoint + UI
План 4 Task 1 эпика project-migration-redesign.

- AdminSupplierIntegrationController +getExportMode/setExportMode
  (validation in:online,batch; system_settings upsert).
- Routes: GET/POST /api/admin/supplier-integration/export-mode
  в admin-группе рядом с manual-queue.
- AdminSupplierIntegrationView.vue +секция «Режим экспорта проектов»
  с v-btn-toggle (online|batch), подпись о ночном синке 18:00.
- Pest 3/3 + Vitest 2/2 (+ соседние 5 не сломаны).
- phpstan-baseline.neon +6 ignore (Pest TestCall::actingAs/getJson/postJson
  — типовой паттерн, как в SupplierManualQueueTest).
2026-05-20 14:34:22 +03:00
Дмитрий 9443b5b446 feat(supplier): manual queue section in AdminSupplierIntegrationView
Таблица pending-записей яруса 3 + кнопка «Отметить выполнено» с confirm-
диалогом, дёргает POST .../manual-queue/{id}/resolve. Реюз существующего
админ-экрана интеграции с поставщиком (после «Истории сверок»).

NB: spec в tests/Frontend/ (vitest include — tests/Frontend/**, не
resources/.../__tests__/ как указал план Step 11.1). loadManualQueue
defensive Array.isArray-guard — иначе onMounted в чужих spec'ах
(mockResolvedValue без queue-ключа) ловил undefined.length.

Spec §4.6. Task 11 of 12. Vitest 5/5 (2 новых + 3 существующих).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 12:55:10 +03:00
Дмитрий 66320166b8 feat(supplier): UI-экран «Интеграция с поставщиком» — здоровье CSV-канала 2026-05-18 17:58:53 +03:00
Дмитрий 1412d3fefd feat(deals/drawer): inline status picker — статус-chip кликабельный, без мутации props
UX-request 18.05.2026 (п.3):
- DealDetailHero: v-chip → v-menu со списком всех статусов из lead_statuses
  store; форма и цвет chip'а не меняются
- DealDetailBody: emit 'status-changed' наверх (без мутации props.deal)
- DealDetailDrawer: forward события наружу
- DealsView: onDrawerStatusChanged → optimistic update dealsState + PATCH
  /api/deals/{id} + rollback
- KanbanView: onDrawerStatusChanged → перенос карточки между колонками
  dealsByStatus + transitionDeals + rollback на ошибку

Vue правило vue/no-mutating-props соблюдено (логика в parent'е, не в Body).

Vitest 5 файлов / 38 passed на затронутых; build 2.29s.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 15:34:07 +03:00
Дмитрий 789e7dcdb6 feat(deals/drawer): убрать «Менеджер», добавить «Тип» + «Источник» read-only
UX-request 18.05.2026 (пп.4/6/7):
- удалена секция «Менеджер»/«Не назначен» (менеджеров в системе пока нет)
- добавлен параметр «Тип» (Сайт/Звонок/СМС) — project.signal_type
- добавлен параметр «Источник» (read-only):
  - site/call → project.signal_identifier (домен или телефон)
  - sms → sms_senders[0] + ' (KEYWORD)' если sms_keyword не пустой
- удалён hardcoded «Я.Директ → landing-1»

Backend: DealController index + show + update payload расширены 4 полями
project_signal_type/identifier/sms_keyword/sms_senders + eager-load
project relation расширен.

Редактирование источника — только в карточке проекта (Task 5 плана).

Larastan baseline bumped (DealShowTest: tenant 13→20, getJson 7→10 для 3 новых тестов).
Pest 51/51 на Deal-endpoints.
Vitest 108 files / 875 passed / 3 skipped (5 новых тестов DealDetailBody).
Build 2.30s.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 15:24:57 +03:00
Дмитрий 3bedf10449 feat(deals): drawer виден при selected≤1, bulk-полоса только при ≥2
UX-request 18.05.2026:
- selected.length === 1 → drawer авто-открывается на этой сделке,
  bulk-полоса скрыта (одну сделку проще менять через drawer)
- selected.length >= 2 → drawer закрыт, bulk-полоса видна
- selected.length === 0 → как сейчас (drawer по row-click)

Vitest 12/12 на DealsView.spec (2 новых теста + 10 существующих, none broken).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 15:14:03 +03:00
Дмитрий 36ea9cde04 feat(deals): убрать префикс B1_/B2_/B3_ из отображения «Источник»
Поставщик crm.bp префиксует имена проектов признаком канала-провайдера
(B1_/B2_/B3_ — три базы лидов). В UI Лидерры префикс — шум: пользователю
интересен сам проект, не канал.

Трансформация display-only — данные в БД не трогаем, фильтрация идёт по
project_id (не name).

Утилита: app/resources/js/composables/projectName.ts → stripChannelPrefix.
Регэксп ^B[123]_ case-insensitive; null/undefined/'' → ''.

Применено в 4 точках:
- DealsTable «Источник» (item.project)
- DealsFilters «Проект» dropdown (через computed-маппинг в DealsView)
- KanbanCard карточка
- DealDetailBody параметры панели

Тесты: 8 unit-тестов на утилиту (B1/B2/B3 case-insensitive, не трогать
B0/B4/Bx, не трогать префикс в середине строки, null/undefined/''),
38/38 на затронутых компонентах, 868/3sk/0 full Vitest, build 2.62s.

Smoke /deals: 20 строк, ни одна не начинается с B1_/B2_/B3_ (был
«B1_73912557675 [35]», стал «73912557675 [35]»; «B3_krk-finance.ru/...»
→ «krk-finance.ru/...»). Скриншот deals-no-bprefix-2026-05-18.png.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 14:33:33 +03:00