Commit Graph

200 Commits

Author SHA1 Message Date
Дмитрий b1c3efa1e1 fix(projects): #909 СОЗДАТЬ кнопка — apiClient + interim A regions
Root causes:
1. Default axios без withXSRFToken не отправлял CSRF header → 419 silent
   fail (catch ловил только 422).
2. PDD regions UI (commits 4f60add..f982046) использовал 32-bit маску,
   несовместимую с schema's 8-битным CHECK chk_projects_region_mask_range
   → 500 silent fail.

Changes (NewProjectDialog.vue):
- Replace default axios import с apiClient + ensureCsrfCookie +
  extractErrorMessage из api/client.ts (same pattern как NewDealDialog).
- await ensureCsrfCookie() перед mutating; apiClient.post/patch.
- Remove regions <v-autocomplete> + selectedRegions ref + inverted
  region_mode watcher (interim A — proper 89-codes реализация в Plan 6).
- Add general error banner для non-422 ошибок (419/401/500/network).
- form.region_mask=255 + region_mode='include' (schema default = вся РФ).

Changes (EditProjectDialog.spec.ts):
- Switch mock с default axios на apiClient (cascading from above).

Verified: Pest 742/739/3sk/0, Vitest 758/3sk/0, vue-tsc 0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 19:28:33 +03:00
Дмитрий f9820460fa feat(pdd): regions multi-select autocomplete + bitmask binding
Реализует Out-of-plan «Region multi-select autocomplete» из parent PDD spec.
Spec: 4f60add. Plan: 159ed3e.

Component (ProjectDetailsDrawer.vue):
- import REGIONS из constants/regions
- selectedRegions: Ref<number[]> + selectableRegions (filter code !== 0
  для исключения «Вся РФ» sentinel — fixes latent NewProjectDialog bug)
- maskToCodes(mask): reverse-decompose bits 1..31
- reseedFromProject: +selectedRegions.value = maskToCodes(form.region_mask)
- watch(selectedRegions): forward-encode mask + mode (include при empty, exclude иначе)
- Template: v-autocomplete multi+chips+clearable между Лимитом и Днями

Tests (ProjectDetailsDrawer.spec.ts): 17 passed (14 prior + 3 new):
- renders region chips when project has non-zero region_mask
- selecting regions encodes mask + sets mode=exclude on save
- clearing all regions resets mask=0 + mode=include on save

NB: config.global.plugins = [createVuetify()] добавлен в spec.ts — v-autocomplete
требует Vuetify defaults provide context. Все 17 PDD tests + 8/1sk ProjectsView
integration green (0 regressions).

Backend без изменений (region_mask + region_mode payload уже в Task 5 onSave).
2026-05-14 17:51:56 +03:00
Дмитрий c5814ecc9c test(projects): integration tests for drawer × bulk-bar mutual exclusion 2026-05-14 17:14:12 +03:00
Дмитрий bfdab40d88 feat(projects): integrate ProjectDetailsDrawer + swap bulk-bar condition >=2
Task 8 of project-details-drawer plan (2026-05-14):
- ProjectsView.vue: import ProjectDetailsDrawer + computed
  - singleSelectedProject computed (Project|null when selectedIds.size === 1)
  - onDrawerClose/onDrawerSaved handlers (clearSelection / fetch)
- Template: BulkActionsBar condition > 0 → >= 2 (mutual exclusion with drawer)
- Template: mount <ProjectDetailsDrawer> with :project / @close / @saved bindings
- Template: .has-drawer class on .projects-view root when single selected
- Style: .projects-view padding-right 480px transition for push effect
- Test: ProjectsView.spec.ts pre-existing 'shows BulkActionsBar' case updated
  to assert >=2 contract (selects 2 projects); 14 PDD tests + 3 view tests
  + 1 skip + toolbar tests all green

