feat(M14): 3-pane backlog — generic SplitPane, BacklogStore, SSE realtime, card-grid TaskPanel (#22)

* feat(split-pane): refactor to generic n-pane SplitPane with cookie persistence

New API: panes[], defaultSplit[], cookieKey, tabLabels. Supports arbitrary
number of panes with n-1 draggable dividers and JSON cookie persistence.
Replaces TriplePane; mobile renders tabs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(split-pane): migrate callers to new panes[] API

Backlog page and sprint board now use generic SplitPane.
TriplePane removed; sprint board uses 3-pane with defaultSplit=[28,35,37].

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(split-pane): add unit tests for 2/3-pane, cookie-restore, mobile tabs

Added jsdom + @testing-library/react devDeps for component testing.
7 cases: render, divider count, cookie restore, invalid cookie fallback,
mobile tab render/switch, and no-dividers-on-mobile.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(backlog): add BacklogStore Zustand store with applyChange reducer

State: pbis, storiesByPbi, tasksByStory. setInitialData for server
hydration; applyChange(entity, op, data) handles I/U/D for SSE events.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(backlog): server-fetch tasks + hydrate BacklogStore on page load

Page now fetches tasks parallel to stories and groups by story_id.
BacklogHydrationWrapper calls setInitialData on mount so the store
is ready for downstream SSE consumers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(backlog): add EmptyPanel shared component, replace inline empty states

EmptyPanel takes title?, message, and optional action with DemoTooltip.
Replaces duplicate inline empty-state markup in pbi-list and story-panel.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(backlog): add TaskPanel with sortable rows and TaskDialog wiring

Reads selectedStoryId + tasksByStory from stores. DnD reorder via
reorderTasksAction. Row click → ?editTask, + button → ?newTask&storyId.
DemoTooltip on drag handles and + button.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(backlog): wire TaskPanel + TaskDialog into backlog page

3-pane SplitPane [20,45,35]. searchParams for newTask/editTask.
TaskDialog and EditTaskLoader render on ?newTask and ?editTask.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(backlog): add TaskPanel tests for render states and click handlers

7 cases: no-story empty, no-tasks empty+action, tasks render, + button
router.push, row click router.push, demo disabled button, demo disabled handles.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(backlog): migrate PbiList to store-driven via useBacklogStore

Removes pbis prop; reads from useBacklogStore(s => s.pbis) so SSE
updates reflect in real-time without prop drilling.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(backlog): migrate StoryPanel to store-driven + selectStory on click

Removes storiesByPbi prop; reads from useBacklogStore. Card click now
dispatches selectStory(id) + shows isSelected highlight. Edit moved to
inline pencil button. page.tsx drops pbis/storiesByPbi props.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(backlog): add 3-pane integration tests for click-cascade flow

Covers: empty states, PBI→stories, story→tasks, cascade-reset,
isSelected highlight. localStorage mocked for sort-mode persistence.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(ST-1115): SSE backlog realtime — endpoint, hook, hydration mount, tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(ST-1116): mobile auto-switch tabs + back button in BacklogSplitPane

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs(ST-1116): update functional-spec (3-pane backlog + mobile) and architecture (backlog SSE + backlog-store)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(ST-1117): TaskPanel card-grid — BacklogCard + rectSortingStrategy, tests updated

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(tests): correct PbiStatusApi type and remove duplicate mock keys

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Janpeter Visser 2026-04-30 18:16:07 +02:00 committed by GitHub
parent 6cd98129f2
commit 8877ea469d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 2474 additions and 305 deletions

View file

@ -194,27 +194,37 @@ Gebruikers kunnen producten aanmaken, bewerken en archiveren. Een product is het
---
### F-04: Product Backlog — gesplitst scherm
### F-04: Product Backlog — 3-paneels gesplitst scherm
**Prioriteit:** v1 — Kritiek
**Persona:** Lars, Dina, Remi
**Omschrijving:**
De Product Backlog wordt weergegeven als een gesplitst scherm: links de PBI's gerangschikt op prioriteit en volgorde, rechts de stories van het geselecteerde PBI. De splitter is horizontaal versleepbaar. Elk paneel heeft een eigen navigatiebar met acties (aanmaken, filteren, verwijderen).
De Product Backlog wordt weergegeven als een 3-paneels gesplitst scherm: PBI's (links) | Stories (midden) | Taken (rechts). De splitters zijn versleepbaar. Selectie cascadeert: klikken op een PBI toont de bijbehorende stories; klikken op een story toont de bijbehorende taken. Elk paneel heeft een eigen navigatiebar met acties.
**Acceptatiecriteria:**
- [ ] Standaard splitverhouding is 40/60 (PBI's / stories)
- [ ] Splitter is versleepbaar; positie wordt lokaal opgeslagen (localStorage)
- [ ] Selecteren van een PBI links toont de bijbehorende stories rechts
- [ ] Standaard splitverhouding is 20/45/35 (PBI's / Stories / Taken)
- [ ] Splitters zijn versleepbaar; positie wordt opgeslagen in een cookie (`sp:backlog-{id}`)
- [ ] Selecteren van een PBI toont de bijbehorende stories in het middenpaneel
- [ ] Geselecteerd PBI is visueel gemarkeerd (achtergrondkleur of rand)
- [ ] Linkerpaneel navigatiebar bevat: [+ PBI aanmaken], [filter], [verwijderen]
- [ ] Rechterpaneel navigatiebar bevat: [+ Story aanmaken], [filter], [verwijderen]
- [ ] Lege staat links: prompt om eerste PBI aan te maken
- [ ] Lege staat rechts (geen PBI geselecteerd): instructie om een PBI te selecteren
- [ ] Lege staat rechts (PBI geselecteerd, geen stories): prompt om eerste story aan te maken
- [ ] Selecteren van een story toont de bijbehorende taken in het rechterpaneel
- [ ] Geselecteerde story is visueel gemarkeerd
- [ ] Cascade-reset: selecteren van een ander PBI wist de geselecteerde story en taken
- [ ] PBI-paneel navigatiebar bevat: [+ PBI aanmaken]
- [ ] Stories-paneel navigatiebar bevat: [+ Story aanmaken], [sorteer], [filter status]
- [ ] Taken-paneel navigatiebar bevat: [+ Nieuwe taak]
- [ ] Lege staat PBI-paneel: prompt om eerste PBI aan te maken
- [ ] Lege staat Stories-paneel (geen PBI geselecteerd): instructie om een PBI te selecteren
- [ ] Lege staat Stories-paneel (PBI geselecteerd, geen stories): prompt om eerste story aan te maken
- [ ] Lege staat Taken-paneel (geen story geselecteerd): instructie om een story te selecteren
- [ ] Lege staat Taken-paneel (story geselecteerd, geen taken): prompt om eerste taak aan te maken
- [ ] Taak aanmaken opent TaskDialog via `?newTask=1&storyId={id}`
- [ ] Taak bewerken opent TaskDialog via `?editTask={id}`
**Randgevallen:**
- Scherm smaller dan 768px → gesplitst scherm schakelt over naar tabbladen (PBI's / Stories)
- Scherm smaller dan 1024px → 3-paneels scherm schakelt over naar 3 tabbladen (PBI's | Stories | Taken)
- Mobile tab-navigatie: klikken op PBI schakelt automatisch naar Stories-tab; klikken op story schakelt naar Taken-tab
- Mobile ← terug-knop in tab-header op tabs 2 en 3 navigeert naar het vorige tabblad
---