Дмитрий
793b20a39c
feat(конкурентное поле): доводка фронта до прототипа — F1/F2/F3 + чистка M2
...
Сверка прототипа с реализацией показала расхождения — закрыты по TDD (dev, фронт):
- F1: экран «Предложения» (FieldProposalsScreen) переписан под вид «Поля» —
карточки-плитки field-shared, тип+«предложение», крупная похожесть, Сайт +
Справочник 2ГИС·Яндекс, править/удалять в карточке, массовый перенос; кнопка
«Собрать конкурентов» открывает единое окно сбора 300 ₽ вместо старого autoform.
- F2: новый дружелюбный админ-экран AdminAutopodborPricingView (правка цен
доп.услуг через PUT /api/admin/system-settings/{key} с обоснованием для аудита,
сетка лидов для справки) + маршрут /admin/autopodbor-pricing + пункт меню.
- F3: колонка «когда списывается» в панели доп.услуг биллинга.
- M2: удалён мёртвый экран FieldManualCompetitorScreen (+ спека) — на него не
было переходов; ручное добавление живёт окном на «Поле».
Тесты автоподбор+админ 43/43 зелёные, продакшен-вёрстка eslint-чистая, vite build ✅ .
НЕ на проде. M1 (18:00/21:00 МСК) — не баг, реальный инвариант продукта, не трогал.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-30 04:57:58 +03:00
Дмитрий
6536c19c96
feat(дашборд): Этап A — сквозная вложенность Лиды до источника
...
Экран «Лиды» (/admin/leads): серверный список с фильтрами (дата/канал/поставщик/
статус/поиск) + пагинация (масштаб 10⁴+ лидов). Карточка лида (/admin/leads/{id}):
полная цепочка — ОТКУДА (поставщик B1/B2/B3 + канал + источник + регион) → КОМУ
(сделки клиентов через deals.source_crm_id = supplier_leads.vid). Дашборд: drill
Лиды +топ-10 последних + «Открыть все лиды →». Nav-пункт «Лиды». ПДн-телефон
маскируется (152-ФЗ). Тесты: backend 3 + FE 5 (38 FE всего зелёные).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-28 10:14:47 +03:00
Дмитрий
d961d1617a
feat(админка): пункт «Командный центр» в левом меню
...
Дашборд не было видно в сайдбаре — уйдя с него, нельзя было быстро вернуться.
Добавлен первый nav-пункт «Командный центр» → /admin/dashboard (иконка dashboard).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-28 08:42:19 +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
Дмитрий
77e98afaa6
feat(pd): 152-ФЗ право на удаление — минимум (hole #4 )
...
Закрывает дыру #4 аудита журналирования. Объём по выбору заказчика — МИНИМУМ:
✅ Админ-API + кнопка в админке для удаления ПДн субъекта
✅ Сервис анонимизации (users + supplier_leads + deals + webhook_log)
✅ Журнал факта удаления в pd_processing_log
❌ БЕЗ формы самообслуживания на стороне субъекта
❌ БЕЗ email-подтверждения
❌ БЕЗ 30-дневного SLA (trigger deadline_at уже в схеме)
Что добавлено:
* Eloquent-модель `App\Models\PdSubjectRequest` (таблица уже была в схеме)
* Сервис `App\Services\Pd\PdErasureService::eraseSubject()`:
- cross-tenant через pgsql_supplier (BYPASSRLS)
- транзакционно (rollback при ошибке)
- users: email→erased-{id}@deleted.local , first_name→Удалено, last_name→null,
phone→+7000{id}
- supplier_leads: phone→+7000XXXXXXX, raw_payload→{erased:true}
- deals: phone→+7000XXXXXXX, contact_name→Удалено (только если есть phone)
- webhook_log: batched UPDATE по 500, raw_payload→{erased,erased_at}
- pd_processing_log запись action=deleted за каждого user/lead с
actor_admin_user_id (hash-chain audit_chain_hash триггером сам подписывает)
- При requestId — pd_subject_requests SET status=completed, completed_at,
response_text счёт
* Контроллер `AdminPdSubjectRequestsController`: index/show/store/executeErasure
* Маршруты под middleware(saas-admin): GET/POST /api/admin/pd-subject-requests,
GET /{id}, POST /{id}/erase
* Vue: `AdminPdSubjectRequestsView` (Quiet Luxury, таблица + диалог создания +
кнопка Анонимизировать для request_type=deletion); ESLint требует
v-slot:[`item.X`]= вместо #item.X для динамических slot-имён с точкой
* Пункт меню в AdminLayout.vue + route /admin/pd-subject-requests
NB: реальная схема — users.first_name/last_name/phone/email; supplier_leads
имеет только phone (нет contact_*); deals имеет phone+contact_name (нет
contact_email); webhook_log JSONB. PdErasureService адаптирован под факт.
Тесты: 12/12 passed (63 assertions, ~2.6s) — index pagination, store +
deadline trigger (+30 дней), eraseSubject анонимизация user/lead/deal/log,
pd_processing_log запись, request status→completed, отклонение
не-deletion типов, gate saas-admin, InvalidArgumentException.
Plan: docs/superpowers/plans/2026-05-23-7-holes-overview.md (#4 ).
2026-05-23 12:21:21 +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
Дмитрий
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
Дмитрий
66320166b8
feat(supplier): UI-экран «Интеграция с поставщиком» — здоровье CSV-канала
2026-05-18 17:58:53 +03:00
Дмитрий
11baaefe21
feat(admin): DEV-only баннер о застабленном auth-gate — Sprint 6 B6
2026-05-17 08:18:43 +03:00
Дмитрий
4a851a2d40
docs(admin): AdminLayout JSDoc — 4→7 nav-пунктов (Sprint 3A final review fixup)
...
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 10:15:11 +03:00
Дмитрий
eebcaf1912
docs+test(admin): ImpersonationBanner — убрать stale JSDoc + тест poll→render (B5 review fixup)
...
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 10:09:29 +03:00
Дмитрий
346c4843b0
feat(admin): ImpersonationBanner — глобальный индикатор активных сессий (audit B5)
...
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 09:59:43 +03:00
Дмитрий
9b1ac10309
feat(admin): AdminLayout nav — Тарифная сетка + Цены поставщиков (audit B4)
...
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 09:43:53 +03:00
Дмитрий
143cc458c1
fix(a11y): Q.DEFER.002 sub-B — 12 patterns fixed across 16 auth views
...
Q.DEFER.002 sub-B closure: manual Pa11y audit-pass via Playwright MCP login +
axe-core CDN inject on 16 auth-required views. Found ~13 unique violation
patterns, 12 fixed, 3 deferred to Q.DEFER.004.
ROOT CAUSE found: AdminLayout `<v-navigation-drawer color="secondary"
theme="dark">` resolved to Vuetify default-dark `secondary=#54b6b2` (Teal
mid) instead of liderraForest `#012019` теало-нуар. Switching to direct hex
preserves design intent + restores white-text contrast across all 8 admin
views (~50 nodes color-contrast violations cleared).
Patterns fixed:
1. AdminLayout sidebar palette (8 admin views):
- color="secondary" → color="#012019 " (root cause)
- .brand-sub red #b94837 → #e06155 (3.41 → 5.08)
- .nav-count gray #7a8c87 → #8a9c95 (4.26 → 5.34)
- <v-list nav> + role="navigation" + aria-label (aria-required-children
fix: <v-list role=list> had [role=link] children — undefined для list)
2. DashboardBalance .runway-bar — role="img" (aria-prohibited-attr fix)
3. DashboardKpiRow .delta-up — #2e8b57 → #1b6e3b (4.27 → 6.25)
4. TransactionsTable .tx-amount-up — #2e8b57 → #1b6e3b (same fix)
5. RemindersList .empty-hint — #9a9690 → #6b6356 (2.98 → 5.74; +liderra-muted alignment)
6. KanbanView .kanban-board — tabindex="0" role="region" aria-label
(scrollable-region-focusable fix)
7. ProjectCard:
- .v-progress-linear + :aria-label="Прогресс дневной нормы: N%"
- icon menu :aria-label="Меню действий проекта «...»"
- bulk-select .card-check input :aria-label="Выбрать проект «...»"
8. useStatusPill in_progress #3F7C95 → #2A5A6E (4.07 → 6.11);
useStatusPill.spec.ts sync
9. ProjectsView toolbar select-all input aria-label
10. AdminTenants impersonate v-btn aria-label
11. Global app.css:
`.v-messages, .v-field-label { --v-medium-emphasis-opacity: 0.7; }`
Vuetify default ~0.52 → rendered #7a7a7a/#767471 fails 4.20-4.29:1;
0.7 → rendered ≈#595959 → 7.9:1+ passes WCAG AA.
Re-verified post-fix via axe-core on all affected views: all clean except
DEV-only `.dev-index-num` chip (tree-shaked в prod, not a real violation).
Vitest verified post-fix: 79 files / 614 passed / 3 skipped / 0 failed
(baseline preserved).
3 patterns deferred to Q.DEFER.004:
- DealsTable VDataTable show-select bulk-checkboxes (6 nodes) — Vuetify
slot rewrite needed
- AdminSupplierPrices 9 form inputs — v-text-field/v-switch label props
- Vuetify v-tooltip eager-mount aria-tooltip-name — library-level cosmetic
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-12 22:09:48 +03:00
Дмитрий
0245f12b51
chore(dev): inject DevIndexBadge for visual feature feedback on localhost
2026-05-12 04:45:18 +03:00
Дмитрий
768628d914
phase2(7-features): bulk-actions / new-deal / tenant-card / system-edit / webhook / smart-filters / impersonation-list
...
7-фичный auto-mode пакет согласно «карте что осталось» (после v1.54).
(1) Bulk-actions DealsView:
- dealsState reactive-копия MOCK_DEALS (deep-clone) для безопасного bulk-edit.
- Bulk-bar (sticky, теало-нуар, theme=dark) при selected.length > 0:
count + Сменить статус (v-menu × 14 lead_statuses) + Экспорт (snackbar) +
Удалить (v-dialog confirm) + ✕ clear.
- На production: smart status-transition с проверкой allowed-переходов;
soft-delete (архив 30 дней); реальный CSV/XLSX export через xlsx-lib.
(2) NewDealDialog (used in DealsView+KanbanView):
- 6 полей: name/phone/project (MOCK_PROJECTS) / manager (MOCK_MANAGERS) /
cost / status (default 'new' или presetStatus). Phone-валидация ≥10 цифр.
- emit('created', deal) → DealsView push в начало dealsState; KanbanView push
в правильную колонку по statusSlug + totalDeals++.
(3) AdminTenantDetailView (/admin/tenants/:code):
- 4 KPI cards (Баланс/runway / Тариф+MRR/мес / Лиды сегодня+неделя+месяц /
Средняя цена). 4 v-tabs: Финансы (balance-history) / Пользователи /
Проекты / Активность с event-кодами.
- Кнопка «Войти как клиент» (использует ImpersonationDialog из v1.54).
404-fallback. composables/mockTenantDetail.ts с expandTenantDetail.
- AdminTenantsView получил @click:row → router.push.
(4) Edit-flow AdminSystemView (audit-log + 2-step):
- Backend: SystemSetting + SaasAdminAuditLog Eloquent (append-only,
payload_before/after JSONB casts).
- AdminSystemSettingsController с GET (list) + PUT (update в DB::transaction
+ INSERT в saas_admin_audit_log; hash-chain trigger BEFORE INSERT
заполняет log_hash).
- Type-validation: int/decimal/bool/json. Reason ≥30 chars. No-op → 422.
- Frontend SystemSettingEditDialog — 3-step (edit → confirm с diff
before/after → done).
(5) Webhook receive endpoint (POST /api/webhook/{token}):
- WebhookReceiveController::receive. Token = tenants.webhook_token.
- 404 unknown / 422 bad payload / 202 success + dispatch ProcessWebhookJob.
- Stub-INSERT в webhook_log через DB::table обёрнут в DB::transaction +
SET LOCAL app.current_tenant_id для RLS.
- CSRF-исключение для api/webhook/* в bootstrap/app.php.
- На prod: + HMAC X-Webhook-Signature + per-token rate-limit.
(6) Smart-filters:
- DealsView: multi-select v-select Проект+Менеджер с auto availableProjects/
availableManagers computed.
- AdminTenantsView: filterStatuses (4 STATUS_OPTIONS) + filterTariffs
(computed availableTariffs).
- Кнопка «Сбросить» появляется только когда фильтры активны.
(7) AdminImpersonationView (/admin/impersonation):
- Backend +2 GET endpoints: /active (used_at != null AND session_ended_at
== null) + /recent (last 20 завершённых с duration_seconds через
abs(diffInSeconds) — Carbon signed по умолчанию).
- ImpersonationToken получил belongsTo(Tenant).
- Frontend view: 2 секции (Активные с end-кнопкой / Недавно завершённые
read-only) + refresh + onMounted load.
- Маршрут /admin/impersonation + 5-й nav-пункт «Impersonation» в AdminLayout.
Vitest +48 (всего 238/238 за 15.31 сек).
Pest +16 (всего 136/136 за 15.8 сек, 495 assertions).
PHPStan baseline регенерирован (0 errors после фикса nullsafe.neverNull).
Регресс: lint+type-check+format ✅ ; vite build 937 ms; Pint+PHPStan passed;
Pest 136/136. Реестр v1.54→v1.55, CLAUDE.md v1.45→v1.46.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-09 05:33:21 +03:00
Дмитрий
1d1353931d
phase2(user-chip): реальный user в AppLayout/AdminLayout + Logout-menu
...
- AppLayout: userInitials/userShortName computed из auth-store, fallback цепочка → email → '?' / 'Гость'
- AdminLayout: тот же паттерн с админ-defaults 'АО' / 'Админ Оператор'
- v-menu offset=8 на user-chip: email + Настройки/Выйти из админки + Выйти
- handleLogout async: auth.logout() (swallows API errors) → router.push('/login')
- Vitest +3 в AppLayout.spec.ts (всего 145/145): store-mock + null-user + email-fallback
- AppShell.spec.ts получил createPinia в plugins
- Регресс: lint+type+format OK, vitest 145/145 за 11.01с, build 855ms, story:build 21/28 за 32.11с, Pest 67/67 за 6.16с
- CLAUDE.md v1.34→v1.35, реестр v1.43→v1.44
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-08 20:29:05 +03:00
Дмитрий
da65cf4bf7
phase2(admin): AdminLayout + AdminTenantsView - админка SaaS (12/13 концептов)
...
- AdminLayout: отдельный sidebar теало-нуар с под-брендом ADMIN (red error
10px JBM uppercase) + 4 nav (Тенанты 142 / Биллинг / Инциденты 3 / Система) +
topbar с crumb «Админка → currentPage» + admin-user-chip с error-color avatar.
- AdminTenantsView (/admin/tenants): page-head + 5-stats + Экспорт +
search/Статус/Тариф фильтры + v-data-table 7 колонок (Тенант с двухстрочным
name+inn / Статус-chip 4 цвета / Тариф / Баланс ₽ с error-color при <0 /
Желаем×факт / MRR с «—» / Активность).
- mockTenants.ts соответствует schema v8.7 §3: 4 статуса × 5 тарифов, 7 mock
с разнообразием (active/trial/overdue/suspended) + AdminStats (142/128/9/5/
1 248 600 ₽).
- AdminPlaceholderView универсальный для Биллинг/Инциденты/Система с
описаниями ссылающимися на schema v8.7 (incidents_log §9 / system_settings §10).
- AppShell расширен meta.layout='admin'. Router: /admin redirect на /tenants +
4 admin-route'а с lazy-imports. Web.php fallback покрывает /admin/*.
- cspell-words.txt: Екб.
Vitest +11 (всего 129/129 за 10.02s):
- заголовок + 5 stats (regex nbsp в 1 248 600 ₽) + 7 columns + 7 rows +
Окна Москва ИНН + overdue −1 200 + trial 4 дня + suspended + search filter
«Натяжные» → 1 row + Экспорт/Статус/Тариф кнопки.
Регресс: lint+type+format OK; vitest 129/129; vite build (admin views
в lazy-chunks; main 104.99 KB); story:build 21/28 за 30.32s; Pest 48/48 за 4.89s.
CLAUDE.md v1.30->v1.31, реестр Открытых_вопросов v1.39->v1.40.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-08 19:23:28 +03:00