Vitest: 3 files / 20 passed / 1 skipped / 0 failed
2026-05-14 15:02:33 +03:00
Дмитрий ae6a370b06 feat(pdd): Delete button with confirm + archive + close 2026-05-14 14:54:55 +03:00
Дмитрий 8aca5b1ba9 feat(pdd): Pause/Resume button with toggleActive + dynamic label 2026-05-14 14:48:24 +03:00
Дмитрий 86b18fc396 feat(pdd): Save action — PATCH /api/projects/{id} + 422 errors 2026-05-14 14:41:28 +03:00
Дмитрий f47ace40f4 feat(pdd): reseed form on project.id change 2026-05-14 14:35:54 +03:00
Дмитрий 66d0d48adf feat(pdd): emit close on X/Cancel/ESC 2026-05-14 14:28:49 +03:00
Дмитрий fa01951d27 feat(pdd): render project name/limit/days form fields 2026-05-14 14:21:07 +03:00
Дмитрий 7d77187eb3 test(pdd): scaffold ProjectDetailsDrawer + null-project no-open test 2026-05-14 14:13:52 +03:00
Дмитрий c5c0e76950 test(coverage): close F-COV-01/02/03 — ReminderDialog + AdminLayout + api/admin
Closes Audit #2+#3 P2 carryforward triplet (low-coverage files at risk
of silent regression).

Coverage results (Vitest --coverage --coverage.include per-file):

| File | Stmts before | Stmts now | Δ |
|---|---|---|---|
| ReminderDialog.vue | 0% | 95.38% | +95 pp |
| AdminLayout.vue | 9.09% | 95.45% | +86 pp |
| api/admin.ts | 11.53% | 100% | +88 pp |

Branches/Funcs deltas (subagent reports):
- ReminderDialog: Branch 0→97.56%, Funcs 0→85.71%, Lines 0→96.61%
- AdminLayout: Branch 0→90%, Funcs 0→90%, Lines 9.09→94.73%
- api/admin: Branch 0→100%, Funcs 27.27→100%, Lines 11.53→100%

Approach: TDD via @vue/test-utils + Vuetify global plugin + vi.mock for
store/api. Three parallel subagents (general-purpose), each focused on
single target — no production code changes, only test infrastructure.

Coverage areas:
- ReminderDialog (19 specs): rendering, watch(dialogOpen) populate/reset,
  submit create-mode happy + 3 errors, submit edit-mode happy + 1 error,
  cancel, common validation paths
- AdminLayout (16 specs): brand block, 5 nav items, count badges (142/3),
  breadcrumb per route (5 cases + fallback), userInitials computed (4
  cases incl. fallback), userShortName (4 cases), handleLogout call-order,
  active state, aria-label
- api/admin (18 specs): 11 exported functions × happy-path; 2 encodeURI
  edge cases; 4 ensureCsrfCookie call-order verifications via
  invocationCallOrder; 2 error-propagation tests

