feat(allocation): implement resource allocation feature
- Add AllocationController with CRUD + bulk endpoints - Add AllocationValidationService for capacity/estimate validation - Add AllocationMatrixService for optimized matrix queries - Add AllocationPolicy for authorization - Add AllocationResource for API responses - Add frontend allocationService and matrix UI - Add E2E tests for allocation matrix (20 tests) - Add unit tests for validation service and policies - Fix month format conversion (YYYY-MM to YYYY-MM-01)
This commit is contained in:
112
openspec/changes/capacity-expert-mode/tasks.md
Normal file
112
openspec/changes/capacity-expert-mode/tasks.md
Normal file
@@ -0,0 +1,112 @@
|
||||
## 1. Backend: Batch Availability Endpoint (Phase 1 - Tests RED)
|
||||
|
||||
- [x] 1.1 Write feature test: POST /api/capacity/availability/batch saves multiple updates and returns 200 with saved count
|
||||
- [x] 1.2 Write feature test: batch endpoint returns 422 when availability value is not in [0, 0.5, 1]
|
||||
- [x] 1.3 Write feature test: batch endpoint returns 422 when team_member_id does not exist
|
||||
- [x] 1.4 Write feature test: empty updates array returns 200 with saved count 0
|
||||
- [x] 1.5 Write unit test: CapacityService::batchUpsertAvailability() upserts all entries and flushes cache once
|
||||
|
||||
## 2. Backend: Batch Availability Endpoint (Phase 2 - Implement GREEN)
|
||||
|
||||
- [x] 2.1 Add `batchUpsertAvailability(array $updates, string $month): int` to CapacityService — upserts all rows, flushes month-level cache once after all upserts
|
||||
- [x] 2.2 Add `batchUpdateAvailability(Request $request): JsonResponse` to CapacityController — validate month + updates array, call service, return `{ data: { saved, month } }`
|
||||
- [x] 2.3 Add route: `POST /api/capacity/availability/batch` in `routes/api.php`
|
||||
- [x] 2.4 Run pint and all backend tests — confirm all pass
|
||||
|
||||
## 3. Backend: Batch Availability Endpoint (Phase 3 - Refactor & Document)
|
||||
|
||||
- [x] 3.1 Add Scribe docblock to `batchUpdateAvailability()` with request/response examples
|
||||
- [x] 3.2 Confirm no N+1 queries in batch upsert (cache flush is single, queries are acceptable for batch size)
|
||||
|
||||
## 4. Frontend: Expert Mode Toggle (Phase 1 - Tests RED)
|
||||
|
||||
- [x] 4.1 Write unit test: expertMode store reads from localStorage key `headroom.capacity.expertMode`, defaults to `false`
|
||||
- [x] 4.2 Write unit test: toggling expertMode persists new value to localStorage
|
||||
- [x] 4.3 Write component test: Expert Mode toggle renders right-aligned on tabs row
|
||||
- [x] 4.4 Write component test: toggle reflects current expertMode store value
|
||||
- [x] 4.5 Write component test: switching mode with dirty cells shows confirmation dialog
|
||||
|
||||
## 5. Frontend: Expert Mode Toggle (Phase 2 - Implement GREEN)
|
||||
|
||||
- [x] 5.1 Create `frontend/src/lib/stores/expertMode.ts` — writable store backed by `localStorage` key `headroom.capacity.expertMode`, default `false`
|
||||
- [x] 5.2 Add Expert Mode toggle (label + DaisyUI toggle switch) to `capacity/+page.svelte`, right-aligned on tabs row
|
||||
- [x] 5.3 Wire toggle to `expertModeStore` — on change, persist to localStorage
|
||||
- [x] 5.4 When toggling off with dirty cells: show confirm dialog; discard changes on confirm, cancel toggle on dismiss
|
||||
- [x] 5.5 Run type-check and unit tests — confirm all pass
|
||||
|
||||
## 6. Frontend: Expert Mode Grid Component (Phase 1 - Tests RED)
|
||||
|
||||
- [x] 6.1 Write component test: CapacityExpertGrid renders a row per active team member
|
||||
- [x] 6.2 Write component test: CapacityExpertGrid renders a column per day of the month
|
||||
- [x] 6.3 Write unit test: token normalization — `H` → `{ rawToken: "H", numericValue: 0, valid: true }`
|
||||
- [x] 6.4 Write unit test: token normalization — `O` → `{ rawToken: "O", numericValue: 0, valid: true }`
|
||||
- [x] 6.5 Write unit test: token normalization — `.5` → `{ rawToken: "0.5", numericValue: 0.5, valid: true }`
|
||||
- [x] 6.6 Write unit test: token normalization — `0.5` → `{ rawToken: "0.5", numericValue: 0.5, valid: true }`
|
||||
- [x] 6.7 Write unit test: token normalization — `1` → `{ rawToken: "1", numericValue: 1, valid: true }`
|
||||
- [x] 6.8 Write unit test: token normalization — `0` → `{ rawToken: "0", numericValue: 0, valid: true }`
|
||||
- [x] 6.9 Write unit test: token normalization — `2` → `{ rawToken: "2", numericValue: null, valid: false }`
|
||||
- [x] 6.10 Write unit test: auto-render — `0` on weekend column → rawToken becomes `O`
|
||||
- [x] 6.11 Write unit test: auto-render — `0` on holiday column → rawToken becomes `H`
|
||||
- [x] 6.12 Write component test: invalid cell shows red border on blur
|
||||
- [x] 6.13 Write component test: Submit button disabled when any invalid cell exists
|
||||
- [x] 6.14 Write component test: Submit button disabled when no dirty cells exist
|
||||
|
||||
## 7. Frontend: Expert Mode Grid Component (Phase 2 - Implement GREEN)
|
||||
|
||||
- [x] 7.1 Create `frontend/src/lib/utils/expertModeTokens.ts` — export `normalizeToken(raw, isWeekend, isHoliday)` returning `{ rawToken, numericValue, valid }`
|
||||
- [x] 7.2 Create `frontend/src/lib/components/capacity/CapacityExpertGrid.svelte`:
|
||||
- Props: `month`, `teamMembers`, `holidays`
|
||||
- On mount: fetch all members' individual capacity in parallel
|
||||
- Render grid: members × days, cells as `<input>` elements
|
||||
- On blur: run `normalizeToken`, apply auto-render rule, mark dirty
|
||||
- Invalid cell: red border
|
||||
- Emit `dirty` and `valid` state to parent
|
||||
- [x] 7.3 Wire `capacity/+page.svelte`: when Expert Mode on, render `CapacityExpertGrid` instead of calendar tab content
|
||||
- [x] 7.4 Add `batchUpdateAvailability(month, updates)` to `frontend/src/lib/api/capacity.ts`
|
||||
- [x] 7.5 Add Submit button to Expert Mode view — disabled when `!hasDirty || !allValid`; on click, call batch API, show success/error toast
|
||||
- [x] 7.6 Run type-check and component tests — confirm all pass
|
||||
|
||||
## 8. Frontend: Live KPI Bar (Phase 1 - Tests RED)
|
||||
|
||||
- [x] 8.1 Write unit test: KPI bar capacity = sum of all members' numeric cell values / 1 (person-days)
|
||||
- [x] 8.2 Write unit test: KPI bar revenue = sum(member person-days × hourly_rate × 8)
|
||||
- [x] 8.3 Write unit test: invalid cells contribute 0 to KPI totals (not null/NaN)
|
||||
- [x] 8.4 Write component test: KPI bar updates when a cell value changes
|
||||
|
||||
## 9. Frontend: Live KPI Bar (Phase 2 - Implement GREEN)
|
||||
|
||||
- [x] 9.1 Create `frontend/src/lib/components/capacity/ExpertModeKpiBar.svelte` (integrated into CapacityExpertGrid)
|
||||
- Props: `gridState` (reactive cell map), `teamMembers` (with hourly_rate)
|
||||
- Derived: `totalPersonDays`, `projectedRevenue`
|
||||
- Render: two stat cards (Capacity in person-days, Projected Revenue)
|
||||
- [x] 9.2 Mount `ExpertModeKpiBar` above the grid in Expert Mode view
|
||||
- [x] 9.3 Confirm KPI bar reactively updates on every cell change without API call
|
||||
- [x] 9.4 Run type-check and component tests — confirm all pass
|
||||
|
||||
## 10. Frontend: Expert Mode Grid (Phase 3 - Refactor)
|
||||
|
||||
- [x] 10.1 Extract cell rendering into a `ExpertModeCell.svelte` sub-component if grid component exceeds ~150 lines (skipped - optional refactor, grid at 243 lines is acceptable)
|
||||
- [x] 10.2 Add horizontal scroll container for wide grids (months with 28–31 days)
|
||||
- [x] 10.3 Ensure weekend columns have muted background, holiday columns have distinct header marker
|
||||
- [x] 10.4 Verify toggle is hidden on mobile (< md breakpoint) using Tailwind responsive classes
|
||||
|
||||
## 11. E2E Tests
|
||||
|
||||
- [x] 11.1 Write E2E test: Expert Mode toggle appears on Capacity page and persists after reload
|
||||
- [x] 11.2 Write E2E test: grid renders all team members as rows for selected month
|
||||
- [x] 11.3 Write E2E test: typing invalid token shows red cell and disables Submit
|
||||
- [x] 11.4 Write E2E test: typing valid tokens and clicking Submit saves and shows success toast
|
||||
- [x] 11.5 Write E2E test: KPI bar updates when cell value changes
|
||||
- [x] 11.6 Write E2E test: switching off Expert Mode with dirty cells shows confirmation dialog
|
||||
|
||||
## 12. Timezone & Accessibility Fixes
|
||||
|
||||
- [x] 12.1 Fix weekend detection: use `new Date(dateStr + 'T00:00:00')` to avoid local timezone drift
|
||||
- [x] 12.2 Update weekend styling: use `bg-base-300` solid background + `border-base-400` for accessibility
|
||||
- [x] 12.3 Update holiday styling: use `bg-warning/40` + `border-warning` with bold `H` marker
|
||||
- [x] 12.4 Prefill weekend cells with `O` on grid load (not `1`)
|
||||
- [x] 12.5 Prefill holiday cells with `H` on grid load (not `1`)
|
||||
- [x] 12.6 Add backend timezone helper using `America/New_York` for weekend/holiday checks
|
||||
- [x] 12.7 Ensure backend seeds weekends with `availability: 0` (to match frontend `O`)
|
||||
- [x] 12.8 Ensure backend seeds holidays with `availability: 0` (to match frontend `H`)
|
||||
- [x] 12.9 Run all tests and verify weekend shading aligns correctly for April 2026 (4th/5th = Sat/Sun)
|
||||
Reference in New Issue
Block a user