docs(ST-1109.10): document PbiStatus enum, sprint-close cascade, and filter UI

- docs/scrum4me-architecture.md: pbis-table updated with status column +
  index; PbiStatus enum + Pbi model in the Prisma schema sample;
  cascade-on-sprint-close rule documented inline
- docs/scrum4me-styling.md: short note pointing to PBI_STATUS_LABELS /
  PBI_STATUS_COLORS in components/shared/pbi-status-select.tsx so future
  components don't ad-hoc-copy the color map
- docs/plans/ST-1109-pbi-status.md: in-repo mirror of the approved plan
  (per feedback_plan_location memory) with cascade pseudo-code and
  end-to-end verification checklist

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Janpeter Visser 2026-04-29 17:21:39 +02:00
parent cde40d28c3
commit e632e043cf
3 changed files with 104 additions and 5 deletions

View file

@ -0,0 +1,79 @@
# Plan — ST-1109 · PBI krijgt een status (Ready / Blocked / Done)
> Spiegel van het goedgekeurde plan dat tijdens de sessie is opgesteld in
> `~/.claude/plans/welke-rioriteiten-heeft-een-mighty-shell.md`. Vastgelegd
> in deze repo per project-conventie (zie `MEMORY.md → feedback_plan_location`).
## Context
PBI's hadden alleen `priority` en `sort_order`; Story en Task hebben wél een status. Dit maakte het onmogelijk om in de Product Backlog te zien welke PBI's klaar staan, geblokkeerd zijn of al afgerond. Een derde filter naast prioriteit + sortering zou de bestaande UI te druk maken.
**Sprint-goal:** "Beter overzicht van openstaande PBI's"
**Story:** als teamlid wil filteren op status van een PBI
## Doelen
1. Nieuwe enum `PbiStatus { READY BLOCKED DONE }`, default `READY`
2. Status zichtbaar als badge in de Product Backlog cards
3. Status manueel te zetten via PBI-dialog (alle drie de waarden)
4. Auto-cascade: bij sprint-close → als alle stories van een PBI `DONE` zijn, PBI naar `DONE` (alleen-promote)
5. Filter-UI consolideren in één shadcn `Popover` (priority + status + sort)
## Stappen (één commit per laag)
| ST-code | Laag | Bestand(en) |
|---|---|---|
| ST-1109.2 | DB | `prisma/schema.prisma` + migration |
| ST-1109.3 | API mappers | `lib/task-status.ts` |
| ST-1109.4 | Server actions | `actions/pbis.ts` |
| ST-1109.5 | Sprint-close cascade | `actions/sprints.ts` |
| ST-1109.6 | UI primitive | `components/ui/popover.tsx` (NIEUW) |
| ST-1109.7 | Dialog | `components/backlog/pbi-dialog.tsx` + `components/shared/pbi-status-select.tsx` |
| ST-1109.8 | Backlog UI | `components/backlog/pbi-list.tsx` + `app/(app)/products/[id]/page.tsx` |
| ST-1109.9 | Tests | `__tests__/lib/task-status.test.ts` + `__tests__/actions/sprints-cascade.test.ts` |
| ST-1109.10 | Docs | architecture, styling, plan-mirror |
Detail-implementatieplannen staan op de individuele MCP-tasks (`mcp__scrum4me__get_claude_context`).
## Cascade-regel (definitief)
```ts
// In completeSprintAction's prisma.$transaction([...])
const candidatePbis = await prisma.pbi.findMany({
where: { id: { in: affectedPbiIds }, status: { not: 'DONE' } },
select: { id: true, stories: { select: { id: true, status: true } } },
})
const decisionByStoryId = new Map(entries) // entries = Object.entries(decisions)
const pbiIdsToMarkDone = candidatePbis
.filter(pbi =>
pbi.stories.length > 0 &&
pbi.stories.every(s => (decisionByStoryId.get(s.id) ?? s.status) === 'DONE')
)
.map(p => p.id)
```
**Regels:**
- Promote-only: een PBI op DONE wordt nooit automatisch teruggezet
- 0-story PBI's blijven READY (ondanks dat `[].every(...) === true`)
- Stories buiten de Sprint worden meegerekend op hun huidige DB-status — een open PBL-story blokkeert de cascade
## Branch + PR
- Branch: `feat/M12-pbi-status`
- Push + PR pas **na handmatige test door gebruiker** (per Branch & PR Strategy)
- Verwachte commits: 9× `feat/test/docs(ST-1109.x)`
## Opvolgactie buiten deze repo
[`madhura68/scrum4me-mcp`](https://github.com/madhura68/scrum4me-mcp): de `create_pbi` tool kan straks optioneel `status` accepteren. Submodule (`vendor/scrum4me`) moet gesynced worden na merge zodat de drift-bewaking maandag groen blijft.
## Verificatie (end-to-end)
1. `npm run lint && npm test && npm run build` — alles groen
2. `npm run dev` (port 3000)
3. Maak nieuwe PBI → status default "Klaar voor sprint" (READY)
4. Edit PBI → wijzig status naar "Geblokkeerd" → save → herlaad → badge toont oranje
5. Sprint met PBI + 2 stories → close met beide DONE → PBI auto-DONE
6. Idem maar 1 story OPEN → PBI blijft READY
7. Filter-popover: open → status=Geblokkeerd → alleen blocked PBIs zichtbaar; (n)-badge klopt; "Wis filters" reset
8. Demo-user: status-velden zijn read-only / save geblokkeerd

View file

@ -123,14 +123,18 @@ Scrum4Me is een desktop-first Next.js 16 webapplicatie die server-side wordt ger
|---|---|---|---|
| id | String (cuid) | PK | |
| product_id | String | FK → products (cascade delete) | |
| code | String | nullable, max 30 | Auto-gegenereerd of handmatig |
| title | String | not null, max 200 | |
| description | String | nullable, max 2000 | |
| priority | Int | 14, not null | 1 = Kritiek, 4 = Laag |
| sort_order | Float | not null | Float voor volgorde tussen items zonder renummering |
| status | Enum | READY \| BLOCKED \| DONE, default READY | Auto-promotie naar DONE bij sprint-close (zie hieronder) |
| created_at | DateTime | default now() | |
| updated_at | DateTime | auto-update | |
**Indexes:** `(product_id, priority, sort_order)` — standaard query voor het gesplitste scherm
**Indexes:** `(product_id, priority, sort_order)` — standaard query voor het gesplitste scherm; `(product_id, status)` — voor het statusfilter op de Product Backlog
**Cascade-regel (sprint-close):** wanneer een Sprint wordt afgerond via `completeSprintAction` en alle stories van een PBI eindigen op DONE (na toepassing van de afsluitbeslissingen), zet diezelfde transactie de PBI-status op DONE. Promotie alléén — een PBI op DONE wordt nooit automatisch teruggezet. Stories die niet in deze Sprint zaten worden meegerekend op hun huidige DB-status. Een PBI zonder stories blijft READY.
---
@ -285,6 +289,12 @@ enum StoryStatus {
DONE
}
enum PbiStatus {
READY
BLOCKED
DONE
}
enum TaskStatus {
TO_DO
IN_PROGRESS
@ -368,18 +378,22 @@ model Product {
}
model Pbi {
id String @id @default(cuid())
product Product @relation(fields: [product_id], references: [id], onDelete: Cascade)
id String @id @default(cuid())
product Product @relation(fields: [product_id], references: [id], onDelete: Cascade)
product_id String
code String? @db.VarChar(30)
title String
description String?
priority Int
sort_order Float
created_at DateTime @default(now())
updated_at DateTime @updatedAt
status PbiStatus @default(READY)
created_at DateTime @default(now())
updated_at DateTime @updatedAt
stories Story[]
@@unique([product_id, code])
@@index([product_id, priority, sort_order])
@@index([product_id, status])
}
model Story {

View file

@ -280,6 +280,12 @@ import { Badge } from '@/components/ui/badge'
<Badge variant="secondary">3 taken</Badge>
```
**PBI-status (READY / BLOCKED / DONE):** hergebruikt bestaande tokens —
`status-todo` voor READY, `status-blocked` voor BLOCKED, `status-done` voor
DONE. Centraal gedefinieerd in `components/shared/pbi-status-select.tsx`
(`PBI_STATUS_LABELS`, `PBI_STATUS_COLORS`); importeer die in plaats van
kleuren ad-hoc te kopiëren.
### Dialog (bevestigingsdialogen)
```tsx