Commit Graph

193 Commits

Author SHA1 Message Date
Дмитрий c025ec4b69 feat(projects-bulk): update_days handler with bitmask OR/AND-NOT
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 14:52:23 +03:00
Дмитрий 8220a85a5d feat(projects-bulk): update_regions handler with bitmask OR/AND-NOT
Refactor ProjectService::bulkAction to accept full payload array and
return structured {updated, skipped, warnings}. Add bulkUpdateRegions
using PG raw bitmask expr (region_mask | add) & ~remove & 255.
Add stubs for bulkUpdateDays/bulkUpdateLimit (Tasks 3-4). Update
controller to pass merged payload and return service result directly.
Un-todo Task-1 region validation test; add regions bitmask test (18/20).
Update phpstan-baseline: actingAs count 5->6, restore match.unhandled.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 14:48:10 +03:00
Дмитрий 08f02100fe fix(projects-bulk): treat empty scope.filter as valid scope
Replace !empty() check with has()+is_array() so scope:{filter:{}} is
accepted as "all projects" rather than rejected as missing selection.
Expand scope.filter to IDs in the controller (500-row limit guard) so
the service receives a typed array[]; add Pest coverage for this case.
Update phpstan baseline count for new actingAs() call.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 14:43:45 +03:00
Дмитрий 40202caf34 feat(projects-bulk): extend validation for 6 actions + scope
- BulkProjectActionRequest: add update_regions/update_days/update_limit actions, scope.filter, withValidator for ids-or-scope + delta/replace mutual exclusion
- ProjectBulkActionsTest: 4 new tests (3 pass, 1 todo pending Task 2 service handler)
- ProjectsActionsTest: update > 100 ids limit test to match new max:500
- phpstan-baseline: add 4 actingAs false-positive entries for new test file

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 14:38:59 +03:00
Дмитрий 5c8ad2738a feat(layout): dark topbar + sidebar cleanup + DevIndexBadge moved below
Sidebar: убраны Менеджеры/Напоминания; Работа в порядке
Проекты/Сделки/Канбан/Дашборд; Команда — только Настройки;
снят useRemindersStore (был только под reminders badge).

