docs: introduce generic entity-dialog pattern + entity-profiles (#45)
* docs(dialog-pattern): add generic entity-dialog spec Introduceert docs/patterns/dialog.md als bron-of-truth voor elke create/edit/detail-dialog in Scrum4Me, ongeacht het achterliggende dataobject. Bevat 14 secties: uitgangspunten, stack, component- architectuur, layout, validatie, drielaagse demo-policy, submission, dialog-gedrag, theming, footer, triggers/URL-state, per-entiteit profile-template, out-of-scope, en een verificatie-checklist. Registreert het patroon in CLAUDE.md "Implementatiepatronen"-tabel zodat Claude (en mensen) de spec verplicht raadplegen voor elke nieuwe dialog. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(dialog-pattern): convert task spec + add pbi/story entity-profiles Reduceert docs/scrum4me-task-dialog.md van 507 naar ~140 regels: alle gedeelde regels verhuisd naar docs/patterns/dialog.md, dit document bevat nu alleen Task-specifieke velden, URL-pattern, status-veld, server actions, triggers en bewuste out-of-scope-keuzes. Voegt twee nieuwe entity-profielen toe voor bestaande dialogen: - docs/scrum4me-pbi-dialog.md (PbiDialog: state-based, code+title-rij, PbiStatusSelect, geen delete in v1) - docs/scrum4me-story-dialog.md (StoryDialog: state-based, header met status/priority badges, inline activity-log, demo-readonly-fallback, inline-delete-confirm i.p.v. AlertDialog) Beide profielen documenteren expliciet de "Bekende gaps t.o.v. generieke spec" zodat opvolgende PR's de afwijkingen kunnen rechtzetten of bewust kunnen accorderen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Added pdevelopment docs --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6c6c8b96b7
commit
55a1ee035c
8 changed files with 1241 additions and 469 deletions
133
docs/plans/ST-1114-copilot-reviews.md
Normal file
133
docs/plans/ST-1114-copilot-reviews.md
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
# Plan — ST-1114 · Copilot reviews op dashboard
|
||||
|
||||
## Context
|
||||
|
||||
Als ontwerper wil je een overzicht zien van GitHub Copilot's PR-reviews om per stuk te beslissen of je 'm implementeert of overslaat. De codebase heeft nu **nul** GitHub-integratie — alleen `product.repo_url` als string voor hyperlinks. We bouwen een minimale, hobby-vriendelijke architectuur.
|
||||
|
||||
## Architectuurkeuzes (via AskUserQuestion bevestigd)
|
||||
|
||||
- **Auth**: lokaal script met `GITHUB_TOKEN` — webapp heeft GEEN GitHub-credentials. Het script draai je lokaal wanneer je wil verversen.
|
||||
- **Fetch**: on-demand op dashboard-load (server-side `prisma.copilotReview.findMany`, geen externe call)
|
||||
- **Decision**: alleen visuele toggle in `localStorage` (geen DB-persistentie)
|
||||
- **Scope**: MVP — tonen + lokale toggle. Geen cron, geen webhook, geen GitHub-auth in productie.
|
||||
|
||||
## Architectuur
|
||||
|
||||
```
|
||||
┌──────────────┐ octokit ┌────────────┐ API token ┌─────────────┐
|
||||
│ scripts/ │ ──────────▶ │ GitHub │ │ Scrum4Me │
|
||||
│ sync-copilot │ │ REST API │ │ /api/ │
|
||||
│ -reviews.ts │ ◀────────── │ │ │ copilot- │
|
||||
└──────────────┘ reviews └────────────┘ POST batch │ reviews │
|
||||
│ │ │
|
||||
└──────────────────────────────────────────────────▶ DB upsert │
|
||||
└──────┬──────┘
|
||||
│
|
||||
┌──────▼──────┐
|
||||
│ /dashboard │
|
||||
│ server-side │
|
||||
│ findMany │
|
||||
└─────────────┘
|
||||
```
|
||||
|
||||
Het script is de enige plek waar GitHub-credentials nodig zijn. Productie kent alleen Scrum4Me-data.
|
||||
|
||||
## Datamodel
|
||||
|
||||
```prisma
|
||||
model CopilotReview {
|
||||
id String @id @default(cuid())
|
||||
product Product @relation(fields: [product_id], references: [id], onDelete: Cascade)
|
||||
product_id String
|
||||
pr_number Int
|
||||
pr_title String
|
||||
pr_url String
|
||||
pr_state String // 'open' | 'closed' | 'merged'
|
||||
author_login String // bv. 'copilot-pull-request-reviewer[bot]'
|
||||
review_state String // 'COMMENTED' | 'APPROVED' | 'CHANGES_REQUESTED'
|
||||
body String @db.Text
|
||||
submitted_at DateTime
|
||||
html_url String // directe link naar de review
|
||||
fetched_at DateTime @default(now())
|
||||
|
||||
@@unique([product_id, pr_number, submitted_at])
|
||||
@@index([product_id, submitted_at])
|
||||
@@map("copilot_reviews")
|
||||
}
|
||||
```
|
||||
|
||||
`@@unique` zorgt voor idempotency — script kan herhaald draaien zonder dupes. Geen `decision`-veld: dat staat in `localStorage`.
|
||||
|
||||
## Script
|
||||
|
||||
`scripts/sync-copilot-reviews.ts` — TypeScript via `tsx`, leest env, gebruikt Octokit, POST naar eigen API.
|
||||
|
||||
```bash
|
||||
GITHUB_TOKEN=ghp_... \
|
||||
SCRUM4ME_API_URL=http://localhost:3000 \
|
||||
SCRUM4ME_API_TOKEN=s4m_... \
|
||||
npx tsx scripts/sync-copilot-reviews.ts
|
||||
```
|
||||
|
||||
Stappen:
|
||||
1. `GET /api/products` (Bearer-auth) — lijst toegankelijke producten met `repo_url`
|
||||
2. Per product: parse `owner/repo` uit `repo_url`, `octokit.pulls.list({state: 'all', per_page: 50})`
|
||||
3. Per PR: `octokit.pulls.listReviews(...)`, filter op `user.type === 'Bot' && user.login.includes('copilot')`
|
||||
4. `POST /api/copilot-reviews` met `{ product_id, reviews: [...] }` — endpoint doet `deleteMany` + `createMany` per product (atomic replace)
|
||||
5. Print samenvatting: aantal reviews per product + totale runtime
|
||||
|
||||
## API endpoint
|
||||
|
||||
`app/api/copilot-reviews/route.ts`:
|
||||
|
||||
- **POST**: Bearer-auth, demo-block, payload `{ product_id, reviews: CopilotReview[] }`. Atomic transaction: delete-all-for-product → createMany. Validatie via Zod.
|
||||
- **GET**: niet nodig — dashboard leest direct via Prisma server-side. Endpoint kan komen voor toekomstige clients.
|
||||
|
||||
## Dashboard widget
|
||||
|
||||
Boven of onder de bestaande product-grid een nieuwe sectie "Copilot reviews".
|
||||
|
||||
`components/dashboard/copilot-reviews.tsx` (client component):
|
||||
- Props: `reviews: CopilotReview[]` (server-fetched)
|
||||
- Lijst met cards: PR-titel + nummer (link naar PR), Copilot's body (truncated of accordion), state-badge, "Implementeer" / "Skip"-knoppen
|
||||
- Decision-state in `localStorage` keyed op `review.id`: `'implement' | 'skip' | undefined` (default: ongezien)
|
||||
- Cards met decision='skip' visueel gedimmed; cards met 'implement' krijgen een groen randje
|
||||
- Filter-toggles bovenaan: "Alle / Te beoordelen / Implementeren / Skip"
|
||||
- Empty state: "Geen Copilot-reviews gevonden — draai het sync-script."
|
||||
|
||||
`app/(app)/dashboard/page.tsx` past `prisma.copilotReview.findMany({ where: { product_id: { in: accessibleIds } }, orderBy: { submitted_at: 'desc' } })` en geeft door.
|
||||
|
||||
## Voorgestelde sub-tasks
|
||||
|
||||
| Code | Onderwerp |
|
||||
|---|---|
|
||||
| ST-1114.2 | DB: `CopilotReview` model + migration |
|
||||
| ST-1114.3 | API: `POST /api/copilot-reviews` (Bearer-auth + demo-block + replace-by-product) |
|
||||
| ST-1114.4 | Script: `scripts/sync-copilot-reviews.ts` met octokit |
|
||||
| ST-1114.5 | UI: dashboard-widget met cards, localStorage-decision, filter-toggle |
|
||||
| ST-1114.6 | Tests: API endpoint (auth, demo-block, validation), dashboard-widget snapshot |
|
||||
| ST-1114.7 | Docs: README-sectie over script + env-vars; CLAUDE.md-update |
|
||||
|
||||
## M11-keuzes voor de implementerende sessie
|
||||
|
||||
Drie open beslissingen die niet kritiek zijn voor het plan zelf:
|
||||
|
||||
1. **PR-state filter**: alle PR's of alleen `state=open`? (closed-PRs hebben oude reviews die misschien niet meer relevant zijn)
|
||||
2. **Markdown-rendering**: react-markdown, of plain `<pre>`? (react-markdown is +35KB bundle)
|
||||
3. **localStorage-key-vorm**: `scrum4me:copilot-decision:{review_id}` per review, of één map-object onder één key?
|
||||
|
||||
## Branch + PR
|
||||
|
||||
- Branch: `feat/M14-copilot-reviews` (M14 = nieuwe milestone)
|
||||
- 6 commits (.2 t/m .7), één per laag
|
||||
- PR pas openen na handmatige test door gebruiker
|
||||
|
||||
## Verificatie (end-to-end)
|
||||
|
||||
1. `npm run dev`
|
||||
2. `GITHUB_TOKEN=... SCRUM4ME_API_TOKEN=... npx tsx scripts/sync-copilot-reviews.ts` — toont `n reviews opgeslagen`
|
||||
3. Browser refresht dashboard → "Copilot reviews"-sectie toont cards met PR-titels
|
||||
4. Klik "Implementeer" → kaart krijgt groen randje, decision in localStorage
|
||||
5. Refresh → state blijft (localStorage)
|
||||
6. Filter toggle "Alleen te beoordelen" → cards met decision verdwijnen
|
||||
7. Demo-user: kan reviews zien, maar `POST /api/copilot-reviews` weigert (al via middleware-guard van ST-1110)
|
||||
Loading…
Add table
Add a link
Reference in a new issue