Archive three completed changes to archive/: - api-resource-standard (70 tasks, 14 resource classes) - capacity-expert-mode (68 tasks, expert mode planning grid) - enhanced-allocation (62 tasks, planning fidelity + reporting) Sync all delta specs to main specs/: - api-resource-standard: API response standardization - capacity-expert-mode: Expert mode toggle, grid, KPIs, batch API - resource-allocation: Month execution comparison, bulk, untracked - untracked-allocation: Null team member support - allocation-indicators: Variance indicators (red/amber/neutral) - monthly-budget: Explicit project-month planning All changes verified and tested (157 tests passing).
161 lines
7.6 KiB
Markdown
161 lines
7.6 KiB
Markdown
## ADDED Requirements
|
||
|
||
### Requirement: Toggle Expert Mode
|
||
The system SHALL provide a toggle switch on the Capacity Planning page that enables or disables Expert Mode. The toggle SHALL be persisted in `localStorage` under the key `headroom.capacity.expertMode` so the user's preference survives page reloads.
|
||
|
||
#### Scenario: Toggle defaults to off
|
||
- **WHEN** a user visits the Capacity Planning page for the first time
|
||
- **THEN** Expert Mode is off and the standard calendar view is shown
|
||
|
||
#### Scenario: Toggle persists across reloads
|
||
- **WHEN** a user enables Expert Mode and reloads the page
|
||
- **THEN** Expert Mode is still enabled and the grid view is shown
|
||
|
||
#### Scenario: Toggle is right-aligned on the tabs row
|
||
- **WHEN** the Capacity Planning page is rendered
|
||
- **THEN** the Expert Mode toggle appears right-aligned on the same row as the Calendar, Summary, Holidays, and PTO tabs
|
||
|
||
#### Scenario: Switching mode with unsaved changes warns user
|
||
- **WHEN** a user has dirty (unsaved) cells in the Expert Mode grid
|
||
- **AND** the user toggles Expert Mode off
|
||
- **THEN** the system shows a confirmation dialog: "You have unsaved changes. Discard and switch?"
|
||
- **AND** if confirmed, changes are discarded and the calendar view is shown
|
||
|
||
---
|
||
|
||
### Requirement: Display Expert Mode planning grid
|
||
The system SHALL render a dense planning grid when Expert Mode is enabled. The grid SHALL show all active team members as rows and all days of the selected month as columns.
|
||
|
||
#### Scenario: Grid shows all active team members
|
||
- **WHEN** Expert Mode is enabled for a given month
|
||
- **THEN** each active team member appears as a row in the grid
|
||
- **AND** inactive team members are excluded
|
||
|
||
#### Scenario: Grid shows all days of the month as columns
|
||
- **WHEN** Expert Mode is enabled for February 2026
|
||
- **THEN** the grid has 28 columns (one per calendar day)
|
||
- **AND** each column header shows the day number
|
||
|
||
#### Scenario: Weekend columns are visually distinct
|
||
- **WHEN** the grid is rendered
|
||
- **THEN** weekend columns (Saturday, Sunday) are visually distinguished (e.g. muted background)
|
||
|
||
#### Scenario: Holiday columns are visually distinct
|
||
- **WHEN** a day in the month is a company holiday
|
||
- **THEN** that column header is visually marked as a holiday
|
||
|
||
#### Scenario: Grid loads existing availability data
|
||
- **WHEN** Expert Mode grid is opened for a month where availability overrides exist
|
||
- **THEN** each cell pre-populates with the stored token matching the saved availability value
|
||
|
||
---
|
||
|
||
### Requirement: Cell token input and validation
|
||
The system SHALL accept exactly the following tokens in each grid cell: `H`, `O`, `0`, `.5`, `0.5`, `1`. Any other value SHALL be treated as invalid.
|
||
|
||
#### Scenario: Valid token accepted on blur
|
||
- **WHEN** a user types `1` into a cell and moves focus away
|
||
- **THEN** the cell displays `1` and is marked valid
|
||
|
||
#### Scenario: Valid token `.5` normalized on blur
|
||
- **WHEN** a user types `.5` into a cell and moves focus away
|
||
- **THEN** the cell displays `0.5` and is marked valid with numeric value `0.5`
|
||
|
||
#### Scenario: `H` and `O` accepted on any date
|
||
- **WHEN** a user types `H` or `O` into any cell (weekend, holiday, or working day)
|
||
- **THEN** the cell is marked valid with numeric value `0`
|
||
- **AND** the display shows the typed token (`H` or `O`)
|
||
|
||
#### Scenario: Invalid token marked red on blur
|
||
- **WHEN** a user types `2` or `abc` or any value not in the allowed set into a cell and moves focus away
|
||
- **THEN** the cell border turns red
|
||
- **AND** the raw text is preserved so the user can correct it
|
||
|
||
#### Scenario: Submit disabled while invalid cell exists
|
||
- **WHEN** any cell in the grid has an invalid token
|
||
- **THEN** the Submit button is disabled
|
||
|
||
#### Scenario: `0` auto-renders as `O` on weekend column
|
||
- **WHEN** a user types `0` into a cell whose column is a weekend day and moves focus away
|
||
- **THEN** the cell displays `O` (not `0`)
|
||
- **AND** the numeric value is `0`
|
||
|
||
#### Scenario: `0` auto-renders as `H` on holiday column
|
||
- **WHEN** a user types `0` into a cell whose column is a company holiday and moves focus away
|
||
- **THEN** the cell displays `H` (not `0`)
|
||
- **AND** the numeric value is `0`
|
||
|
||
---
|
||
|
||
### Requirement: Live KPI bar in Expert Mode
|
||
The system SHALL display a live KPI bar above the grid showing team-level Capacity (person-days) and Projected Revenue, updating in real time as cell values change.
|
||
|
||
#### Scenario: KPI bar shows correct capacity on load
|
||
- **WHEN** Expert Mode grid loads for a month
|
||
- **THEN** the KPI bar shows total team capacity in person-days matching the sum of all members' numeric cell values
|
||
|
||
#### Scenario: KPI bar updates when a cell changes
|
||
- **WHEN** a user changes a valid cell from `1` to `0.5`
|
||
- **THEN** the KPI bar immediately reflects the reduced capacity and revenue without a page reload
|
||
|
||
#### Scenario: Invalid cells excluded from KPI totals
|
||
- **WHEN** a cell contains an invalid token
|
||
- **THEN** that cell contributes `0` to the KPI totals (not `null` or an error)
|
||
|
||
#### Scenario: Projected Revenue uses hourly rate and hours per day
|
||
- **WHEN** the KPI bar calculates projected revenue
|
||
- **THEN** revenue = SUM(member capacity in person-days × hourly_rate × 8 hours/day) for all active members
|
||
|
||
---
|
||
|
||
### Requirement: Batch save availability from Expert Mode
|
||
The system SHALL allow saving all pending cell changes in a single batch operation via a Submit button.
|
||
|
||
#### Scenario: Submit saves all dirty valid cells
|
||
- **WHEN** a user has changed multiple cells and clicks Submit
|
||
- **THEN** the system sends a single batch request with all dirty cell values
|
||
- **AND** on success, all dirty flags are cleared and a success toast is shown
|
||
|
||
#### Scenario: Submit is disabled when no dirty cells exist
|
||
- **WHEN** no cells have been changed since the last save (or since load)
|
||
- **THEN** the Submit button is disabled
|
||
|
||
#### Scenario: Submit is disabled when any invalid cell exists
|
||
- **WHEN** at least one cell contains an invalid token
|
||
- **THEN** the Submit button is disabled regardless of other valid dirty cells
|
||
|
||
#### Scenario: Submit failure shows error
|
||
- **WHEN** the batch save API call fails
|
||
- **THEN** the system shows an error alert
|
||
- **AND** dirty flags are preserved so the user can retry
|
||
|
||
#### Scenario: Batch endpoint validates each availability value
|
||
- **WHEN** the batch endpoint receives an availability value not in `[0, 0.5, 1]`
|
||
- **THEN** the system returns HTTP 422 with a validation error message
|
||
|
||
---
|
||
|
||
### Requirement: Batch availability API endpoint
|
||
The system SHALL expose `POST /api/capacity/availability/batch` accepting an array of availability updates for a given month.
|
||
|
||
#### Scenario: Batch endpoint saves multiple updates
|
||
- **WHEN** a POST request is made to `/api/capacity/availability/batch` with a valid month and array of updates
|
||
- **THEN** the system upserts each `{team_member_id, date, availability}` entry
|
||
- **AND** returns HTTP 200 with `{ "data": { "saved": <count>, "month": "<YYYY-MM>" } }`
|
||
|
||
#### Scenario: Batch endpoint invalidates cache once
|
||
- **WHEN** a batch save completes for a given month
|
||
- **THEN** the month-level capacity cache is flushed exactly once (not once per row)
|
||
|
||
#### Scenario: Batch endpoint rejects invalid team_member_id
|
||
- **WHEN** a batch request contains a `team_member_id` that does not exist
|
||
- **THEN** the system returns HTTP 422 with a validation error
|
||
|
||
#### Scenario: Batch endpoint rejects invalid availability value
|
||
- **WHEN** a batch request contains an `availability` value not in `[0, 0.5, 1]`
|
||
- **THEN** the system returns HTTP 422 with a validation error
|
||
|
||
#### Scenario: Empty batch is a no-op
|
||
- **WHEN** a POST request is made with an empty `updates` array
|
||
- **THEN** the system returns HTTP 200 with `{ "data": { "saved": 0, "month": "<YYYY-MM>" } }`
|