Files
headroom/openspec/changes/capacity-expert-mode/tasks.md
Santhosh Janardhanan 3324c4f156 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)
2026-02-25 16:28:47 -05:00

113 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 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 2831 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)