Topbar: тёмный фон linear-gradient(noir → #04261E) совпадающий
с sidebar #1271; убран breadcrumb «Рабочая область»;
v-toolbar__content padding-left:240 (не уходит под sidebar).

DevIndexBadge: top:64 (ниже топбара, не перекрывает user-chip).

Vitest AppLayout 15/15 PASS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 14:32:03 +03:00
Дмитрий 9a7615b257 fix(dev-indices): Esc pause-hover + skip inert Vue compiler tags
#1 (review-Important) — Esc now also calls pauseHover(2000) so the next
mousemove doesn't re-target the cursor element within 16ms. User gets
2 seconds to move off before hover re-engages.

#4 (review-Important) — Plugin walker now skips data-dx injection for
inert Vue compiler tags (template / slot / component / Transition /
TransitionGroup / Suspense / KeepAlive) but still recurses into their
children with the tag preserved in ancestor chain (keeps descendant
signatures stable). Manifest regenerated — no more phantom IDs that
reference no-DOM-element nodes.

Other review findings (CI integration, save-amplification, code-style
polish) skipped: this feature is temporary, will be removed at final
release.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 12:32:15 +03:00
Дмитрий e3804cd12b feat(dev-indices): CLI 'npm run dx <id>' for manifest lookup
Prints file:line/tag/text/parent-chain/signature/created for any manifest
entry. Handles deleted IDs (tombstones) with separate message format.
Exit codes: 0=found, 1=not-found-or-no-manifest, 2=usage-error.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 12:07:13 +03:00
Дмитрий d238ca5f4a feat(dev-indices): overlay Alt-keys (up/down) + Alt+Shift+I toggle + mini-badges
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 12:05:53 +03:00
Дмитрий d8c33b4cd6 feat(dev-indices): DevIndexOverlay (hover badge + click-copy + Esc + AppShell mount)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 12:02:38 +03:00
Дмитрий 901530ae41 feat(dev-indices): useDevIndices composable (state + DOM walk)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 11:57:35 +03:00
Дмитрий c771192db2 feat(dev-indices): JSON Schema for manifest validation
IDE auto-completion/validation for app/dev-indices.json via the $schema
reference in the manifest header.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 11:54:56 +03:00
Дмитрий b182dae89b feat(dev-indices): register plugin in vite.config (dev-only + Vitest guard)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 11:54:13 +03:00
Дмитрий f27ccc0081 feat(dev-indices): Vite plugin core (transform + magic-string injection)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 11:50:15 +03:00
Дмитрий 8edd720395 feat(dev-indices): signature module (structural + data-dev-name escape hatch) 2026-05-12 11:46:20 +03:00
Дмитрий 1f834bfac3 test(dev-indices): cover loadManifest error branches + markDeleted no-op 2026-05-12 11:43:59 +03:00
Дмитрий baf51bd2cf feat(dev-indices): manifest IO module (types + load/save/lookup/tombstones)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 11:37:36 +03:00
Дмитрий f4ec5dcafa fix(redesign): sidebar position:fixed + main padding-left — restore main content visibility
Hotfix: после Task 12 замены v-navigation-drawer на plain <aside> sidebar остался position:static и толкал v-main в flow ниже (y=901), весь контент уезжал за viewport. Добавлен .ld-sidebar position:fixed top:0 left:0 height:100vh z-index:1006 + .app-main padding-left:232px. Verified via Playwright snapshot — Dashboard KPI/charts отрисованы корректно.
2026-05-12 10:48:15 +03:00
Дмитрий e2669270f3 feat(redesign): Task 17 — ProjectCard tokens (hover lift + JetBrains Mono numerics) 2026-05-12 10:22:36 +03:00
Дмитрий 22e6bdf8b8 feat(redesign): Task 16 — KanbanView StatusPill + hover lift (motion #4)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 10:19:21 +03:00
Дмитрий 2f46a3e5ec feat(redesign): Task 15 — DealsView filterbar + density + StatusPill + hover lift (motion #2,#4) 2026-05-12 10:13:19 +03:00
Дмитрий 35662f7b56 feat(redesign): Task 14 — DashboardView KPI count-up (motion #1) + live pulse 2026-05-12 10:06:08 +03:00
Дмитрий a09434eca0 feat(redesign): Task 13 — page transition wiring (Vue Transition + CSS fadeup, motion #6) 2026-05-12 09:59:22 +03:00
Дмитрий 3f956224bd feat(redesign): Task 12 — AppSidebar двухтоновый shell + ⌘K stub + active marker (motion #7) 2026-05-12 09:54:54 +03:00
Дмитрий 2707ff64ab feat(redesign): Task 11 — DensityToggle component (compact/comfortable + persist) 2026-05-12 09:49:37 +03:00
Дмитрий 0b2ec5b802 feat(redesign): Task 10 — FilterChip component (label + count + active states) 2026-05-12 09:47:02 +03:00
Дмитрий 52cc64c9e6 feat(redesign): Task 9 — Kbd component (⌘K, Esc badges; light+dark variants) 2026-05-12 09:45:22 +03:00
Дмитрий ff3bc8bcc1 feat(redesign): Task 8 — StatusPill component + 14-variant Histoire story 2026-05-12 09:43:02 +03:00
Дмитрий 7322c7f33a feat(redesign): Task 7 — useDensity composable (localStorage + rowHeight) 2026-05-12 09:37:43 +03:00
Дмитрий eda13679b4 feat(redesign): Task 6 — useCountUp composable (RAF tween + prefers-reduced-motion) 2026-05-12 09:34:55 +03:00
Дмитрий cdd1b5efdb feat(redesign): Task 5 — useStatusPill composable (14 slugs из db/schema.sql) 2026-05-12 09:29:53 +03:00
Дмитрий ea4570dafe feat(redesign): Task 4 — extend Vuetify theme (12 colors) + global component defaults 2026-05-12 09:27:22 +03:00
Дмитрий b858df569e feat(redesign): Task 3 — motion.css (5 keyframes + reduced-motion wrapper + utilities) 2026-05-12 09:23:50 +03:00
Дмитрий baf27bd02d feat(redesign): Task 2 — typography.css (Inter variable + JetBrains Mono + tnum) 2026-05-12 09:20:13 +03:00
Дмитрий 688d9cfb24 feat(redesign): Task 1 — tokens.css (12 colors + spacing + radii + shadows) 2026-05-12 09:15:29 +03:00
Дмитрий 0245f12b51 chore(dev): inject DevIndexBadge for visual feature feedback on localhost 2026-05-12 04:45:18 +03:00
Дмитрий 76b1562593 feat(frontend): Plan 5 Task 11 — polling integration (setTimeout-recursion + backoff) 2026-05-11 19:44:56 +03:00
Дмитрий 1c3989a6df feat(frontend): Plan 5 Task 10 — EditProjectDialog wrapper + BulkActionsBar + 7 tests 2026-05-11 19:41:53 +03:00
Дмитрий 92082606e3 feat(frontend): Plan 5 Task 8 — ProjectsView + projectsStore (no polling) + 9 tests 2026-05-11 19:38:59 +03:00
Дмитрий 8bc7838f0c feat(frontend): Plan 5 Task 9 — NewProjectDialog (3 tabs Site/Call/SMS) + story 2026-05-11 19:31:26 +03:00
Дмитрий c9ee8d866e feat(frontend): Plan 5 Task 7 — router + nav + regions + ProjectCard + story 2026-05-11 19:31:23 +03:00
Дмитрий 458fa0b84d feat(projects): Plan 5 Task 6 — destroy + sync + toggle-active + bulk endpoints
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 19:06:07 +03:00
Дмитрий 6238b8b580 feat(projects): Plan 5 Task 5 — update + UpdateProjectRequest + resync trigger
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 19:00:39 +03:00
Дмитрий 85f8e9e7a0 feat(jobs): Plan 5 Task 4 — SyncSupplierProjectJob full impl + ensureSupplierProject
- SyncSupplierProjectJob: replace stub with full implementation
  (tries=3, backoff=[15,60,300]s; resolvePlatforms uppercase B1/B2/B3;
  buildUniqueKey site/call→signal_identifier, sms B2→sender+keyword, B3→sender;
  column name via strtolower($platform) to match schema snake_case)
- SupplierPortalClient: drop final modifier (Mockery testability);
  add ensureSupplierProject() idempotent lookup-or-create wrapper
- Tests: 6 passing (site/call/sms-with-kw/sms-no-kw/exception/partial-failure);
  DI fix via dispatchJobSync() helper resolving mock from container;
  uppercase platform fixtures matching CHECK constraint B1/B2/B3;
  last_error column absent from schema — partial-failure test uses sync_status only
- phpstan-baseline.neon: add $this->mock() Pest TestCase inference gaps

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 18:52:51 +03:00
Дмитрий 2ffbb49faa fix(projects): Plan 5 Task 3 code-review fixes (2 Important + 2 Minor)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 18:38:52 +03:00
Дмитрий 9d2e7270de feat(projects): Plan 5 Task 3 — store + StoreProjectRequest + ProjectService::create
- StoreProjectRequest: 3-way conditional validation (site domain regex, call 7\d{10}, sms senders required)
- ProjectService::create(): max_projects limit check via Tenant.limits JSONB + dispatch SyncSupplierProjectJob
- ProjectController: constructor DI + store() method returning 201
- SyncSupplierProjectJob: stub (Task 4 полная реализация)
- POST /api/projects route inside auth:sanctum+tenant group (name projects.store)
- Migration add_limits_to_tenants: JSONB DEFAULT '{}' per-tenant limits column
- Tenant model: limits added to fillable + casts as array
- schema.sql/CHANGELOG: tenants.limits documented in v8.20
- phpstan-baseline: +8 actingAs entries for new test file
- Quirk: region_mode in request uses 'include'/'exclude' (schema CHECK) not 'all'/'whitelist' (plan spec typo)
- Quirk: Project::first() → Project::where('signal_identifier','x.ru')->latest()->first() (no RefreshDatabase, persistent test DB)
- 8/8 ProjectsStoreTest passed; 699/706 total (4 pre-existing failures unchanged)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 18:29:54 +03:00
Дмитрий e242e7d7fc fix(projects): Plan 5 Task 2 code-review fixes (2 Important + 2 Minor)
I-1/M-1: introduce resolvedSupplierProjects() private helper on Project
model; rewrite aggregateSyncStatus(), aggregateLastSyncedAt(),
getSupplierLinks() to read from eager-loaded supplierB1/B2/B3 relations
instead of SupplierProject::find() — eliminates up to 120 SELECTs/page.

I-2: aggregateLastSyncedAt() now uses sortBy(timestamp) instead of
Collection::min() on Carbon objects (string-comparison was unreliable).

M-2: add explanatory comment on intval+array_filter silent-drop behaviour
in the ?ids batch-fetch path.

M-3: new test — ?ids batch silently excludes foreign-tenant project IDs.
M-4: new test — show returns 200 for archived project (read preserved).

PHPStan baseline updated: 2 new test functions raise actingAs() count 7→9.
Tests: 9/9 passed (33 assertions). Larastan: 0 errors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 18:15:36 +03:00
Дмитрий 35310b5517 feat(projects): Plan 5 Task 2 — index expanded (filters/search/pagination/ids) + show
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 18:08:01 +03:00
Дмитрий 622773f929 fix(db): Plan 5 Task 1 code-review fixes (2 Important + 2 Minor)
I-1: scopeActive docblock — явное предупреждение что scope НЕ фильтрует
     is_active; приостановленные проекты попадают; пример комбинирования.

I-2: migration down() — комментарий об асимметрии с up() и риске drift
     с schema.sql v8.20 при случайном rollback.

M-1: archived_at перемещён в $fillable на позицию сразу после is_active
     (lifecycle-state рядом с lifecycle-state, как указано в плане).

M-2: CHANGELOG header счётчик восемнадцать → девятнадцать записей.

Tests: ArchivedAtTest 2/2 PASS (4 assertions, 472 ms). No behavior change.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 17:51:33 +03:00
Дмитрий 144d4cbb98 feat(db): Plan 5 Task 1 — schema delta v8.19 → v8.20 + Project.archived_at
Schema delta (1 правка в db/schema.sql):
- projects + archived_at TIMESTAMPTZ NULL — soft archive flow (отличие от
  is_active=false который = pause).

Метрики: 62 базовых таблицы / 117 индексов / 39 RLS (без изменений).

Сопутствующие правки:
- db/CHANGELOG_schema.md — v8.20 entry.
- app/Models/Project — fillable+casts: archived_at datetime + scopeActive +
  scopeArchived (whereNull/whereNotNull archived_at).
- Migration guard: Schema::hasColumn() проверка перед ALTER TABLE — предотвращает
  "duplicate column" после migrate:fresh (schema.sql v8.20 уже содержит колонку).

Tests:
- ArchivedAtTest.php — 2 it() блоков: archived_at колонка timestamptz + fillable/casts.
- pest --filter=ArchivedAtTest: 2/2 PASS (4 assertions, 485 ms).
- Full suite: 689/686+3 skipped/0 failed (2094 assertions, 84638 ms).

Quirk зафиксирован: Schema::getColumnType('projects', 'archived_at') → 'timestamptz'
(не 'timestamp') — PostgreSQL TIMESTAMPTZ → Doctrine/Laravel native type string.
План spec ожидал 'timestamp', скорректировано в тесте с комментарием.

Spec: docs/superpowers/specs/2026-05-10-claude-brain-extraction-design.md (Plan 5).
Plan: docs/superpowers/plans/2026-05-10-claude-brain-extraction.md Task 1.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 17:39:46 +03:00
Дмитрий 4bc488e940 fix(admin): AdminPricingTiersView strip ISO-suffix from effective_from caption
Caption "(с 1970-01-01T00:00:00.000000Z)" → "(с 1970-01-01)".
Slice on optional-chain in template; UI smoke verified via Playwright,
Vitest tests/Frontend/AdminPricingTiersView.spec.ts 5/5 passed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:37:59 +03:00