Фундамент под сквозную вложенность: periodRange() читает date_from/date_to (приоритет) либо preset; Финансы и Клиенты считаются по выбранному периоду через whereBetween. FE: «Свой период» + два date-поля + «Применить» → date_from/date_to. Спека дизайна A+B+C+масштаб сохранена. Baseline перегенерирован (getJson тестов). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
10 KiB
Дашборд: сквозная вложенность до источника + масштаб + выбор периода
Дата: 2026-06-28 Автор: Claude (по запросу владельца) Статус: одобрено владельцем (брейншторм 28.06)
Цель
С дашборда «Командный центр» по логике вложенности доходить до конечного источника (лид → откуда пришёл и кому ушёл), все списки должны оставаться читаемыми при больших объёмах (1000 клиентов, десятки тысяч лидов), а период анализа — выбираемым (пресеты + свой диапазон дат).
Решения владельца
- Полная цепочка лида: Дашборд → Лиды → список (фильтры) → конкретный лид → откуда (поставщик-проект B1/B2/B3 + канал сайт/звонок/sms + регион) → кому (клиент + сделка + статус).
- Масштаб: топ-N на дашборде + «Открыть всё →» в полноэкранный список с серверными поиском/фильтрами/страницами. Дашборд остаётся лёгким.
- Период: пресеты (Сегодня / 7 / 30 / 60 / 90 дней) + свой период (дата с — дата по). Все плитки, drill'ы и списки считаются по выбранному периоду.
Принцип вложенности (3 уровня)
L1 плитка (сводка, светофор)
→ L2 drill (топ-N + смысл + «Открыть всё →»)
→ L3 полноэкранный список (поиск + фильтры + страницы)
→ L4 карточка / конечный источник
Дашборд НЕ становится браузером данных: тяжёлые списки живут на полноэкранных экранах, которые держат объём (серверная пагинация). Карточки клиента/проекта переиспользуем существующие.
Кросс-режущее: период
- Query-параметры всех эндпоинтов:
period=today|7d|30d|60d|90dИЛИdate_from=YYYY-MM-DD&date_to=YYYY-MM-DD(свой диапазон; приоритет над preset). periodStart()в контроллере дополняется разборомdate_from/date_to→ возвращает[from, to]. Все запросы фильтруют>= from AND < to+1day.- Фронт: тумблер пресетов + кнопка «Свой период» открывает два date-picker'а
(Vuetify
v-date-input/диалог); выбор шлётdate_from/date_to. Подпись периода показывается в шапке.
Кросс-режущее: серверная пагинация (масштаб)
- Полноэкранные списки отдают
{ data: [...], total, page, per_page }(Laravel paginator). Параметры:?page,?per_page(дефолт 25, max 100),?search, плюс специфичные фильтры. - Индексы под фильтры — только аддитивные миграции (CREATE INDEX), без блокировок
(CONCURRENTLY на проде, в окне). Сверять с
db/CHANGELOG_schema.md. - На дашборде (L2) — только топ-N (10–20), без пагинации; «Открыть всё» уводит в L3.
Этап A — Лиды (полная цепочка) ⭐ главное
L2 (drill Лиды): топ-10 последних лидов: время · источник (канал+identifier) · поставщик (B1/B2/B3) · клиент-получатель · статус. Кнопка «Открыть все лиды →».
L3 — новый экран «Лиды» (/admin/leads, nav-пункт):
- Серверная таблица: время · источник (signal_type+identifier) · поставщик (platform) · регион · клиент(ы) · статус (доставлен/завис/ошибка).
- Фильтры: диапазон дат, канал (signal_type: site/call/sms), поставщик (B1/B2/B3/direct), клиент (tenant), статус; поиск по identifier/телефону(маска)/id.
- Пагинация серверная.
L4 — карточка лида (/admin/leads/{id}):
- Откуда: поставщик-платформа + signal (канал+identifier) + регион (resolved_subject_code)
- ссылка на проект у поставщика.
- Что пришло: received_at, ключевые поля raw_payload (ПДн — маскируются по 152-ФЗ:
телефон
+7 9** *** ** 21, имя — инициалы). - Кому ушло: deals_created_count → список сделок (клиент + статус сделки + ссылка на карточку тенанта / сделку).
- Обработка: processed_at, error (если был).
Данные: supplier_leads (platform, signal_type, signal_identifier?, raw_payload,
received_at, processed_at, error, deals_created_count, resolved_subject_code, region_source)
deals(tenant_id, status, project_id, received_at) связь по лиду. Точную связь lead↔deal уточнить по схеме при реализации (deal.source_lead_id / по телефону+времени).
Безопасность: ПДн маскируются в списке и карточке (152-ФЗ); полный показ — только если уже так делается в клиентской карточке сделки. Не логировать ПДн.
Этап B — Заказ у поставщика (кликабельные группы)
- Строки таблицы групп → кликабельны → детали группы: какие клиентские проекты
входят (спрос по каждому) + проект(ы) у поставщика (факт) + «Открыть в Проектах
у поставщика →» (
/admin/supplier-projectsс фильтром по группе). - «Открыть всё →» из drill →
/admin/supplier-projects. - Конечный источник заказа = запись проекта у поставщика (существующий экран) + клиентские проекты группы.
Этап C — Здоровье + «Открыть всё» на Финансы/Клиенты
- Здоровье: подсистемы кликабельны → Инциденты (
/admin/incidents), очереди/джобы → экран системы/упавших джоб, синхрон/CSV →/admin/supplier-integration, вебхуки → соответствующий экран. (Куда именно — по наличию экранов.) - Финансы: «Требуют внимания»/«Топ» → уже карточка тенанта; добавить «Открыть всё →»
→
/admin/tenants(фильтр «в минусе») и/admin/billing. - Клиенты: новые/спящие → карточка тенанта; «Открыть всё →» →
/admin/tenants(фильтры новые / спящие). Проверить, что/admin/tenantsуже серверно-пагинирован и ищет; если нет — добавить (масштаб 1000 клиентов).
Масштаб — что проверить отдельно (1000 клиентов / 10⁴ лидов)
/admin/tenants(куда ведут Финансы/Клиенты): серверный поиск + пагинация + фильтры (новые/спящие/в минусе). Если сейчас клиентский список тянет всё — переделать на сервер.- Дашбордовые агрегаты (counts) — по индексам; тяжёлые DISTINCT/JOIN проверить на плане.
- Лиды L3 — обязательно серверная пагинация + индексы по received_at (партиции уже есть), signal_type, platform.
Порядок работ
Владелец: «сразу всё» (A+B+C) + масштаб + выбор периода. Реализация по этапам с выкатом каждого (чтобы ценность приходила частями и риск был мал):
- Кросс: выбор периода (свой диапазон) + контракт серверной пагинации.
- Этап A: экран «Лиды» + карточка лида + связка из дашборда (главное).
- Этап B: Заказ кликабельный + «Открыть всё».
- Этап C: Здоровье-ссылки + «Открыть всё» на Финансы/Клиенты + аудит масштаба
/admin/tenants.
Каждый этап: TDD (Pest + Vitest), показ глазами, выкат по «выкатывай» + prod-deploy-validator.
Известные ограничения / уточнить при реализации
- Точная связь lead↔deal (по какому полю) — уточнить по
db/schema.sql(Этап A Step 1). - Маскирование ПДн в админ-карточке лида — согласовать уровень (полный/маска).
- Экраны-приёмники для подсистем «Здоровья» (упавшие джобы/вебхуки) — есть не все; вести в ближайший существующий, недостающее — отдельный follow-up.
- «Свой период» для очень больших диапазонов на партиционированных таблицах — следить за планом запроса (партиции по received_at помогают).