Verification (full sweep after merge):
- Vitest: 91 files / 736 passed / 3 skipped / 0 failed (+3 files, +53 specs
  from Audit #3 baseline 88/683/3sk)
- Pest --parallel: 742/739/3sk/0 (identical to baseline, 0 regressions)
- Vite build: 2.03s
- vue-tsc: 0 errors
- ESLint: 0 errors

Plan: docs/superpowers/plans/2026-05-14-audit3-deferred-fixes.md Task 3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 08:37:26 +03:00
Дмитрий 08605cf640 fix(tests): Bus::fake partial + session mock — close quirk #72
CsvReconcileJobTest used Bus::fake() (all jobs), silencing dispatch_sync of
RefreshSupplierSessionJob when a parallel afterEach wiped supplier:session.
Now: Bus::fake([RouteSupplierLeadJob::class]) + anonymous mock that re-puts
the session in handle(), making race-window recovery deterministic.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 05:35:06 +03:00
Дмитрий 9a45346205 fix(tests): RefreshDatabase on LookupsTest + ProjectExtensionsTest — close quirk #62
DatabaseTransactions did not prevent cross-session data accumulation in
liderra_testing; count assertions drifted (1465 managers, 519 projects).
RefreshDatabase runs migrate:fresh once per session (RefreshDatabaseState::migrated)
so stale data is wiped at start of each composer test run.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 05:29:34 +03:00
Дмитрий e280edd431 style(frontend): apply prettier --write — fix formatting drift
4 files reformatted (import list expansion, line-length wrapping).
Vitest 88/683+3sk green.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 13:30:51 +03:00
Дмитрий f2627e4d3e test(router): Q.DEFER.003 sub-C — 5 integration tests for guard branches
Coverage uplift router/index.ts от 33% Stmts / 7% Funcs к ~85% Funcs:
- authenticated /login (guestOnly) → /dashboard redirect
- authenticated /dashboard passes requiresAuth
- /no-such-path → 404 catch-all
- /admin → /admin/tenants redirect
- /reset/:token param exposure

Refactored vi.mock me() для conditional resolve/reject per test.
2026-05-13 01:55:06 +03:00
Дмитрий c09bff3799 test(security): Q.DEFER.003 sub-B — TwoFactorCard 9 own-spec tests
Coverage uplift от 28% to 80%+: enable button visibility / disable button
visibility / chip status / setup wizard openSetup→confirm→codes / invalid
code error / disable flow valid+invalid password / closeSetup state reset.
vi.mock authApi для 3 endpoint'ов (init/confirm/disable).
2026-05-13 01:46:30 +03:00
Дмитрий 918c962b26 test(security): Q.DEFER.003 sub-B — RecoveryCodesCard 6 own-spec tests
Coverage uplift от 28% to 70%+ (auth-gated visibility / dialog flow /
confirmRegen success+error / closeRegen reset). vi.mock authApi
для изоляции; VDialog stub'аем для DOM unit-test (избегаем teleport).
2026-05-13 01:41:27 +03:00
Дмитрий 4c6d593776 test(security): Q.DEFER.003 sub-B — ChangePasswordCard 3 own-spec tests
Placeholder card (17 lines, static UI) — add minimal coverage for heading,
last-change hint, and button rendering. Closes coverage debt от 0% Stmts.
2026-05-13 01:36:22 +03:00
Дмитрий c8005e0cfc fix(a11y): Q.DEFER.004 sub-B — AdminSupplierPricesView 9 inputs aria-label
3 supplier rows × 3 form controls (cost_rub v-text-field +
quality_score v-text-field + is_active v-switch) = 9 nodes без label —
axe-core критичная label violation.

Fix: :aria-label='${field} для ${supplier.name}' (e.g. 'Cost (₽) для B1 — Сайты и Звонки').

Test coverage: AdminSupplierPricesView.spec.ts 4-й spec проверяет все 9 ожидаемых
aria-label через DOM query.
2026-05-13 00:35:05 +03:00
Дмитрий d9fc3d92e4 fix(a11y): Q.DEFER.004 sub-A — DealsTable show-select bulk-checkbox aria-label
VDataTable show-select prop генерировал unlabeled checkbox per row + select-all
header — axe-core критичная label violation (6 nodes на demo seed).

Override через Vuetify 3.12 typed slots:
- header.data-table-select → aria-label='Выбрать все сделки'
- item.data-table-select → aria-label='Выбрать сделку «{{name}}»' (per row)

Test coverage: tests/Frontend/DealsTable.spec.ts (2 specs).
2026-05-13 00:28:39 +03:00
Дмитрий 95f5f94a6b test(api): Q.DEFER.003 sub-A — 43 unit tests for api/*.ts layer
User chose (A) api/* unit tests first (highest ROI per blocked.md). 5 new
spec files covering auth/deals/notifications/reminders/reports api modules.

- auth-api.spec.ts (13 tests): login/register/me/logout/verifyTwoFactor/
  useRecoveryCode/twoFactorInit/Confirm/Disable/RegenerateRecoveryCodes/
  forgotPassword/resetPassword/updateNotificationPreferences
- deals-api.spec.ts (12 tests): createDeal/bulkDelete/bulkRestore/update/
  transition/exportCSV/exportXLSX/getDeal/listDeals×2/listManagers/
  listProjects
- notifications-api.spec.ts (6 tests): listNotifications×3 (unreadOnly
  variants)/markRead/markAllRead/delete
- reminders-api.spec.ts (6 tests): listReminders×2/create/update/complete/
  delete
- reports-api.spec.ts (6 tests): listReportJobs×2/create/retry/cancel/delete

Approach: vi.mock('../../resources/js/api/client') replaces apiClient with
{get,post,patch,delete} mocks + ensureCsrfCookie mock. Each test verifies:
(1) correct HTTP method, (2) correct URL, (3) correct params/body
(camelCase→snake_case mapping for query params), (4) data unwrap from
wrapper objects ({user}/{deal}/{job}/{reminder}/{managers}/{projects}),
(5) ensureCsrfCookie called for mutating endpoints.

Vitest delta: 614 → 657 passed (+43 / 0 failed); 79 → 84 files (+5).
3 skipped unchanged. Q.DEFER.003 sub-B (security cards) + sub-C (router
guards) remain deferred — sub-A api/* was highest ROI per blocked.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 22:14:51 +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
Дмитрий 245b76ec43 test(frontend): fix 17 ESLint errors + TwoFactorView router stub
ESLint emitted 17 errors in tests/Frontend/* (production code clean):
- 13× @typescript-eslint/no-explicit-any in axios mock casts
  (BulkActionsBar, ProjectsView, projectsStore specs)
- 3× vitest/no-disabled-tests rule-not-found
  (eslint-plugin-vitest not registered; inline-disable comments stale)
- 1× @typescript-eslint/no-unused-vars on imported beforeEach

Plus Phase 5 audit finding: TwoFactorView.spec.ts test router was
missing /recovery-use stub → Vue Router warn on every TwoFactorView mount.

Changes:
- BulkActionsBar.spec.ts, ProjectsView.spec.ts, projectsStore.spec.ts:
  replace `as any` with `as unknown as ReturnType<typeof vi.fn>` on
  axios method mocks; one case used `as unknown as { regionsOpen: bool }`
  for vm shape.
- NewProjectDialog.spec.ts, ProjectsView.spec.ts: remove stale
  `// eslint-disable-next-line vitest/no-disabled-tests` comments
  (it.skip() lines kept).
- ProjectsView.toolbar.spec.ts: drop unused `beforeEach` from import.
- TwoFactorView.spec.ts: add `/recovery-use` route stub.

Verification:
- npx eslint --max-warnings=0 → exit 0 (was 17 errors).
- npx vitest run on affected specs → 24/27 passed + 3 skipped (was same).
- TwoFactorView spec → 3/3 passed, no Vue Router warn.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 20:23:51 +03:00
Дмитрий 55a9d3fe00 fix(types): unify Project interface + NavItem.countKey + drop legacy Record
vue-tsc was emitting 9 errors from two issues:

1. ProjectCard.vue had a local `interface Project` missing region_mask /
   region_mode / delivery_days_mask, while stores/projectsStore.ts
   exported the canonical one with those fields. ProjectsView.vue passed
   the canonical Project to ProjectCard handler signatures which expected
   the local incomplete one → 5× TS2322.

2. EditProjectDialog passed `project: Project | Record<string, unknown>`
   to NewProjectDialog which expected `Record<string, unknown> | null`.
   Project lacks an index signature → TS2322.

3. AppSidebar.vue template referenced `item.countKey` not declared in
   NavItem interface → 2× TS2339.

Changes:
- ProjectCard.vue: drop local Project, import from projectsStore.
- NewProjectDialog.vue: project prop type → Project | null (was Record).
  Drop `as { id: number }` cast on PATCH URL.
- EditProjectDialog.vue: project prop type → Project | null.
- AppSidebar.vue: add `countKey?: string` to NavItem.
- projectsStore.ts: make region_mask/region_mode/delivery_days_mask
  optional (backward-compat for mock fixtures; production rows always
  populate them by schema).
- Test/story fixtures expanded with delivered_today/is_active/archived_at/
  sync_status to match strict Project shape.

Verification:
- npx vue-tsc --noEmit → 0 errors (was 9).
- npx vitest run on 5 affected specs → 16/16 passed + 2 skipped.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 20:15:26 +03:00
Дмитрий b5849bbd2a fix(projects): cyrillic ILIKE via PG ICU + clearable workaround
Корень: dev-БД `liderra` создавалась с LC_CTYPE=C — lower()/upper() не
делает case-folding для кириллицы, `ILIKE '%сп%'` на «Окна СПб» = 0 строк.
Test-БД с Russian_Russia.1251 маскировала проблему.

Системный fix: dev-БД пересоздана через `LOCALE_PROVIDER icu ICU_LOCALE 'und'`
(PG 16+ ICU collation, кросс-платформенно). Точечный COLLATE-workaround не
понадобился — все 5 ILIKE-endpoint'ов теперь работают с кириллицей без
правки кода. CTO-20 закрыт в реестре v1.81; команда CREATE DATABASE с ICU
зафиксирована для prod-deploy.

Сопутствующее:
- ProjectsView clearable: workaround `::after content '✕'` + видимость
  через `.v-field--dirty` (mdi-* font не подключён в проекте — CTO-19
  заведён в реестре).
- LookupsTest: удалён stale case `GET /api/projects?tenant_id=N`,
  заменённый auth:sanctum-роутом в Plan 5.
- Pest +1 регрессионный тест (`search is case-insensitive for Cyrillic`)
  в ProjectsListShowTest, 10/10 / 37 assertions.
- phpstan-baseline регенерирован (3 actingAs + удалённый case).
- cspell-words: +Регистронезависимый, +und.
- app/.backups/ в gitignore.

Verify:
- Pest --parallel: 742 passed / 1 flaky error (CsvReconcileJobTest cache
  race, в изоляции 2/2 PASS) / 3 skipped.
- Browser: «сп» и «окн» возвращают «Окна СПб».

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 19:25:25 +03:00
Дмитрий 84530d55bf test(projects): fix ProjectCard change-trigger target
Pre-existing failing test from commit c9ee8d8 — data-testid lives on
<label>, but @change handler sits on <input> inside it. jsdom does not
bubble change-event from label to input via @vue/test-utils trigger.
Use child-input selector to fire the event on the right node.

Baseline после fix: 614 passed / 3 skipped / 0 failed (vs 613 / 3 / 1).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 17:36:33 +03:00
Дмитрий df92ac02ff feat(projects-bulk): wire 3 new dialogs into BulkActionsBar
Add RegionsBulkDialog / DaysBulkDialog / LimitBulkDialog to
BulkActionsBar with open-state refs (regionsOpen/daysOpen/limitOpen),
runBulk helper via store.bulkUpdate, and flex-wrap layout.
Update spec: fix existing tests (bulkAction → bulkUpdate), add 3 new
dialog-wiring tests (7/7 pass; full suite 614+3skipped/0failed).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 15:23:29 +03:00
Дмитрий 4b6ab8f113 feat(projects-bulk): LimitBulkDialog delta or replace mode
Delta mode combines Add/Remove numeric inputs into a single signed delta;
Replace mode switches to an absolute value input via v-checkbox toggle.
5/5 Vitest pass; full suite 611 passed + 3 skipped.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 15:20:18 +03:00
Дмитрий 4c470813b4 feat(projects-bulk): DaysBulkDialog Add/Remove (7 weekday bitmask)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 15:17:16 +03:00
Дмитрий 3b254fb56f feat(projects-bulk): RegionsBulkDialog Add/Remove (8 ФО bitmask) 2026-05-12 15:14:18 +03:00
Дмитрий 95bba384a1 feat(projects-bulk): select-all toolbar with counter and indeterminate state
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 15:11:21 +03:00
Дмитрий a46e63bdd3 feat(projects-bulk): store selectAllByFilter + bulkUpdate with scope discriminator
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 15:08:08 +03:00
Дмитрий cb36a52171 test(projects-bulk): RLS cross-tenant isolation + empty-resolve edge case
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 15:02:04 +03:00
Дмитрий 64d8daede7 feat(projects-bulk): scope.filter resolver + 500-limit guard
Refactor inline scope resolution from ProjectController::bulk() into
ProjectService::resolveBulkScope (BULK_MAX=500 constant). Adds 2 tests:
scope.filter->ids mapping and >500 rejection (12 total, all pass).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 14:59:59 +03:00
Дмитрий c6eae16282 feat(projects-bulk): update_limit handler with per-project skip on delivered_today conflict
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 14:55:45 +03:00
Дмитрий 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
Дмитрий 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
Дмитрий 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