|
|
@@ -272,8 +272,127 @@ with a `BOOTSTRAP_ADMIN` audit row.
|
|
|
|
|
|
### Upcoming
|
|
|
|
|
|
-Nothing scheduled. The next time real work lands, follow the maintenance
|
|
|
-rule (§14) and add the entry here.
|
|
|
+- [ ] **Phase 13 — "Focus" filter in the task list**
|
|
|
+
|
|
|
+ **Problem.** With more than a handful of workers on a sprint, the
|
|
|
+ task list is wide and noisy for someone who only cares about what
|
|
|
+ *they* (or one specific teammate) are on the hook for. Today you
|
|
|
+ can filter by owner, but "owner" is not the same as "has time
|
|
|
+ assigned" — a task owned by Alice can still have Bob doing 3 days
|
|
|
+ of the work. Scanning a 12-worker grid for Bob's non-zero cells is
|
|
|
+ exactly what the computer should do.
|
|
|
+
|
|
|
+ **Goal.** Add a **Focus** control to the task-list toolbar. Pick
|
|
|
+ one sprint worker; the list then shows only tasks where that
|
|
|
+ worker's assignment is > 0, and any worker column that is zero for
|
|
|
+ every remaining visible task collapses away. A **Reset filters**
|
|
|
+ button next to it clears the search, priority filter, owner
|
|
|
+ multi-select, focus worker, and any manually-hidden columns in one
|
|
|
+ click. All state is purely client-side — no API changes.
|
|
|
+
|
|
|
+ **Scope (plan).**
|
|
|
+
|
|
|
+ 1. **Toolbar (`views/sprints/show.php`).**
|
|
|
+ - Between the existing `[data-owner-filter-root]` and
|
|
|
+ `[data-columns-root]`, insert a `[data-focus-filter-root]`
|
|
|
+ block: a `<select data-focus-select>` with `<option value="">
|
|
|
+ All workers</option>` + one `<option value="{sw_id}">{name}
|
|
|
+ </option>` per sprint worker. Styled to match the other
|
|
|
+ toolbar controls.
|
|
|
+ - At the far left of the toolbar row, add a
|
|
|
+ `<button data-reset-filters>` (subtle, border-only, "Reset"
|
|
|
+ label). Visible only when any filter is active (managed by
|
|
|
+ the same logic that computes filter state below).
|
|
|
+ - No schema or controller changes — this is pure view + JS.
|
|
|
+
|
|
|
+ 2. **JS filter state (`public/assets/js/sprint-planner.js`).**
|
|
|
+ - New localStorage key: `sp:{sprintId}:focusWorker`, string
|
|
|
+ value (sw_id or "").
|
|
|
+ - Hydrate on boot, reflect into `<select data-focus-select>`.
|
|
|
+ - On `change`: persist, then call `applyFilters()`.
|
|
|
+ - `applyFilters()` grows a fourth AND clause: if focus is set
|
|
|
+ and the row's `[data-assign][data-sw-id="{focus}"]` input has
|
|
|
+ value ≤ 0, hide the row. Use the same zero-tolerance the
|
|
|
+ capacity math uses (`Number(v) > 0`, not `!== 0`, so 0.0 is
|
|
|
+ treated as empty).
|
|
|
+
|
|
|
+ 3. **Derived column auto-hide (new function,
|
|
|
+ `applyFocusColumnVisibility()`).**
|
|
|
+ - Called at the end of `applyFilters()` when focus is active.
|
|
|
+ - For each sprint worker's column `sw-{id}`:
|
|
|
+ - If any visible task row has `[data-assign][data-sw-id="{id}"]`
|
|
|
+ > 0 → keep the column as-is (respect the user's manual
|
|
|
+ visibility from the Columns dropdown).
|
|
|
+ - Otherwise → add a transient CSS class `focus-auto-hidden`
|
|
|
+ to every `[data-col="sw-{id}"]` cell/header (don't touch
|
|
|
+ the user's `hiddenCols` set, so turning focus off restores
|
|
|
+ their pick).
|
|
|
+ - When focus is cleared, strip every `.focus-auto-hidden` and
|
|
|
+ let `applyColumnVisibility()` re-run.
|
|
|
+ - Define `.focus-auto-hidden { display: none; }` in
|
|
|
+ `assets/css/input.css` (one line; Tailwind's `@layer
|
|
|
+ utilities` section, or inline in the view — former keeps the
|
|
|
+ vendored CSS path consistent).
|
|
|
+
|
|
|
+ 4. **Reset button.**
|
|
|
+ - On click: clear `[data-task-search]`, reset
|
|
|
+ `[data-prio-filter]` to `""`, empty `ownerFilterSet` (and
|
|
|
+ persist), clear `focusWorker` (and persist), and empty
|
|
|
+ `hiddenCols` (and persist), then call the existing update
|
|
|
+ functions (`updateOwnerFilterUi`, `applyColumnVisibility`,
|
|
|
+ `applyFilters`). One code path, no reloads.
|
|
|
+ - Visibility rule: show the button when any of
|
|
|
+ `{search, prio, ownerFilterSet.size, focusWorker,
|
|
|
+ hiddenCols.size}` is non-empty. Re-evaluate after every
|
|
|
+ filter change.
|
|
|
+
|
|
|
+ 5. **Edge cases / behaviour.**
|
|
|
+ - **Focus + owner + search all active** → AND. The existing
|
|
|
+ `applyFilters` ordering is fine; focus is just another
|
|
|
+ predicate per row.
|
|
|
+ - **Focused worker has no assigned tasks** → row list is empty,
|
|
|
+ every sw column is all-zero, so every sw column collapses.
|
|
|
+ The existing `[data-task-empty-filter]` banner covers the
|
|
|
+ empty-rows case — test that it shows, not the "No tasks yet"
|
|
|
+ banner.
|
|
|
+ - **New task created while focus is on** → `buildTaskRow` adds
|
|
|
+ a row with zero assignments, so the focus filter will hide
|
|
|
+ it immediately. Acceptable (user can clear focus to see it),
|
|
|
+ but a small UX surprise — call it out in the commit message.
|
|
|
+ - **Worker removed from sprint** → the reorder-then-reload
|
|
|
+ (§10) already covers this; focus just reads from the refreshed
|
|
|
+ DOM.
|
|
|
+ - **Archived sprints** → read-only, but the focus/reset
|
|
|
+ controls are pure UI, so they still work for viewers.
|
|
|
+
|
|
|
+ 6. **Tests.** No new PHPUnit — this phase is 100% client-side JS
|
|
|
+ over existing HTML. Manual acceptance:
|
|
|
+ (a) pick a worker with assignments → only their tasks show
|
|
|
+ and only columns with non-zero cells for those tasks
|
|
|
+ remain;
|
|
|
+ (b) pick a worker with no assignments → empty-filter banner,
|
|
|
+ all sw columns collapsed;
|
|
|
+ (c) stack with search + prio + owner filters → all four AND;
|
|
|
+ (d) hit Reset → everything clears, Reset button vanishes.
|
|
|
+ Add these to [ACCEPTANCE.md](ACCEPTANCE.md) in the same commit
|
|
|
+ so the §10 manual walkthrough stays truthful.
|
|
|
+
|
|
|
+ **Out of scope.**
|
|
|
+ - Server-side enforcement of the filter. The API does not gate
|
|
|
+ which tasks a user can see — sprint membership already grants
|
|
|
+ read access to the whole sprint, and focus is a personal lens
|
|
|
+ over that.
|
|
|
+ - Multi-worker focus. Sticking to one worker at a time keeps the
|
|
|
+ UI + column-collapse rule simple. Multi-focus would muddy "what
|
|
|
+ counts as a relevant column" and nobody has asked for it.
|
|
|
+ - Persisting the Reset click as an audit event. Pure UI state, no
|
|
|
+ data mutation, no audit row.
|
|
|
+
|
|
|
+ **Spec alignment.**
|
|
|
+ - §3 / §4 / §5: unchanged — no schema or domain changes.
|
|
|
+ - §6 Routes: unchanged — no new endpoints.
|
|
|
+ - §7 Audit: unchanged — no mutations.
|
|
|
+ - §11: phpunit count stays 88 (no new tests).
|
|
|
|
|
|
## 10. Residual known gaps / deferred items
|
|
|
|
|
|
@@ -330,12 +449,14 @@ vendor/bin/phpunit
|
|
|
Tell Claude:
|
|
|
|
|
|
> Working on `/Users/achiappa/Development/claude_code_private/sprint_planer_web`.
|
|
|
-> Read `HANDOFF.md`, the git log, and `ACCEPTANCE.md`. All 12 phases are
|
|
|
-> shipped (see §9). There is no scheduled coding work. The only
|
|
|
-> outstanding items are in §10 (mostly a manual acceptance walkthrough
|
|
|
-> in the running container). If I ask for new work, follow the
|
|
|
-> maintenance rule in §14 — commit code, then commit a HANDOFF.md
|
|
|
-> update separately.
|
|
|
+> Read `HANDOFF.md`, the git log, and `ACCEPTANCE.md`. Phases 1–12 are
|
|
|
+> shipped (see §9); Phase 13 ("Focus" filter + reset in the task list)
|
|
|
+> is planned but not implemented — the scope is in §9 under Upcoming.
|
|
|
+> Other outstanding items are in §10 (mostly a manual acceptance
|
|
|
+> walkthrough in the running container). If I ask you to work Phase 13,
|
|
|
+> follow the maintenance rule in §14 — commit code, then commit a
|
|
|
+> HANDOFF.md update separately that moves the entry from Upcoming →
|
|
|
+> Shipped with its SHA.
|
|
|
|
|
|
Claude should verify what's described here against actual repo state
|
|
|
before acting — nothing here is load-bearing once it grows stale.
|