Commit Graph

4 Commits

Author SHA1 Message Date
Дмитрий 849bc73290 refactor(frontend): Sprint 4 Phase B/2 — split 3 user views (audit O-refactor-04 хвост)
BillingView 416→114 (+ BalanceCard 155 + TransactionsTable 113 + InvoicesTable 90
+ billingFormatters 51 composable: formatPlain/formatCost/statusChipColor/
statusLabel/formatLabel/formatIcon/txAmountClass).
SecurityTab 354→39 (+ ChangePasswordCard 17 + TwoFactorCard 218 + RecoveryCodesCard 104
+ SessionsTable 66; auth-store читается напрямую в каждом sub-component).
RemindersView 345→183 (+ RemindersFilters 51 + RemindersList 173;
ReminderDialog уже отдельный с прошлой фазы — служит как ReminderForm).

State (`activeTab`, `editingReminder`, `deletingReminderId` в RemindersView)
остаётся в parent ради единого reload-flow + confirm-dialog'ов. Auth-store
читается напрямую в TwoFactorCard/RecoveryCodesCard через useAuthStore() —
без prop-drilling. Reminders-store читается напрямую в RemindersFilters/
RemindersList.

Все sub-components <250 строк (acceptance threshold). 3 view-shells: 114/39/183.

Регрессия: ESLint 0 + vue-tsc 0 + Vitest 416/416 + build OK 968 ms.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 04:46:14 +03:00
Дмитрий f55b91cfa4 phase2(notifications-stage3): NotificationsTab schema-aligned + prefs API
Закрывает архитектурное расхождение v1.28 — Tab сохранял prefs только
локально без API. Backend events не совпадали с handoff'ом.

Backend:
- PATCH /api/auth/me/notification-preferences под auth:sanctum.
- Replace-семантика: незадекларированные events/channels отбрасываются.
- userResource расширен: notification_preferences + sound_enabled.
- UserFactory с schema-default JSON (Eloquent не перечитывает после INSERT,
  DB-DEFAULT JSONB виден как null без явного override).
- Pest +10: 401 / replace / неизвестные events/channels отбрасываются /
  422 без prefs / sound_enabled опционален / bool-cast 1/'1' / replace-
  семантика (отсутствующие events исчезают).

Frontend:
- api/auth.ts: типы NotificationChannel/EventKey/Preferences +
  updateNotificationPreferences helper. AuthUser получил optional поля.
- NotificationsTab.vue переписан под schema:
  8 событий (new_lead/reminder/low_balance/zero_balance/topup_success/
  invoice_paid/new_device_login/marketing) × 3 канала (inapp/push/email,
  НЕ sms). Sync-init prefs (без onMounted — иначе v-if блокирует рендер
  и тесты mount-then-find падают). dirty через computed-сравнение с
  originalPrefs snapshot. save async + success/error alerts.
- SettingsView.spec.ts: legacy event-имена → schema-aligned.
- Vitest +10: 8 schema events / 3 channels (НЕ sms) / legacy отсутствуют /
  читает prefs из user / save calls API + alerts / Отменить возвращает.

cspell-words: +prefs.
PHPStan baseline регенерирован.

Pest 315/315 (+10) за 36.73 сек, 1130 assertions.
Vitest 349/349 (+10) за 20.42 сек.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 11:41:35 +03:00
Дмитрий 73e64128dc phase2(2fa-setup): wizard init+confirm+disable+regenerate в SettingsView/SecurityTab
- TwoFactorSetupController (auth:sanctum): /api/2fa/{init,confirm,disable,regenerate-recovery-codes}
- init секрет в session (не в БД), QR-URL otpauth://; confirm активирует 2FA + 8 recovery codes
- disable/regenerate требуют password-confirmation
- User.casts: totp_secret => encrypted

Schema v8.7→v8.8: users.totp_secret VARCHAR(255) → TEXT (encrypted ~256 chars)
Migration fix: explicit ALTER TABLE webhook_dedup_keys ADD FK после DB::unprepared (PDO глотал FK на partitioned)
PartitionsCreateMonthsTest fix: DETACH PARTITION + DROP вместо DROP CASCADE

Frontend: SecurityTab реальная логика (setup wizard 3 шага, disable, regenerate dialogs)

- Pest +10 (101/101 за 13.37с, 364 assertions)
- Vitest 166/166
- CLAUDE.md v1.39→v1.40, реестр v1.48→v1.49, schema v8.7→v8.8

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 04:03:02 +03:00
Дмитрий 394663597f phase2(settings): SettingsView - 8 вкладок (4 реализованы, 4 placeholder)
- SettingsView (/settings): sidebar tabs-rail (md=3, 8 v-list-item с mdi-icon)
  + content-pane (md=9 v-card outlined min-height 480px). activeTab ref
  переключает рендер вкладки.

Реализованы:
- ProfileTab: avatar 80px + 5 form-fields (имя/email disabled/телефон/TZ/роль).
- SecurityTab: 3 cards (Пароль / 2FA включена + recovery codes + Отключить /
  Активные сессии 3 mock с Завершить-btn).
- ApiTab: API-ключ password+eye-toggle + Webhook (URL + signing secret HMAC).
  Текст про дедуп (tenant_id, source_crm_id) 24ч и антифрод по phone (§10.8.1).
- NotificationsTab: матрица 8x3 (events × channels) соответствует schema v8.7
  §4 users.notification_preferences JSONB. 8 событий (new_lead, duplicate_detected,
  low_balance, tariff_charge, reminder_due, manager_assigned, webhook_failed,
  monthly_report) × 3 канала (email/sms/in_app). + sound_enabled switch.

Placeholder:
- PlaceholderTab универсальный с props title/description + v-alert «В разработке».
- Используется для Проекты / Команда / Интеграции / Тихие часы.

Маршрут /settings (meta.layout=app, lazy-import) в router + web.php.
.gitleaks.toml: settings/*.vue в allowlist (фиктивный профиль).
cspell-words.txt: смыслово.

Vitest +8 (всего 98/98 за 8.42s):
- 8 nav-tabs + все названия + дефолт «Профиль» + Проекты → «В разработке» +
  Уведомления показывает «События × каналы» + 5 событий матрицы +
  Безопасность: 2FA + сессии + API: API-ключ + Signing secret HMAC.

Регресс: lint+type+format OK; vitest 98/98; vite build (SettingsView lazy-chunk;
main app-chunk 107.85KB); story:build 17/24 за 31.7s; Pest 48/48 за 5.03s.

CLAUDE.md v1.27->v1.28, реестр Открытых_вопросов v1.36->v1.37.

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