Een desktop-first fullstack webapplicatie voor solo developers en kleine Scrum Teams die meerdere softwareprojecten parallel beheren. De app organiseert werk hiërarchisch (product → PBI → story → taak), biedt gesplitste planningsschermen met drag-and-drop, en integreert met Claude Code via een REST API en MCP https://scrum4-me.vercel.app
Find a file
Janpeter Visser d587be2fb3
feat(PBI-79): Product Backlog sprint-membership via vinkjes (#190)
* feat(PBI-79/ST-1333): active-sprint null-contract + clearActiveSprintAction

- lib/user-settings.ts: activeSprints values nullable in Zod-schema.
  Key-aanwezigheid heeft nu betekenis (key+null = bewust geen sprint;
  key ontbreekt = fallback-cascade).
- lib/active-sprint.ts: nieuwe readStoredActiveSprintState helper +
  resolveActiveSprint respecteert expliciet 'cleared' state zonder fallback.
  clearActiveSprintInSettings schrijft null i.p.v. de key te verwijderen.
- actions/active-sprint.ts: nieuwe clearActiveSprintAction met auth +
  membership-check.
- components/shared/sprint-switcher.tsx: '— Geen actieve sprint —'-optie
  in dropdown, disabled wanneer er geen actieve sprint is.
- Tests: nieuwe active-sprint.test.ts (resolver-paden + clear),
  active-sprint-action.test.ts (action-laag), uitbreiding user-settings.test.ts.

Plan: docs/plans/PBI-79-backlog-sprint-workflow.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(PBI-79/ST-1334): user-settings pendingSprintDraft-slot

- lib/user-settings.ts: nieuw workflow.pendingSprintDraft veld met
  compacte intent-shape (pbiIntent + per-PBI storyOverrides).
- actions/sprint-draft.ts: setPendingSprintDraftAction +
  clearPendingSprintDraftAction met product-membership-check + Zod-validatie.
- stores/user-settings/store.ts: setPendingSprintDraft / clearPendingSprintDraft
  optimistic acties + fine-grained mutators upsertPbiIntent / upsertStoryOverride.
  Sprint-draft actions worden dynamisch geïmporteerd zodat jsdom-tests
  zonder DATABASE_URL niet falen.
- Tests: nieuwe sprint-draft.test.ts (action-laag), uitbreiding
  user-settings store-tests (5 nieuwe cases) en schema-tests (4 cases).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(PBI-79/ST-1343): sprint-conflicts helper-library

- lib/sprint-conflicts.ts: drie pure/server-side helpers voor eligibility
  + cross-sprint detectie.
  - isEligibleForSprint(story): sprint_id IS NULL en status != DONE
  - partitionByEligibility(prisma, storyIds, excludeSprintId): split in
    eligible / notEligible / crossSprint met reden per story
  - getBlockingSprintMap(prisma, productId, storyIds, excludeSprintId):
    map storyId → { sprintId, sprintName } voor stories in andere OPEN sprint
- Tests: __tests__/lib/sprint-conflicts.test.ts (16 cases) — alle eligibility
  paden + cross-sprint scoping + CLOSED-sprint filtering.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(PBI-79/ST-1335): sprint-membership-summary + cross-sprint-blocks endpoints

Twee nieuwe GET-route handlers, beide verplicht gescoped op pbiIds (geen
product-brede aanroepen).

- app/api/products/[id]/sprint-membership-summary/route.ts
  Response: { [pbiId]: { total, inSprint } } via twee prisma.groupBy calls
  (totaal + binnen actieve sprint). Voor state-B tri-state.

- app/api/products/[id]/cross-sprint-blocks/route.ts
  Response: { [storyId]: { sprintId, sprintName } } voor stories in andere
  OPEN sprints. UX-hint voor disabled-vinkjes; commit-acties blijven
  autoritatief.

Tests: 13 cases dekken happy path, 400 zonder pbiIds, 400 zonder sprintId,
404 zonder product-access, auth-fail, en NOT-clause voor excludeSprintId.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(PBI-79/ST-1336): product-workspace sprint-membership slice + selectors

Datalaag voor de vinkje-UI van state A′ en state B.

types.ts:
- PbiSummaryEntry, CrossSprintBlock, SprintMembershipSlice toegevoegd.

store.ts:
- Nieuwe slice `sprintMembership` met pbiSummary, crossSprintBlocks,
  pending: { adds[], removes[] }, loadedSummaryForSprintId.
- Acties: setPbiSummary, setCrossSprintBlocks, toggleStorySprintMembership
  (cancel-out logic), resetSprintMembershipPending, fetchSprintMembershipSummary,
  fetchCrossSprintBlocks.
- hydrateSnapshot reset óók de membership-slice.

selectors.ts:
- selectPbiTriState (aggregate-only zolang stories niet geladen; rekent
  pending mee bij loaded PBI's).
- selectStoryEffectiveInSprint (DB ⊕ pending).
- selectStoryIsBlocked (cross-sprint hint).
- selectIsDirty, selectPendingCount.

Tests: 25 cases in nieuwe sprint-membership.test.ts dekken alle selector-
paden, toggle-cancel-out, fetch-helpers, en pbiId-scoping.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(PBI-79/ST-1337): state A′ UI — metadata dialog + sticky banner + PbiList ombouw

UI-laag voor de sprint-definitie-flow (state A′).

Nieuw:
- NewSprintMetadataDialog (stap 1): sprint_goal + optionele dates;
  'Verder' schrijft via useUserSettingsStore.setPendingSprintDraft.
- SprintDefinitionBanner (sticky): toont doel + X PBI's / Y stories teller;
  'Annuleren' → AlertDialog confirm → clearPendingSprintDraft;
  'Sprint aanmaken' nog niet aangesloten (wacht op ST-1339).
- NewSprintTrigger: button in page header die de metadata-dialog opent;
  verbergt zichzelf zolang er al een draft loopt.
- SprintDraftBanner: client-wrapper, rendert banner alleen als draft bestaat.

Wijzigingen:
- lib/user-settings.ts: pendingSprintDraft startAt/endAt → z.string().date().
- PbiList: oude selectionMode + selectedIds + NewSprintDialog vervangen door
  hasDraft-afgeleide A′-mode met tri-state vinkjes; togglen muteert
  upsertPbiIntent('all'|'none') en wist storyOverrides per PBI.
- StoryPanel: in A′-mode toont elke story een cherrypick-checkbox die
  upsertStoryOverride('add'/'remove'/'clear') aanroept; cross-sprint-blocked
  stories krijgen disabled-icoon met sprint-naam tooltip.
- app/(app)/products/[id]/page.tsx: StartSprintButton vervangen door
  NewSprintTrigger; SprintDraftBanner gepositioneerd boven split-pane.

Tests: bestaande tests blijven groen (806 cases) — UI-specifieke component
tests volgen later. ST-1339 sluit createSprintWithSelectionAction aan.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(PBI-79/ST-1339): createSprintWithSelectionAction + banner wire-up

actions/sprints.ts:
- Nieuwe createSprintWithSelectionAction(productId, metadata, pbiIntent,
  storyOverrides).
- Server-side intent-resolve:
  1. Voor elke PBI met intent='all': fetch child-story-IDs minus
     storyOverrides[pbi].remove.
  2. Plus storyOverrides[*].add (cross-PBI cherrypick toegestaan).
- Eligibility-filter via partitionByEligibility (sprint_id IS NULL + status
  != DONE; stories in andere OPEN sprint → conflicts.crossSprint).
- Transactie wrapt sprint.create + story.updateMany (status='IN_SPRINT') +
  task.updateMany (sprint_id cascade) — alles atomair.
- setActiveSprintInSettings na success.
- Return: { success, sprintId, affectedStoryIds, affectedPbiIds,
  affectedTaskIds, conflicts: { notEligible, crossSprint } } of error.

components/backlog/sprint-definition-banner.tsx:
- 'Sprint aanmaken'-knop sluit aan op createSprintWithSelectionAction;
  toast bij conflicts, success-toast anders, router.refresh() voor SSR
  cycle. Pending draft wordt door de action zelf nog niet expliciet gewist
  — dat gebeurt via revalidatePath en kan in ST-1340 finetuned worden.

Tests: __tests__/actions/create-sprint-with-selection.test.ts (6 cases)
dekken intent-resolve, override-respect, cross-sprint conflict, transactie-
binding van story.status + task.sprint_id, return-shape, en error-pad.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(PBI-79/ST-1340): commitSprintMembershipAction + gerichte client-store patches

actions/sprints.ts:
- Nieuwe commitSprintMembershipAction(activeSprintId, adds[], removes[]).
- Eligibility-filter voor adds via partitionByEligibility (sprint_id IS NULL
  en niet DONE; cross-sprint conflicts → notEligible).
- Race-safety voor removes: alleen stories met huidige sprint_id ==
  activeSprintId; rest → conflicts.alreadyRemoved.
- Transactie wrapt twee updateMany-paren (story status mee, task.sprint_id
  cascade). Update-paren overgeslagen wanneer leeg.
- Return: { success, affectedStoryIds, affectedPbiIds, affectedTaskIds,
  conflicts: { notEligible, alreadyRemoved } }.

stores/product-workspace/store.ts:
- applyMembershipCommitResult({ activeSprintId, addedStoryIds,
  removedStoryIds }) patcht entities.storiesById met juiste sprint_id +
  status; ledigt sprintMembership.pending. Geen task-veld omdat
  BacklogTask geen sprint_id-kolom heeft in de store.

Tests: __tests__/actions/commit-sprint-membership.test.ts (8 cases) — happy
path, DONE-conflict, cross-sprint, race-safety voor removes, transactie-
inhoud (status='IN_SPRINT'/'OPEN'), task-cascade, return-shape, auth-fail.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(PBI-79/ST-1338): state B vinkjes-UI + 'Sprint opslaan'-knop met teller

State B (actieve sprint geselecteerd, geen draft) hangt nu aan dezelfde
vinkje-UI als state A′, maar muteert de transient pending-buffer in plaats
van de draft.

- PbiList: nieuwe prop activeSprintId. selectionMode = hasDraft ||
  stateBMode. togglePbiInDraft routeert naar upsertPbiIntent (A′) of bulk-
  toggleStorySprintMembership over eligible child-stories (B, skip blocked).
- StoryPanel: idem prop activeSprintId. StoryBlockWithCherrypick muteert
  draft via upsertStoryOverride in A′ of pending buffer via
  toggleStorySprintMembership in B (cross-sprint blocked = disabled).
- SaveSprintButton (nieuw): client component in page header, alleen
  zichtbaar als er een actieve sprint is. Disabled bij clean buffer,
  enabled met teller bij dirty. Klikken calls commitSprintMembershipAction
  → applyMembershipCommitResult gericht in store + toast bij conflicts.
- page.tsx: activeSprintItem.id wordt doorgegeven aan PbiList, StoryPanel
  en SaveSprintButton.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(PBI-79/ST-1341+ST-1342): SprintEditDialog metadata-edit + multi-OPEN sprints

ST-1341 (T-946):
- actions/sprints.ts: nieuwe updateSprintAction(sprintId, fields) — JSON
  input, accepteert optionele goal/startAt/endAt; auth + product-access
  check, prisma.sprint.update, revalidatePath. Type-safe return.
- components/backlog/sprint-edit-dialog.tsx: Entity-Dialog-pattern voor
  metadata-edit van een sprint. Velden: sprint_goal, start_date, end_date.
  Link 'Sprint afronden… →' naar bestaande /products/[id]/sprint/[sprintId]
  zodat de completion-flow (per-story DONE/OPEN beslissing + PBI-promotie)
  niet wordt geduplicereerd. useDirtyCloseGuard.

ST-1342 (T-947):
- actions/sprints.ts: OPEN-uniqueness check in createSprintAction
  verwijderd. Een product mag nu meerdere OPEN sprints tegelijk hebben;
  cross-sprint-conflicts per story worden afgevangen door
  partitionByEligibility in de membership-commit-flow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(PBI-79/ST-1344): updateSprintAction regression coverage

Audits van de geplande non-regressie-tests laten zien dat alle invarianten
uit het ST-1344 plan reeds gedekt zijn door eerder toegevoegde tests:

- clearActiveSprintAction null-not-delete → __tests__/lib/active-sprint.test.ts
  + __tests__/actions/active-sprint-action.test.ts
- Endpoints rejecten zonder pbiIds (400) → __tests__/api/sprint-membership-summary.test.ts
  + __tests__/api/cross-sprint-blocks.test.ts
- Status-mutaties story.status=IN_SPRINT/OPEN met task.sprint_id cascade
  in dezelfde transactie → __tests__/actions/create-sprint-with-selection.test.ts
  + __tests__/actions/commit-sprint-membership.test.ts
- Cross-sprint conflicts + DONE-eligibility → __tests__/lib/sprint-conflicts.test.ts

Nieuw: __tests__/actions/update-sprint.test.ts (6 cases) dekt
updateSprintAction die nog geen tests had — goal alleen, dates alleen,
null-clear, 403 zonder access, lege goal weigering, leeg fields-object
weigering.

Handmatige E2E checklist (T-949) blijft staan voor menselijke browser-
validatie tijdens PR-review.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(PBI-79): PBI-rij selecteert weer in A′/B-modus; vinkje is aparte trigger

Voor PBI-79 maakte het hele PBI-kaartje in selectionMode (state A′ én B)
de toggle. Daardoor:
- klik op rij = bulk-toggle stories (teller liep op);
- geen setActivePbi, dus StoryPanel kreeg geen content.

Fix: in selectionMode wordt onClick = onSelect (PBI activeren → stories
laden) en de tri-state-iconen verhuizen naar een eigen <button> in de
actions-slot met stopPropagation. Toggle gedrag (bulk add/remove in B,
upsertPbiIntent in A′) blijft ongewijzigd via die knop.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(PBI-79): cascade-restore alleen als hint-story bij nieuwe PBI hoort

Bug: setActivePbi reset activeStoryId/activeTaskId, maar het cascade-
restore-pad zette daarna een hint-story actief zonder te valideren of die
story bij de nieuw-geselecteerde PBI hoort. Bij PBI-switch bleef daardoor
de task-kolom de taken van de vorige story tonen.

Fix: alleen setActiveStory(hint) als entities.storiesById[hint].pbi_id ===
pbiId. Bij mismatch blijft activeStoryId null en is de task-kolom leeg
totdat de gebruiker een story uit de nieuwe PBI kiest.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(PBI-79): sprint-switch auto-select PBI/story + user-settings persist

Bij sprint-switch wordt de sprint-content server-side opgevraagd. Wanneer
de sprint precies één PBI (en die PBI exact één story binnen de sprint)
heeft, worden PBI en story automatisch geselecteerd. Alle drie keuzes
(sprint, pbi, story) worden atomair in user-settings opgeslagen zodat ze
cross-device blijven hangen.

- lib/user-settings.ts: layout krijgt nullable activePbis +
  activeStories per product.
- lib/active-sprint.ts: setActiveSelectionInSettings schrijft de drie
  keys atomair + notify pg_notify.
- actions/active-sprint.ts: switchActiveSprintAction(productId, sprintId)
  doet de server-side auto-select-resolutie (single PBI → single story)
  en returnt { sprintId, pbiId, storyId }.
- components/shared/sprint-switcher.tsx: handleSwitchSprint roept de
  nieuwe action aan en synchroniseert de workspace-store gelijk zodat
  de UI geen flash krijgt voor de SSR-refresh.
- components/backlog/active-selection-hydrator.tsx (nieuw): client-side
  effect dat user-settings.activePbis/activeStories naar workspace-store
  spiegelt; wint van de localStorage hint-restore.
- app/(app)/products/[id]/page.tsx: ActiveSelectionHydrator gemount
  binnen BacklogHydrationWrapper.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(PBI-79): plan-update met implementatie-stand + scope-aanpassing

Documenteert wat er sinds de eerste implementatie-pass is gebeurd:
- Tabel van 14 commits met hun rol.
- Twee bugs die tijdens testen boven kwamen (PBI-rij-klik, cascade-restore).
- Nieuwe feature sprint-switch auto-select (server resolveert single-PBI/
  single-story; user-settings persist).

En kondigt scope-aanpassing aan voor de volgende implementatie-ronde:
- pendingSprintDraft wordt session-only (geen server-persist meer).
- useDirtyCloseGuard wist draft op leave-with-confirm.
- Sprint-switcher krijgt concept-entry zolang er een draft loopt.

De rest van het plan beneden blijft van kracht behalve waar deze sectie
het overruled.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(PBI-79): pendingSprintDraft session-only + concept-entry + leave-guard

Scope-aanpassing uit plan-revisie: drafts persisten niet meer server-side.

Wijzigingen:
- stores/user-settings/store.ts:
  - hydrate() strip nu workflow.pendingSprintDraft uit serverstate
    (legacy DB-entries blijven harmless aanwezig maar worden niet
    gehydreerd → effectief unreachable voor de UI).
  - setPendingSprintDraft / clearPendingSprintDraft worden lokale-only;
    geen import van sprint-draft-actions, geen server-roundtrip.
  - upsertPbiIntent / upsertStoryOverride blijven via setPendingSprintDraft
    routeren → ook session-only.
- components/shared/sprint-switcher.tsx: leest draft-goal uit user-settings
  store en toont '⚙ Concept — [goal]' als niet-selecteerbare entry
  bovenaan de dropdown zolang er een draft loopt.
- components/backlog/sprint-draft-leave-guard.tsx (nieuw): registreert
  een beforeunload-listener zolang er een draft is. Browser-refresh,
  tab-close en back-navigatie tonen daarmee de standaard confirm. In-app
  route-changes blijven via de banner-Annuleren-knop lopen.
- app/(app)/products/[id]/page.tsx: SprintDraftLeaveGuard gemount naast
  de banner.
- Tests: user-settings store-tests aangepast (geen server-call assert
  meer, hydrate strip-assert toegevoegd; upsert-tests seed nu via
  setPendingSprintDraft i.p.v. legacy hydrate).

setPendingSprintDraftAction + clearPendingSprintDraftAction blijven bestaan
voor eventuele toekomstige opruim-flows, maar worden niet meer aangeroepen
vanuit de UI.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(PBI-79): mark scope-aanpassing afgerond + localStorage overzicht

- Drie open punten uit plan-revisie afgevinkt (commit 2a4ee6a).
- Sectie 'Bewust niet geïmplementeerd': server-persist van manuele
  PBI/story-klikken — op vraag van user nu out-of-scope voor deze PR.
- Tabel localStorage-gebruik in de codebase voor toekomstige referentie.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 18:56:46 +02:00
.github/workflows chore(ci): gate auto-deploy behind AUTO_DEPLOY_ENABLED repo-variable (#154) 2026-05-07 20:17:15 +02:00
.husky docs: AI-optimized docs restructure (Phases 1–8) (#61) 2026-05-03 03:21:59 +02:00
.icons chore: middleware.ts verwijderd, icon-bron toegevoegd, versie 0.2.0 2026-04-24 23:05:00 +02:00
__tests__ feat(PBI-79): Product Backlog sprint-membership via vinkjes (#190) 2026-05-11 18:56:46 +02:00
actions feat(PBI-79): Product Backlog sprint-membership via vinkjes (#190) 2026-05-11 18:56:46 +02:00
app feat(PBI-79): Product Backlog sprint-membership via vinkjes (#190) 2026-05-11 18:56:46 +02:00
components feat(PBI-79): Product Backlog sprint-membership via vinkjes (#190) 2026-05-11 18:56:46 +02:00
docs feat(PBI-79): Product Backlog sprint-membership via vinkjes (#190) 2026-05-11 18:56:46 +02:00
hooks fix(PBI-59): map jobs_initial SSE payload by job_id, not id (#155) 2026-05-07 20:22:07 +02:00
lib feat(PBI-79): Product Backlog sprint-membership via vinkjes (#190) 2026-05-11 18:56:46 +02:00
prisma feat(PBI-76): user-settings DB-store infrastructure (Phase 0) (#185) 2026-05-10 12:44:32 +02:00
public Sprint: pbi-55 (#156) 2026-05-07 21:46:01 +02:00
scripts feat(PBI-76): migrate localStorage prefs to user-settings store (Phase 1) (#188) 2026-05-10 15:13:39 +02:00
stores feat(PBI-79): Product Backlog sprint-membership via vinkjes (#190) 2026-05-11 18:56:46 +02:00
tests feat(PBI-74): Zustand product-workspace rearchitecture (Stories 1-8) (#180) 2026-05-10 02:25:19 +02:00
.env.example feat(PBI-66): wekelijkse sync van model_prices via Anthropic /v1/models (#167) 2026-05-08 09:38:33 +02:00
.gitattributes chore: .gitattributes toevoegen voor consistente LF regeleindes 2026-04-24 23:06:21 +02:00
.gitignore chore: ignore .claude/worktrees in git (#166) 2026-05-08 09:29:59 +02:00
AGENTS.md Agent batch-flow: lokaal committen, push + PR aan het eind (#66) 2026-05-03 15:51:24 +02:00
CHANGELOG.md chore: bump version to 1.0.0 2026-05-04 14:25:15 +02:00
CLAUDE.md feat(PBI-74): Zustand product-workspace rearchitecture (Stories 1-8) (#180) 2026-05-10 02:25:19 +02:00
components.json feat: ST-001–ST-005 foundation — scaffolding, Prisma, schema, seed, env 2026-04-22 21:04:48 +02:00
eslint.config.mjs Load/render workspace alignment (#182) 2026-05-10 07:34:58 +02:00
instrumentation-client.ts feat(ops): Sentry error-monitoring (v1-readiness item 2) 2026-05-04 13:24:19 +02:00
instrumentation.ts feat(ops): Sentry error-monitoring (v1-readiness item 2) 2026-05-04 13:24:19 +02:00
next.config.ts feat(ops): Sentry error-monitoring (v1-readiness item 2) 2026-05-04 13:24:19 +02:00
package-lock.json feat(PBI-76): user-settings DB-store infrastructure (Phase 0) (#185) 2026-05-10 12:44:32 +02:00
package.json feat: shared backlog filter popover + sprint header polish (v1.3.3) (#184) 2026-05-10 11:12:04 +02:00
postcss.config.mjs Initial commit from Create Next App 2026-04-22 20:25:19 +02:00
prisma.config.ts fix: url en directUrl uit schema.prisma verplaatst naar prisma.config.ts (Prisma v7) 2026-04-24 14:26:44 +02:00
proxy.ts proxy: add /ideas to protectedRoutes; verify demo-guard for /api/ideas (M12 T-501) 2026-05-04 19:56:41 +02:00
README.md chore: remove prisma-erd-generator and stale erd refs 2026-05-08 14:45:39 +02:00
sentry.edge.config.ts feat(ops): Sentry error-monitoring (v1-readiness item 2) 2026-05-04 13:24:19 +02:00
sentry.server.config.ts feat(ops): Sentry error-monitoring (v1-readiness item 2) 2026-05-04 13:24:19 +02:00
tsconfig.json Initial commit from Create Next App 2026-04-22 20:25:19 +02:00
vercel.json feat(T-553): vercel.json git.deploymentEnabled=false + GitHub-labels 2026-05-05 23:31:34 +02:00
vitest.config.ts Load/render workspace alignment (#182) 2026-05-10 07:34:58 +02:00

Scrum4Me Agile Project Management Tool

Portfolio samenvatting

Scrum4Me is een moderne fullstack webapplicatie voor agile projectmanagement.
De applicatie is gebouwd als portfolio-project om mijn vaardigheden in moderne softwareontwikkeling, cloud deployment en AI-assisted development te demonstreren.

Doel

Veel teams missen overzicht en flexibiliteit in agile workflows.
Scrum4Me biedt een lichtgewicht, web-based oplossing voor het beheren van sprints, taken en teamprocessen.

Mijn rol

  • Architectuur en ontwerp
  • Fullstack development (frontend + backend)
  • Database ontwerp
  • Implementatie van authenticatie en API's
  • CI/CD en deployment
  • AI-assisted development workflow

Functionaliteiten

  • Agile dashboards en product backlogs
  • PBI-, story-, sprint- en taakbeheer
  • Authenticatie en gebruikersbeheer
  • Teamtoegang via eigenaar of gekoppelde Developer
  • API tokens voor externe integraties
  • REST API voor Claude Code workflows
  • Drag-and-drop interactie voor planning
  • Story-activiteitenlog voor plannen, testresultaten en commits
  • Profielfoto, bio en rolbeheer

Technologie stack

  • Next.js 16 (App Router)
  • React 19
  • TypeScript
  • Prisma ORM
  • PostgreSQL (Neon)
  • iron-session en bcryptjs
  • Zustand
  • dnd-kit
  • Tailwind CSS en shadcn/ui
  • Sharp voor avatarverwerking
  • Vercel Analytics
  • Vercel hosting
  • GitHub Actions / CI-CD

Documentation

Architectuur (kort)

  • Frontend en backend via Next.js App Router
  • Server Components voor data loading
  • Server Actions voor UI-mutaties
  • Route Handlers voor de externe REST API
  • Database via Prisma + PostgreSQL
  • Auth via versleutelde sessiecookies
  • Producttoegang via eigenaar of product_members
  • Deployment via Vercel met Neon als database

Live demo

Voeg hier je Vercel link toe.

Screenshots

Voeg hier screenshots toe van dashboard, product backlog, sprint planning en instellingen.

Wat ik geleerd heb

  • Werken met moderne fullstack architectuur in Next.js
  • Databaseontwerp met Prisma en PostgreSQL
  • Server Actions combineren met REST API Route Handlers
  • Beveiliging van cross-user en cross-scope toegang
  • AI-assisted development integreren in een eigen workflow
  • Cloud deployment en verificatie via Vercel
  • Documentatie en agent-instructies verbeteren op basis van code review

Toekomstige verbeteringen

  • Multi-user samenwerking verder uitbreiden
  • Notificaties
  • Performance optimalisatie
  • Uitbreiding AI-functionaliteit
  • Meer integratietests voor autorisatie en API-flows

Repository

https://github.com/madhura68/Scrum4Me


Technische aanvulling

Deze sectie bevat de praktische projectinformatie die nodig is om de app lokaal te draaien, te deployen en veilig door te ontwikkelen.

Lokale setup

  1. Installeer dependencies:
npm ci
  1. Maak lokale environment variabelen:
cp .env.example .env.local

Vul daarna DATABASE_URL en SESSION_SECRET in. DIRECT_URL is optioneel lokaal, maar handig voor migraties in cloudomgevingen.

  1. Synchroniseer of migreer de database:
npx prisma db push
  1. Genereer Prisma Client:
npx prisma generate
  1. Seed testdata indien nodig:
npx prisma db seed
  1. Start de app:
npm run dev

Testing

Unit tests (Vitest, geen database vereist):

npm test

Verwacht: alle 445 tests slagen, 0 failures.

API curl-tests (vereist lopende dev server + API token):

# Zie scripts/README.md voor setup-instructies
bash scripts/test-api.sh

De curl-tests dekken alle 7 API-endpoints: auth (401), demo-blokkering (403), inputvalidatie (400) en happy paths. Zie docs/qa/api-test-plan.md voor het volledige testplan.

Database

Het schema staat in prisma/schema.prisma; uitgebreide documentatie in docs/architecture/data-model.md.

Gebruik npx prisma db push om schema-wijzigingen naar de database te synchroniseren. npx prisma generate (of prisma generate --generator client in CI) genereert de Prisma Client.

De app draait standaard op http://localhost:3000.

Scripts

npm run dev      # lokale development server
npm run lint     # ESLint
npm test         # Vitest test suite
npm run build    # productiebuild zoals Vercel die verwacht

Environment variables

Zie .env.example.

Variabele Verplicht Doel
DATABASE_URL Ja PostgreSQL connection string voor Prisma
DIRECT_URL Nee Directe Neon connection string voor migraties (Prisma directUrl)
SESSION_SECRET Ja Minimaal 32 tekens; gebruikt door iron-session
CRON_SECRET Productie Bearer-secret voor /api/cron/* routes — required als crons aan staan
NEXT_PUBLIC_VAPID_PUBLIC_KEY Nee VAPID public key voor Web Push — genereer met npx web-push generate-vapid-keys
VAPID_PRIVATE_KEY Nee VAPID private key voor Web Push
VAPID_SUBJECT Nee Contact URI voor Web Push (bijv. mailto:admin@example.com)
INTERNAL_PUSH_SECRET Nee Bearer-secret voor /api/internal/push/* routes (min 32 tekens)
NEXT_PUBLIC_SENTRY_DSN Nee Sentry DSN — zonder is de SDK een no-op
SENTRY_ORG / SENTRY_PROJECT / SENTRY_AUTH_TOKEN Nee Source-map upload tijdens build
NODE_ENV Nee Wordt door Node/Vercel gezet

Vercel Analytics gebruikt geen project-specifieke environment variabele in deze app; de component staat in app/layout.tsx.

Commit Guidelines

We follow a strict commit structure to keep the project maintainable and reviewable.

Rules

  • 1 commit = 1 logical change
  • Do not mix:
    • features + docs
    • features + config
    • schema + UI
  • Keep commits small and focused
  • Prefer multiple small commits over one large commit

Commit format

We use a simplified conventional commit style:

  • feat: new feature
  • fix: bug fix
  • docs: documentation only
  • chore: config / tooling / cleanup
  • refactor: code improvement without behavior change

Examples

Good:

feat(db): add user profile fields
feat(api): add avatar upload endpoint
feat(ui): add profile editor
docs: document profile feature

Bad:

feat: add profile + update docs + fix config

API-overzicht

Alle API-endpoints vereisen:

Authorization: Bearer <token>
Methode Endpoint Doel
GET /api/health Liveness; ?db=1 doet ook een DB-ping (geen auth)
GET /api/products Actieve producten waarvoor de tokengebruiker eigenaar of teamlid is
GET /api/products/:id/next-story Hoogst geprioriteerde open story uit de actieve sprint
GET /api/products/:id/claude-context Bundled product / actieve sprint / next-story (met tasks) / open ideas voor MCP
GET /api/sprints/:id/tasks?limit=10 Eerste taken van een sprint
PATCH /api/stories/:id/tasks/reorder Taakvolgorde aanpassen; alle IDs moeten bij de story horen
POST /api/stories/:id/log Implementatieplan, testresultaat of commit vastleggen
PATCH /api/tasks/:id Taakstatus of implementation_plan bijwerken
GET / POST /api/ideas · GET / PATCH /api/ideas/:id Idea CRUD (M12 — vervangt voormalige /api/todos)
GET /api/jobs/:id/sub-tasks sprint_task_executions van een SPRINT_IMPLEMENTATION-job
GET /api/users/:id/avatar Avatar van een specifieke gebruiker
POST / GET /api/profile/avatar Eigen avatar uploaden of opvragen

Daarnaast leveren /api/realtime/{backlog,solo,jobs,notifications} SSE-streams en zijn er auth-helpers /api/auth/pair/* (QR-pairing, M10), interne push-routes onder /api/internal/push/*, en cron-handlers (/api/cron/cleanup-agent-artifacts, /api/cron/expire-questions).

Security-regels

  • Server Actions en Route Handlers vertrouwen nooit op losse client-ID's zonder scope-check.
  • Producttoegang loopt via eigenaar of product_members.
  • Bulk-mutaties valideren eerst dat alle IDs bij dezelfde toegankelijke parent horen.
  • Denormalized fields zoals story.product_id worden afgeleid van de database-parent, niet van form-data.
  • Demo-gebruikers krijgen 403 op schrijfoperaties.

Deployment

De productieomgeving is gericht op Vercel + Neon.

  1. Zet DATABASE_URL, eventueel DIRECT_URL, en SESSION_SECRET in Vercel.
  2. Zorg dat de Neon-database gemigreerd is.
  3. Push naar main; Vercel deployt automatisch.
  4. Controleer na deployment:
    • login en dashboard
    • /api/products met een API-token
    • avatar-upload
    • Vercel Analytics in het Vercel dashboard

Documentatie