Add analytics and documentation updates
This commit is contained in:
parent
e0efb65efb
commit
b5e967d8d3
15 changed files with 414 additions and 37 deletions
10
.env.example
Normal file
10
.env.example
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
# Database
|
||||||
|
DATABASE_URL="postgresql://user:password@host/dbname?sslmode=require"
|
||||||
|
DIRECT_URL="postgresql://user:password@host/dbname?sslmode=require"
|
||||||
|
|
||||||
|
# Auth/session
|
||||||
|
# Generate with: openssl rand -base64 32
|
||||||
|
SESSION_SECRET="replace-with-at-least-32-characters"
|
||||||
|
|
||||||
|
# Optional; Vercel and Node set this automatically in deployed environments.
|
||||||
|
NODE_ENV="development"
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -32,6 +32,7 @@ yarn-error.log*
|
||||||
|
|
||||||
# env files (can opt-in for committing if needed)
|
# env files (can opt-in for committing if needed)
|
||||||
.env*
|
.env*
|
||||||
|
!.env.example
|
||||||
|
|
||||||
# vercel
|
# vercel
|
||||||
.vercel
|
.vercel
|
||||||
|
|
@ -47,4 +48,4 @@ next-env.d.ts
|
||||||
*.db-wal
|
*.db-wal
|
||||||
|
|
||||||
# Claude Code local settings
|
# Claude Code local settings
|
||||||
.claude/settings.local.json
|
.claude/settings.local.json
|
||||||
|
|
|
||||||
33
AGENTS.md
33
AGENTS.md
|
|
@ -3,3 +3,36 @@
|
||||||
|
|
||||||
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices.
|
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices.
|
||||||
<!-- END:nextjs-agent-rules -->
|
<!-- END:nextjs-agent-rules -->
|
||||||
|
|
||||||
|
# Scrum4Me Codex Rules
|
||||||
|
|
||||||
|
Read `CLAUDE.md` and the relevant files in `docs/` before changing behavior. The same product and security rules apply to Codex work.
|
||||||
|
|
||||||
|
## Access Control
|
||||||
|
|
||||||
|
- Product-scoped access is owner-or-member: use `productAccessFilter(userId)` from `lib/product-access.ts`.
|
||||||
|
- Use owner-only `user_id` checks only for actions that truly require ownership, such as product archiving and team management.
|
||||||
|
- Never trust client-provided IDs by themselves. For reorder, promotion, completion, or bulk updates, fetch the records with both `id in (...)` and the parent scope (`product_id`, `pbi_id`, `sprint_id`, or `story_id`) before writing.
|
||||||
|
- Reject duplicate IDs in ordered lists or decision payloads.
|
||||||
|
- Derive denormalized fields from database parents, for example `pbi.product_id`, not from form data or JSON bodies.
|
||||||
|
- Demo users and demo API tokens must receive 403 on write operations.
|
||||||
|
|
||||||
|
## Documentation Sync
|
||||||
|
|
||||||
|
When changing behavior, API responses, dependencies, environment variables, deployment behavior, or analytics, update the matching docs in the same change:
|
||||||
|
|
||||||
|
- `README.md` for setup, dependencies, deployment, and API overview.
|
||||||
|
- `docs/scrum4me-functional-spec.md` for user-facing/API requirements.
|
||||||
|
- `docs/scrum4me-architecture.md` for stack, access model, data model, env vars, and deployment.
|
||||||
|
- `docs/patterns/` when a reusable implementation rule changes.
|
||||||
|
- `CLAUDE.md` and this file when an agent instruction would have prevented the issue.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
Before handing work back, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run lint
|
||||||
|
npm test
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
|
||||||
10
CLAUDE.md
10
CLAUDE.md
|
|
@ -22,6 +22,7 @@ Lees het relevante document voordat je aan een feature begint. Nooit gokken over
|
||||||
| `docs/scrum4me-personas.md` | Lars (primair), Dina, Remi — gebruik bij UI-beslissingen |
|
| `docs/scrum4me-personas.md` | Lars (primair), Dina, Remi — gebruik bij UI-beslissingen |
|
||||||
| `docs/scrum4me-product-backlog.md` | Testdata voor de seed |
|
| `docs/scrum4me-product-backlog.md` | Testdata voor de seed |
|
||||||
| `docs/scrum4me-styling.md` | **Lees dit voor elk component** — MD3-kleuren, shadcn patronen |
|
| `docs/scrum4me-styling.md` | **Lees dit voor elk component** — MD3-kleuren, shadcn patronen |
|
||||||
|
| `docs/agent-instruction-audit.md` | Waarom de agent-instructies zijn aangescherpt; checklist voor toekomstige wijzigingen |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -58,6 +59,8 @@ dnd-kit (drag-and-drop)
|
||||||
Prisma v7 + PostgreSQL (Neon)
|
Prisma v7 + PostgreSQL (Neon)
|
||||||
iron-session (auth cookies)
|
iron-session (auth cookies)
|
||||||
bcryptjs + Zod + Sonner
|
bcryptjs + Zod + Sonner
|
||||||
|
Sharp (avatarverwerking)
|
||||||
|
Vercel Analytics (`@vercel/analytics/next`)
|
||||||
```
|
```
|
||||||
|
|
||||||
> ⚠️ **Stylingregel:** Gebruik **nooit** `bg-blue-500` of willekeurige Tailwind-kleuren.
|
> ⚠️ **Stylingregel:** Gebruik **nooit** `bg-blue-500` of willekeurige Tailwind-kleuren.
|
||||||
|
|
@ -100,9 +103,13 @@ SESSION_SECRET="" # openssl rand -base64 32
|
||||||
- **Branches:** `feat/ST-001-scaffolding`
|
- **Branches:** `feat/ST-001-scaffolding`
|
||||||
- **Server Actions:** altijd in `actions/[domein].ts`, nooit inline in page.tsx
|
- **Server Actions:** altijd in `actions/[domein].ts`, nooit inline in page.tsx
|
||||||
- **Validatie:** altijd Zod, nooit handmatige checks
|
- **Validatie:** altijd Zod, nooit handmatige checks
|
||||||
- **Eigenaarschap:** elke Server Action en Route Handler controleert dat de resource bij de ingelogde gebruiker hoort
|
- **Toegangsmodel:** product-scoped resources gebruiken `productAccessFilter(userId)` tenzij het expliciet een eigenaarsactie is
|
||||||
|
- **Bulk-ID's:** reorder- en beslissingsacties valideren dat alle meegegeven IDs binnen dezelfde parent-scope vallen voordat er geschreven wordt
|
||||||
|
- **Foreign keys:** denormalized keys zoals `story.product_id` worden afgeleid uit de database-parent (`pbi.product_id`), nooit uit client-input
|
||||||
- **Demo-check:** elke Server Action controleert `session.isDemo` vóór schrijven
|
- **Demo-check:** elke Server Action controleert `session.isDemo` vóór schrijven
|
||||||
- **Foutberichten:** Nederlands voor eindgebruikers — comments in code: Engels
|
- **Foutberichten:** Nederlands voor eindgebruikers — comments in code: Engels
|
||||||
|
- **Dependencies:** elke geïmporteerde runtime package staat direct in `dependencies`, niet alleen transitief in `package-lock.json`
|
||||||
|
- **Docs-sync:** elke gedrags-, dependency-, API- of deploymentwijziging werkt README, relevante docs en patterns bij in dezelfde change
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -126,3 +133,4 @@ SESSION_SECRET="" # openssl rand -base64 32
|
||||||
- [ ] App opzetbaar via README zonder extra hulp
|
- [ ] App opzetbaar via README zonder extra hulp
|
||||||
- [ ] CI/CD actief — falende build blokkeert merge
|
- [ ] CI/CD actief — falende build blokkeert merge
|
||||||
- [ ] Beveiligingsreview API geslaagd (cross-user toegang onmogelijk)
|
- [ ] Beveiligingsreview API geslaagd (cross-user toegang onmogelijk)
|
||||||
|
- [ ] Documentatie is bijgewerkt voor gewijzigde API's, dependencies, deployment en agent-instructies
|
||||||
|
|
|
||||||
173
README.md
173
README.md
|
|
@ -1,64 +1,199 @@
|
||||||
# Scrum4Me – Agile Project Management Tool
|
# Scrum4Me – Agile Project Management Tool
|
||||||
|
|
||||||
## Portfolio samenvatting
|
## Portfolio samenvatting
|
||||||
|
|
||||||
Scrum4Me is een moderne fullstack webapplicatie voor agile projectmanagement.
|
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.
|
De applicatie is gebouwd als portfolio-project om mijn vaardigheden in moderne softwareontwikkeling, cloud deployment en AI-assisted development te demonstreren.
|
||||||
|
|
||||||
## Doel
|
## Doel
|
||||||
|
|
||||||
Veel teams missen overzicht en flexibiliteit in agile workflows.
|
Veel teams missen overzicht en flexibiliteit in agile workflows.
|
||||||
Scrum4Me biedt een lichtgewicht, web-based oplossing voor het beheren van sprints, taken en teamprocessen.
|
Scrum4Me biedt een lichtgewicht, web-based oplossing voor het beheren van sprints, taken en teamprocessen.
|
||||||
|
|
||||||
## Mijn rol
|
## Mijn rol
|
||||||
|
|
||||||
- Architectuur en ontwerp
|
- Architectuur en ontwerp
|
||||||
- Fullstack development (frontend + backend)
|
- Fullstack development (frontend + backend)
|
||||||
- Database ontwerp
|
- Database ontwerp
|
||||||
- Implementatie van authenticatie en API’s
|
- Implementatie van authenticatie en API's
|
||||||
- CI/CD en deployment
|
- CI/CD en deployment
|
||||||
|
- AI-assisted development workflow
|
||||||
|
|
||||||
## Functionaliteiten
|
## Functionaliteiten
|
||||||
- Agile dashboards (scrum boards)
|
|
||||||
- Taakbeheer (create/update/delete)
|
- Agile dashboards en product backlogs
|
||||||
|
- PBI-, story-, sprint- en taakbeheer
|
||||||
- Authenticatie en gebruikersbeheer
|
- Authenticatie en gebruikersbeheer
|
||||||
|
- Teamtoegang via eigenaar of gekoppelde Developer
|
||||||
- API tokens voor externe integraties
|
- API tokens voor externe integraties
|
||||||
- Drag-and-drop interactie
|
- REST API voor Claude Code workflows
|
||||||
- Integratie met AI tooling (Claude Code)
|
- Drag-and-drop interactie voor planning
|
||||||
|
- Story-activiteitenlog voor plannen, testresultaten en commits
|
||||||
|
- Profielfoto, bio en rolbeheer
|
||||||
|
|
||||||
## Technologie stack
|
## Technologie stack
|
||||||
- Next.js (App Router)
|
|
||||||
- React
|
- Next.js 16 (App Router)
|
||||||
|
- React 19
|
||||||
- TypeScript
|
- TypeScript
|
||||||
- Prisma ORM
|
- Prisma ORM
|
||||||
- PostgreSQL (Neon)
|
- PostgreSQL (Neon)
|
||||||
- Vercel (hosting)
|
- iron-session en bcryptjs
|
||||||
- GitHub Actions (CI/CD)
|
- Zustand
|
||||||
|
- dnd-kit
|
||||||
|
- Tailwind CSS en shadcn/ui
|
||||||
|
- Sharp voor avatarverwerking
|
||||||
|
- Vercel Analytics
|
||||||
|
- Vercel hosting
|
||||||
|
- GitHub Actions / CI-CD
|
||||||
|
|
||||||
## Architectuur (kort)
|
## Architectuur (kort)
|
||||||
- Frontend en backend via Next.js
|
|
||||||
|
- 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
|
- Database via Prisma + PostgreSQL
|
||||||
- Auth en API via server routes
|
- Auth via versleutelde sessiecookies
|
||||||
- Deployment via Vercel
|
- Producttoegang via eigenaar of `product_members`
|
||||||
|
- Deployment via Vercel met Neon als database
|
||||||
|
|
||||||
## Live demo
|
## Live demo
|
||||||
👉 Voeg hier je Vercel link toe
|
|
||||||
|
Voeg hier je Vercel link toe.
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
👉 Voeg hier screenshots toe (dashboard, board, etc.)
|
|
||||||
|
Voeg hier screenshots toe van dashboard, product backlog, sprint planning en instellingen.
|
||||||
|
|
||||||
## Wat ik geleerd heb
|
## Wat ik geleerd heb
|
||||||
- Werken met moderne fullstack architectuur
|
|
||||||
- Integratie van database via Prisma
|
- Werken met moderne fullstack architectuur in Next.js
|
||||||
- Opzetten van CI/CD pipelines
|
- Databaseontwerp met Prisma en PostgreSQL
|
||||||
- Structureren van schaalbare webapplicaties
|
- 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
|
## Toekomstige verbeteringen
|
||||||
- Multi-user samenwerking verbeteren
|
|
||||||
|
- Multi-user samenwerking verder uitbreiden
|
||||||
- Notificaties
|
- Notificaties
|
||||||
- Performance optimalisatie
|
- Performance optimalisatie
|
||||||
- Uitbreiding AI-functionaliteit
|
- Uitbreiding AI-functionaliteit
|
||||||
|
- Meer integratietests voor autorisatie en API-flows
|
||||||
|
|
||||||
## Repository
|
## Repository
|
||||||
|
|
||||||
https://github.com/madhura68/Scrum4Me
|
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:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm ci
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Maak lokale environment variabelen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example .env.local
|
||||||
|
```
|
||||||
|
|
||||||
|
Vul daarna `DATABASE_URL` en `SESSION_SECRET` in. `DIRECT_URL` is optioneel lokaal, maar handig voor migraties in cloudomgevingen.
|
||||||
|
|
||||||
|
3. Synchroniseer of migreer de database:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx prisma db push
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Seed testdata indien nodig:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx prisma db seed
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Start de app:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
De app draait standaard op `http://localhost:3000`.
|
||||||
|
|
||||||
|
### Scripts
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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](.env.example).
|
||||||
|
|
||||||
|
| Variabele | Verplicht | Doel |
|
||||||
|
|---|---:|---|
|
||||||
|
| `DATABASE_URL` | Ja | PostgreSQL connection string voor Prisma |
|
||||||
|
| `DIRECT_URL` | Nee | Directe Neon connection string voor migraties |
|
||||||
|
| `SESSION_SECRET` | Ja | Minimaal 32 tekens; gebruikt door iron-session |
|
||||||
|
| `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`.
|
||||||
|
|
||||||
|
### API-overzicht
|
||||||
|
|
||||||
|
Alle API-endpoints vereisen:
|
||||||
|
|
||||||
|
```http
|
||||||
|
Authorization: Bearer <token>
|
||||||
|
```
|
||||||
|
|
||||||
|
| Methode | Endpoint | Doel |
|
||||||
|
|---|---|---|
|
||||||
|
| `GET` | `/api/products` | Actieve producten waarvoor de tokengebruiker eigenaar of teamlid is |
|
||||||
|
| `GET` | `/api/products/:id/next-story` | Volgende story uit de actieve sprint |
|
||||||
|
| `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 implementatieplan bijwerken |
|
||||||
|
| `POST` | `/api/todos` | Todo aanmaken binnen een productcontext |
|
||||||
|
|
||||||
|
### 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
|
||||||
|
|
||||||
|
- [Functionele specificatie](docs/scrum4me-functional-spec.md)
|
||||||
|
- [Technische architectuur](docs/scrum4me-architecture.md)
|
||||||
|
- [Backlog](docs/scrum4me-backlog.md)
|
||||||
|
- [Agent-instructie audit](docs/agent-instruction-audit.md)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { Geist, Geist_Mono } from "next/font/google";
|
import { Geist, Geist_Mono } from "next/font/google";
|
||||||
|
import { Analytics } from "@vercel/analytics/next";
|
||||||
import { Toaster } from "sonner";
|
import { Toaster } from "sonner";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
|
|
||||||
|
|
@ -43,6 +44,7 @@ export default function RootLayout({
|
||||||
>
|
>
|
||||||
<body className="min-h-full flex flex-col">
|
<body className="min-h-full flex flex-col">
|
||||||
{children}
|
{children}
|
||||||
|
<Analytics />
|
||||||
<Toaster richColors position="bottom-right" />
|
<Toaster richColors position="bottom-right" />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
88
docs/agent-instruction-audit.md
Normal file
88
docs/agent-instruction-audit.md
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
# Agent Instruction Audit
|
||||||
|
|
||||||
|
Datum: 2026-04-25
|
||||||
|
|
||||||
|
## Aanleiding
|
||||||
|
|
||||||
|
Tijdens de code review kwamen twee soorten fouten naar voren:
|
||||||
|
|
||||||
|
- Server Actions vertrouwden client-provided IDs te veel nadat alleen de parent-resource was gecontroleerd.
|
||||||
|
- Documentatie liep achter op dependency-, API- en deploymentwijzigingen.
|
||||||
|
|
||||||
|
Dit document legt vast welke wijzigingen zijn gecontroleerd, welke documentatie is bijgewerkt en welke instructies Claude Code en Codex voortaan expliciet moeten volgen.
|
||||||
|
|
||||||
|
## Gecontroleerde wijzigingen
|
||||||
|
|
||||||
|
| Wijziging | Code-locatie | Documentatie bijgewerkt |
|
||||||
|
|---|---|---|
|
||||||
|
| Reorder-acties valideren alle IDs binnen de juiste parent-scope | `actions/stories.ts`, `actions/sprints.ts` | `docs/scrum4me-architecture.md`, `docs/patterns/server-action.md`, `docs/patterns/sort-order.md`, `CLAUDE.md`, `AGENTS.md` |
|
||||||
|
| Sprint afronden accepteert alleen stories uit de actieve sprint | `actions/sprints.ts` | `docs/scrum4me-architecture.md`, `docs/patterns/server-action.md`, `AGENTS.md` |
|
||||||
|
| Todo-promotie gebruikt scoped todo lookup en `pbi.product_id` als bron van waarheid | `actions/todos.ts` | `docs/scrum4me-architecture.md`, `docs/patterns/server-action.md`, `CLAUDE.md`, `AGENTS.md` |
|
||||||
|
| `GET /api/products` retourneert ook gedeelde producten via `product_members` | `app/api/products/route.ts` | `README.md`, `docs/scrum4me-functional-spec.md`, `docs/scrum4me-backlog.md`, `docs/patterns/route-handler.md` |
|
||||||
|
| `sharp` is directe runtime dependency | `package.json`, `package-lock.json` | `README.md`, `docs/scrum4me-architecture.md`, `CLAUDE.md` |
|
||||||
|
| Vercel Analytics is toegevoegd aan de root layout | `app/layout.tsx`, `package.json`, `package-lock.json` | `README.md`, `docs/scrum4me-architecture.md`, `CLAUDE.md` |
|
||||||
|
| `.env.example` ontbrak ondanks verwijzingen in architectuurdocs | `.env.example` | `README.md`, `docs/scrum4me-architecture.md` |
|
||||||
|
|
||||||
|
## Preventieve regels
|
||||||
|
|
||||||
|
### Access-control regels
|
||||||
|
|
||||||
|
Agents mogen nooit aannemen dat een ID veilig is omdat de UI hem normaal gesproken correct doorgeeft. Elke Server Action en Route Handler moet bij writes de volledige resource-scope bewijzen.
|
||||||
|
|
||||||
|
Concrete regels:
|
||||||
|
|
||||||
|
- Gebruik `productAccessFilter(userId)` voor product-scoped resources waar eigenaar en teamlid toegang hebben.
|
||||||
|
- Gebruik owner-only filters alleen bij echte eigenaarsacties.
|
||||||
|
- Valideer bulk-ID's met `id in (...)` plus parent-scope voordat er wordt geschreven.
|
||||||
|
- Weiger dubbele IDs in reorder- en decision-payloads.
|
||||||
|
- Leid denormalized foreign keys af van de database-parent, niet van client-input.
|
||||||
|
- Valideer runtime waarden met Zod of expliciete runtime checks; TypeScript types alleen zijn onvoldoende.
|
||||||
|
- Gebruik scoped deletes/updates nadat ownership bewezen is.
|
||||||
|
|
||||||
|
### Documentatie-regels
|
||||||
|
|
||||||
|
Een codewijziging is niet klaar als de documentatie een oud contract beschrijft. Agents moeten bij elke wijziging nagaan of deze plekken geraakt worden:
|
||||||
|
|
||||||
|
- `README.md`: setup, scripts, dependencies, deployment, API-overzicht.
|
||||||
|
- `docs/scrum4me-functional-spec.md`: functionele eisen en API-contracten.
|
||||||
|
- `docs/scrum4me-architecture.md`: stack, datamodel, securitymodel, env vars, deployment.
|
||||||
|
- `docs/scrum4me-backlog.md`: "done when"-criteria en scope van backlog-items.
|
||||||
|
- `docs/patterns/`: herbruikbare implementatiepatronen.
|
||||||
|
- `CLAUDE.md` en `AGENTS.md`: agent-regels die de fout in de toekomst voorkomen.
|
||||||
|
|
||||||
|
### Dependency-regels
|
||||||
|
|
||||||
|
- Elke runtime import moet als directe dependency in `package.json` staan.
|
||||||
|
- Als een dependency aan deploymentgedrag raakt, moet README of architectuurdocs dat noemen.
|
||||||
|
- Na `npm install` moet `package-lock.json` meegaan in dezelfde change.
|
||||||
|
|
||||||
|
### Verification-regels
|
||||||
|
|
||||||
|
Voor oplevering:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run lint
|
||||||
|
npm test
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
Bij securitywijzigingen hoort minimaal een test die laat zien dat cross-user of cross-scope input wordt geweigerd. Als die test nog niet bestaat, noteer dat expliciet in de oplevering.
|
||||||
|
|
||||||
|
## Claude Code
|
||||||
|
|
||||||
|
`CLAUDE.md` is aangescherpt met:
|
||||||
|
|
||||||
|
- een verwijzing naar dit auditdocument;
|
||||||
|
- de directe stackwijzigingen voor Sharp en Vercel Analytics;
|
||||||
|
- expliciete access-control regels voor `productAccessFilter`, bulk-ID's en foreign-key afleiding;
|
||||||
|
- een docs-sync regel voor gedrags-, API-, dependency- en deploymentwijzigingen.
|
||||||
|
|
||||||
|
## Codex
|
||||||
|
|
||||||
|
`AGENTS.md` is uitgebreid van alleen Next.js-waarschuwing naar projectregels voor:
|
||||||
|
|
||||||
|
- access control;
|
||||||
|
- documentatie-sync;
|
||||||
|
- verificatiecommands.
|
||||||
|
|
||||||
|
Deze regels zijn korter dan `CLAUDE.md`, maar bewust dwingend: ze moeten direct gelezen worden bij elke Codex-taak in deze repo.
|
||||||
|
|
@ -36,6 +36,7 @@ export async function authenticateApiRequest(request: Request) {
|
||||||
// app/api/products/[id]/next-story/route.ts
|
// app/api/products/[id]/next-story/route.ts
|
||||||
import { authenticateApiRequest } from '@/lib/api-auth'
|
import { authenticateApiRequest } from '@/lib/api-auth'
|
||||||
import { prisma } from '@/lib/prisma'
|
import { prisma } from '@/lib/prisma'
|
||||||
|
import { productAccessFilter } from '@/lib/product-access'
|
||||||
|
|
||||||
export async function GET(
|
export async function GET(
|
||||||
request: Request,
|
request: Request,
|
||||||
|
|
@ -49,7 +50,7 @@ export async function GET(
|
||||||
const { id } = await params
|
const { id } = await params
|
||||||
|
|
||||||
const sprint = await prisma.sprint.findFirst({
|
const sprint = await prisma.sprint.findFirst({
|
||||||
where: { product_id: id, status: 'ACTIVE', product: { user_id: auth.userId } },
|
where: { product_id: id, status: 'ACTIVE', product: productAccessFilter(auth.userId) },
|
||||||
})
|
})
|
||||||
if (!sprint) {
|
if (!sprint) {
|
||||||
return Response.json({ error: 'Geen actieve Sprint gevonden' }, { status: 404 })
|
return Response.json({ error: 'Geen actieve Sprint gevonden' }, { status: 404 })
|
||||||
|
|
@ -88,3 +89,11 @@ export async function GET(
|
||||||
| POST | `/api/stories/:id/log` | Plan / testresultaat / commit vastleggen |
|
| POST | `/api/stories/:id/log` | Plan / testresultaat / commit vastleggen |
|
||||||
| PATCH | `/api/tasks/:id` | Taakstatus bijwerken |
|
| PATCH | `/api/tasks/:id` | Taakstatus bijwerken |
|
||||||
| POST | `/api/todos` | Todo aanmaken |
|
| POST | `/api/todos` | Todo aanmaken |
|
||||||
|
|
||||||
|
## Security-invarianten
|
||||||
|
|
||||||
|
- Elk endpoint start met `authenticateApiRequest`.
|
||||||
|
- Schrijf-endpoints geven `403` voor demo-tokens.
|
||||||
|
- Product-scoped reads en writes gebruiken `productAccessFilter(auth.userId)`, zodat eigenaar en gekoppeld teamlid hetzelfde toegangsmodel volgen.
|
||||||
|
- Endpoints die geordende ID-lijsten ontvangen valideren dat elke ID bij de parent-resource hoort voordat er wordt geupdated.
|
||||||
|
- JSON bodies worden met Zod gevalideerd; TypeScript types zijn geen runtime-beveiliging.
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import { cookies } from 'next/headers'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { prisma } from '@/lib/prisma'
|
import { prisma } from '@/lib/prisma'
|
||||||
import { SessionData, sessionOptions } from '@/lib/session'
|
import { SessionData, sessionOptions } from '@/lib/session'
|
||||||
|
import { productAccessFilter } from '@/lib/product-access'
|
||||||
|
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
productId: z.string().cuid(),
|
productId: z.string().cuid(),
|
||||||
|
|
@ -32,9 +33,9 @@ export async function createPbi(formData: FormData) {
|
||||||
})
|
})
|
||||||
if (!parsed.success) return { error: parsed.error.flatten().fieldErrors }
|
if (!parsed.success) return { error: parsed.error.flatten().fieldErrors }
|
||||||
|
|
||||||
// 3. Eigenaarschap controleren
|
// 3. Toegang controleren: eigenaar of gekoppeld product member
|
||||||
const product = await prisma.product.findFirst({
|
const product = await prisma.product.findFirst({
|
||||||
where: { id: parsed.data.productId, user_id: session.userId }
|
where: { id: parsed.data.productId, ...productAccessFilter(session.userId) }
|
||||||
})
|
})
|
||||||
if (!product) return { error: 'Product niet gevonden' }
|
if (!product) return { error: 'Product niet gevonden' }
|
||||||
|
|
||||||
|
|
@ -51,3 +52,13 @@ export async function createPbi(formData: FormData) {
|
||||||
return { success: true, pbi }
|
return { success: true, pbi }
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Security-invarianten
|
||||||
|
|
||||||
|
- Controleer auth en `session.isDemo` voordat er geschreven wordt.
|
||||||
|
- Gebruik `productAccessFilter(userId)` voor resources waar eigenaar en gekoppelde Developer beide toegang hebben.
|
||||||
|
- Gebruik eigenaar-only filters (`user_id: session.userId`) alleen voor eigenaarsacties zoals product archiveren, teamleden beheren of persoonlijke todos.
|
||||||
|
- Vertrouw nooit losse client-ID's. Als een action meerdere IDs ontvangt, haal ze eerst op met `id in (...)` plus de parent-scope en weiger de operatie als het aantal gevonden records niet exact gelijk is.
|
||||||
|
- Weiger dubbele IDs in reorder-lijsten of beslissingsobjecten.
|
||||||
|
- Leid denormalized foreign keys af uit de database-parent. Voorbeeld: gebruik `pbi.product_id` bij story creation, niet `formData.get('productId')`.
|
||||||
|
- Delete pas nadat ownership/scoping bewezen is; gebruik scoped `deleteMany` als een directe unique `delete` anders een cross-user record kan raken.
|
||||||
|
|
|
||||||
|
|
@ -27,3 +27,13 @@ async function reindexIfNeeded(items: { id: string; sort_order: number }[]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Reorder Server Actions
|
||||||
|
|
||||||
|
Een drag-and-drop reorder stuurt altijd client-controlled ID-lijsten naar de server. Behandel die lijst als onbetrouwbaar.
|
||||||
|
|
||||||
|
- Weiger dubbele IDs.
|
||||||
|
- Haal alle IDs op met de parent-scope, bijvoorbeeld `product_id`, `pbi_id`, `sprint_id` of `story_id`.
|
||||||
|
- Weiger de operatie als het aantal gevonden records niet exact gelijk is aan het aantal aangeleverde IDs.
|
||||||
|
- Update pas daarna `sort_order` in een transactie.
|
||||||
|
- Gebruik bij priority changes dezelfde parent uit de database, niet een los meegegeven `productId`.
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
## Architectuursamenvatting
|
## Architectuursamenvatting
|
||||||
|
|
||||||
Scrum4Me is een desktop-first Next.js 16 webapplicatie die server-side wordt gerenderd en gedeployed op Vercel. De database is PostgreSQL via Neon, aangestuurd via Prisma v7. Authenticatie is custom username/password via iron-session — geen externe auth-provider, geen e-mail. De REST API voor Claude Code-integratie loopt via Next.js Route Handlers, beveiligd met API-tokens. Drag-and-drop in de planningsschermen wordt afgehandeld door dnd-kit.
|
Scrum4Me is een desktop-first Next.js 16 webapplicatie die server-side wordt gerenderd en gedeployed op Vercel. De database is PostgreSQL via Neon, aangestuurd via Prisma v7. Authenticatie is custom username/password via iron-session — geen externe auth-provider, geen e-mail. De REST API voor Claude Code-integratie loopt via Next.js Route Handlers, beveiligd met API-tokens. Drag-and-drop in de planningsschermen wordt afgehandeld door dnd-kit. Vercel Analytics meet pageviews via de root layout; profielfoto's worden server-side verwerkt met Sharp.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -25,6 +25,8 @@ Scrum4Me is een desktop-first Next.js 16 webapplicatie die server-side wordt ger
|
||||||
| Authenticatie | Custom — iron-session + bcrypt | Username/password zonder e-mail vereist geen externe auth-provider; iron-session beheert versleutelde cookies server-side |
|
| Authenticatie | Custom — iron-session + bcrypt | Username/password zonder e-mail vereist geen externe auth-provider; iron-session beheert versleutelde cookies server-side |
|
||||||
| Drag-and-drop | dnd-kit | Actief onderhouden, React-native hooks, 60fps bij grote lijsten, ondersteuning voor meerdere containers |
|
| Drag-and-drop | dnd-kit | Actief onderhouden, React-native hooks, 60fps bij grote lijsten, ondersteuning voor meerdere containers |
|
||||||
| REST API | Next.js Route Handlers (`/app/api/`) | Naast Server Actions nodig voor Claude Code-integratie; Route Handlers zijn volledig HTTP-compatibel |
|
| REST API | Next.js Route Handlers (`/app/api/`) | Naast Server Actions nodig voor Claude Code-integratie; Route Handlers zijn volledig HTTP-compatibel |
|
||||||
|
| Image processing | Sharp | Avataruploads worden gevalideerd, geschaald en als WebP opgeslagen in PostgreSQL |
|
||||||
|
| Analytics | Vercel Analytics (`@vercel/analytics/next`) | Pageviews zonder extra client-configuratie; component staat in `app/layout.tsx` |
|
||||||
| Hosting | Vercel | Zero-config Next.js deployment; preview-URLs per PR; gratis tier voldoende voor v1 |
|
| Hosting | Vercel | Zero-config Next.js deployment; preview-URLs per PR; gratis tier voldoende voor v1 |
|
||||||
| CI/CD | GitHub Actions | Lint + typecheck + build op elke PR; Vercel handelt de daadwerkelijke deploy af |
|
| CI/CD | GitHub Actions | Lint + typecheck + build op elke PR; Vercel handelt de daadwerkelijke deploy af |
|
||||||
|
|
||||||
|
|
@ -239,6 +241,27 @@ Koppelt Developer-gebruikers aan een product backlog. De eigenaar (`products.use
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Toegangsmodel en schrijfbeveiliging
|
||||||
|
|
||||||
|
Producttoegang is centraal gedefinieerd als:
|
||||||
|
|
||||||
|
- eigenaar: `products.user_id === gebruiker.id`
|
||||||
|
- teamlid: `product_members` bevat `(product_id, user_id)`
|
||||||
|
|
||||||
|
Code gebruikt hiervoor `productAccessFilter(userId)` uit `lib/product-access.ts`. Route Handlers en Server Actions mogen geen eigenaar-only filter (`user_id`) gebruiken voor product-scoped resources tenzij het expliciet om eigenaarsbeheer gaat, zoals archiveren of teamleden beheren.
|
||||||
|
|
||||||
|
Schrijfoperaties volgen deze invarianten:
|
||||||
|
|
||||||
|
- Controleer eerst authenticatie en `session.isDemo`.
|
||||||
|
- Valideer input met Zod, maar behandel TypeScript types niet als runtime-beveiliging.
|
||||||
|
- Controleer de parent-resource met `productAccessFilter`.
|
||||||
|
- Vertrouw bulk-ID's nooit los: haal de records eerst op met `id in (...)` plus de parent-scope (`product_id`, `pbi_id`, `sprint_id` of `story_id`) en weiger de operatie als aantallen niet exact overeenkomen.
|
||||||
|
- Weiger dubbele IDs in reorder- en beslissingslijsten.
|
||||||
|
- Leid denormalized foreign keys af van de database-parent (`pbi.product_id`, `sprint.product_id`) en niet van form-data of JSON body.
|
||||||
|
- Delete of update alleen nadat de resource scoped is gevonden; gebruik scoped `deleteMany`/`updateMany` wanneer een unique `delete` anders onveilig zou zijn.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Prisma Schema (excerpt)
|
## Prisma Schema (excerpt)
|
||||||
|
|
||||||
```prisma
|
```prisma
|
||||||
|
|
@ -474,8 +497,9 @@ Inloggen:
|
||||||
→ bij mismatch: generieke foutmelding (geen onderscheid)
|
→ bij mismatch: generieke foutmelding (geen onderscheid)
|
||||||
|
|
||||||
Sessie per request:
|
Sessie per request:
|
||||||
middleware.ts → iron-session cookie uitlezen → user_id + is_demo in request
|
proxy.ts → sessiecookie-aanwezigheid controleren
|
||||||
→ beschermde routes: redirect /login als geen geldige sessie
|
→ beschermde routes: redirect /login als geen sessiecookie aanwezig is
|
||||||
|
→ app layout valideert de volledige sessie server-side
|
||||||
|
|
||||||
API-aanroepen (Claude Code):
|
API-aanroepen (Claude Code):
|
||||||
Authorization: Bearer <token> header → SHA-256 hash → opzoeken in api_tokens
|
Authorization: Bearer <token> header → SHA-256 hash → opzoeken in api_tokens
|
||||||
|
|
@ -554,7 +578,7 @@ scrum4me/
|
||||||
│ ├── schema.prisma
|
│ ├── schema.prisma
|
||||||
│ ├── migrations/
|
│ ├── migrations/
|
||||||
│ └── seed.ts # Testdata uit Product Backlog document
|
│ └── seed.ts # Testdata uit Product Backlog document
|
||||||
├── middleware.ts # Sessiecheck op beschermde routes
|
├── proxy.ts # Next.js 16 proxy voor route protection
|
||||||
├── prisma.config.ts # Prisma v7 config (DATABASE_URL)
|
├── prisma.config.ts # Prisma v7 config (DATABASE_URL)
|
||||||
└── .env.example
|
└── .env.example
|
||||||
```
|
```
|
||||||
|
|
@ -755,6 +779,7 @@ NODE_ENV="development"
|
||||||
- [ ] `prisma migrate deploy` uitgevoerd op productiedatabase
|
- [ ] `prisma migrate deploy` uitgevoerd op productiedatabase
|
||||||
- [ ] Demo-gebruiker aangemaakt via seed of handmatig
|
- [ ] Demo-gebruiker aangemaakt via seed of handmatig
|
||||||
- [ ] API-token aangemaakt en getest met `curl`-aanroep naar `/api/products`
|
- [ ] API-token aangemaakt en getest met `curl`-aanroep naar `/api/products`
|
||||||
|
- [ ] Vercel Analytics actief in het Vercel dashboard na eerste productiebezoek
|
||||||
- [ ] Vercel preview-deployments getest op een PR
|
- [ ] Vercel preview-deployments getest op een PR
|
||||||
- [ ] `next build` lokaal geslaagd zonder TypeScript-fouten
|
- [ ] `next build` lokaal geslaagd zonder TypeScript-fouten
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,8 +53,8 @@ De MVP is klaar wanneer Lars — de primaire persona — de volledige cyclus kan
|
||||||
- Schrijf `lib/auth.ts` (registreer met bcrypt hash, verifieer bij inloggen); schrijf `lib/session.ts` (iron-session config); implementeer `/register` en `/login` pagina's met Server Actions; sla `{ userId, isDemo }` op in sessiecookie
|
- Schrijf `lib/auth.ts` (registreer met bcrypt hash, verifieer bij inloggen); schrijf `lib/session.ts` (iron-session config); implementeer `/register` en `/login` pagina's met Server Actions; sla `{ userId, isDemo }` op in sessiecookie
|
||||||
- Done when: registreren → ingelogde sessie → redirect `/dashboard`; inloggen met verkeerde credentials geeft generieke foutmelding; sessie blijft actief na paginaverversing
|
- Done when: registreren → ingelogde sessie → redirect `/dashboard`; inloggen met verkeerde credentials geeft generieke foutmelding; sessie blijft actief na paginaverversing
|
||||||
|
|
||||||
- [ ] **ST-007** Route-beveiliging via `middleware.ts`
|
- [ ] **ST-007** Route-beveiliging via `proxy.ts`
|
||||||
- Schrijf `middleware.ts` die iron-session cookie uitleest; redirect naar `/login` bij alle `/dashboard`, `/products/*`, `/todos`, `/settings/*` routes zonder geldige sessie; authenticated users worden van `/login` en `/register` doorgestuurd naar `/dashboard`
|
- Schrijf `proxy.ts` die sessiecookie-aanwezigheid controleert; redirect naar `/login` bij alle `/dashboard`, `/products/*`, `/todos`, `/settings/*` routes zonder sessiecookie; authenticated users worden van `/login` en `/register` doorgestuurd naar `/dashboard`; volledige sessievalidatie gebeurt server-side in de app layout
|
||||||
- Done when: directe navigatie naar `/dashboard` zonder sessie redirect naar `/login`; ingelogde gebruiker op `/login` redirect naar `/dashboard`
|
- Done when: directe navigatie naar `/dashboard` zonder sessie redirect naar `/login`; ingelogde gebruiker op `/login` redirect naar `/dashboard`
|
||||||
|
|
||||||
- [ ] **ST-008** Navigatieshell + dashboard-layout
|
- [ ] **ST-008** Navigatieshell + dashboard-layout
|
||||||
|
|
@ -214,8 +214,8 @@ De MVP is klaar wanneer Lars — de primaire persona — de volledige cyclus kan
|
||||||
- Done when: token aangemaakt en waarde zichtbaar; na sluiten dialoog niet meer te zien; intrekken maakt token onbruikbaar (getest via curl)
|
- Done when: token aangemaakt en waarde zichtbaar; na sluiten dialoog niet meer te zien; intrekken maakt token onbruikbaar (getest via curl)
|
||||||
|
|
||||||
- [ ] **ST-403** `GET /api/products` — productenlijst
|
- [ ] **ST-403** `GET /api/products` — productenlijst
|
||||||
- Route Handler; authenticatie via `api-auth.ts`; retourneer actieve producten `[{ id, name, repo_url }]` als JSON
|
- Route Handler; authenticatie via `api-auth.ts`; retourneer actieve producten `[{ id, name, repo_url }]` als JSON voor producten waar de tokengebruiker eigenaar of teamlid is
|
||||||
- Done when: `curl -H "Authorization: Bearer <token>" /api/products` retourneert correct JSON; 401 zonder token
|
- Done when: `curl -H "Authorization: Bearer <token>" /api/products` retourneert correct JSON inclusief gedeelde product backlogs; 401 zonder token
|
||||||
|
|
||||||
- [ ] **ST-404** `GET /api/products/:id/next-story` — volgende story ophalen
|
- [ ] **ST-404** `GET /api/products/:id/next-story` — volgende story ophalen
|
||||||
- Route Handler; haal hoogst geprioriteerde OPEN story op van actieve Sprint van het product (priority ASC, sort_order ASC); retourneer `{ id, title, description, acceptance_criteria, tasks[] }`; 404 als geen open stories
|
- Route Handler; haal hoogst geprioriteerde OPEN story op van actieve Sprint van het product (priority ASC, sort_order ASC); retourneer `{ id, title, description, acceptance_criteria, tasks[] }`; 404 als geen open stories
|
||||||
|
|
@ -318,17 +318,17 @@ De MVP is klaar wanneer Lars — de primaire persona — de volledige cyclus kan
|
||||||
- Done when: 11 snelle inlogpogingen leiden tot 429-respons met duidelijke melding
|
- Done when: 11 snelle inlogpogingen leiden tot 429-respons met duidelijke melding
|
||||||
|
|
||||||
- [ ] **ST-609** Beveiligingsreview API-endpoints
|
- [ ] **ST-609** Beveiligingsreview API-endpoints
|
||||||
- Controleer alle Route Handlers: elke schrijfoperatie valideert dat de resource bij de geverifieerde gebruiker hoort; cross-user reads zijn onmogelijk; voeg integratietests toe die cross-user toegang testen
|
- Controleer alle Route Handlers: elke schrijfoperatie valideert dat de resource binnen de toegankelijke product-scope valt; cross-scope reads zijn onmogelijk tenzij de gebruiker via `product_members` gekoppeld is; voeg integratietests toe die cross-user toegang testen
|
||||||
- Done when: poging om andere gebruikers's product op te halen via API geeft 403; getest met twee test-gebruikers
|
- Done when: poging om een niet-gedeeld product van een andere gebruiker op te halen via API geeft 403 of 404; gedeelde producten zijn wel zichtbaar; getest met twee test-gebruikers
|
||||||
|
|
||||||
- [ ] **ST-610** CI/CD via GitHub Actions
|
- [ ] **ST-610** CI/CD via GitHub Actions
|
||||||
- Workflow: `lint` (ESLint), `typecheck` (tsc --noEmit), `prisma validate`, `build` (next build) op elke PR en push naar main; Vercel auto-deploy op main
|
- Workflow: `lint` (ESLint), `typecheck` (tsc --noEmit), `prisma validate`, `build` (next build) op elke PR en push naar main; Vercel auto-deploy op main
|
||||||
- Done when: een TypeScript-fout in een PR blokkeert merge; succesvolle merge triggert Vercel-deploy
|
- Done when: een TypeScript-fout in een PR blokkeert merge; succesvolle merge triggert Vercel-deploy
|
||||||
|
|
||||||
- [ ] **ST-611** README en lokale setup-documentatie
|
- [ ] **ST-611** README en lokale setup-documentatie
|
||||||
- Schrijf `README.md` met: wat is Scrum4Me, quickstart lokaal (clone → env → prisma push → seed → dev), cloud deployment (Vercel + Neon stappenplan), API-documentatie (alle 7 endpoints met voorbeelden), Claude Code-integratie uitleg
|
- Schrijf `README.md` met: wat is Scrum4Me, quickstart lokaal (clone → env → prisma push → seed → dev), cloud deployment (Vercel + Neon stappenplan), API-documentatie (alle 7 endpoints met voorbeelden), Claude Code-integratie uitleg, Vercel Analytics status en directe dependencies zoals Sharp
|
||||||
- De in-app landingspagina (`/`) bevat al een gebruikershandleiding, Scrum-samenvatting en API-overzicht — de README richt zich op lokale setup en deployment
|
- De in-app landingspagina (`/`) bevat al een gebruikershandleiding, Scrum-samenvatting en API-overzicht — de README richt zich op lokale setup en deployment
|
||||||
- Done when: iemand zonder context de app lokaal kan draaien op basis van alleen de README
|
- Done when: iemand zonder context de app lokaal kan draaien op basis van alleen de README en `.env.example`
|
||||||
|
|
||||||
- [ ] **ST-612** End-to-end acceptatietest
|
- [ ] **ST-612** End-to-end acceptatietest
|
||||||
- Voer handmatig de volledige Lars-flow uit: product aanmaken → PBI's en stories aanmaken → Sprint starten → stories slepen → taken aanmaken → API-token aanmaken → curl `next-story` → curl `log` (plan, test, commit) → activiteitenlog controleren in UI
|
- Voer handmatig de volledige Lars-flow uit: product aanmaken → PBI's en stories aanmaken → Sprint starten → stories slepen → taken aanmaken → API-token aanmaken → curl `next-story` → curl `log` (plan, test, commit) → activiteitenlog controleren in UI
|
||||||
|
|
|
||||||
|
|
@ -362,7 +362,7 @@ Een REST API waarmee Claude Code stories en taken kan ophalen, de taakvolgorde k
|
||||||
**Acceptatiecriteria:**
|
**Acceptatiecriteria:**
|
||||||
|
|
||||||
**Endpoints:**
|
**Endpoints:**
|
||||||
- [ ] `GET /api/products` — lijst van actieve producten
|
- [ ] `GET /api/products` — lijst van actieve producten waarvoor de tokengebruiker eigenaar of teamlid is
|
||||||
- [ ] `GET /api/products/:id/next-story` — hoogst geprioriteerde open story van de actieve Sprint
|
- [ ] `GET /api/products/:id/next-story` — hoogst geprioriteerde open story van de actieve Sprint
|
||||||
- [ ] `GET /api/sprints/:id/tasks?limit=10` — eerste N taken in huidige volgorde
|
- [ ] `GET /api/sprints/:id/tasks?limit=10` — eerste N taken in huidige volgorde
|
||||||
- [ ] `PATCH /api/stories/:id/tasks/reorder` — accepteert geordende lijst van taak-id's
|
- [ ] `PATCH /api/stories/:id/tasks/reorder` — accepteert geordende lijst van taak-id's
|
||||||
|
|
@ -474,6 +474,7 @@ De app is deployable op Vercel + Neon PostgreSQL en lokaal draaibaar met een Neo
|
||||||
| Toegankelijkheid | Keyboard-navigatie voor alle primaire acties; WCAG AA voor de sprint planning flow |
|
| Toegankelijkheid | Keyboard-navigatie voor alle primaire acties; WCAG AA voor de sprint planning flow |
|
||||||
| Database | Prisma ORM; PostgreSQL via Neon |
|
| Database | Prisma ORM; PostgreSQL via Neon |
|
||||||
| Deployment | Vercel (cloud) of lokaal via `npm run dev` |
|
| Deployment | Vercel (cloud) of lokaal via `npm run dev` |
|
||||||
|
| Analytics | Vercel Analytics via `@vercel/analytics/next` in de root layout |
|
||||||
| Sessiebeheer | Server-side sessie of JWT; geen opslag van gevoelige data in localStorage |
|
| Sessiebeheer | Server-side sessie of JWT; geen opslag van gevoelige data in localStorage |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
43
package-lock.json
generated
43
package-lock.json
generated
|
|
@ -15,6 +15,7 @@
|
||||||
"@dnd-kit/utilities": "^3.2.2",
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
"@prisma/adapter-pg": "^7.8.0",
|
"@prisma/adapter-pg": "^7.8.0",
|
||||||
"@prisma/client": "^7.8.0",
|
"@prisma/client": "^7.8.0",
|
||||||
|
"@vercel/analytics": "^2.0.1",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
|
@ -4342,6 +4343,48 @@
|
||||||
"win32"
|
"win32"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/@vercel/analytics": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vercel/analytics/-/analytics-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-MTQG6V9qQrt1tsDeF+2Uoo5aPjqbVPys1xvnIftXSJYG2SrwXRHnqEvVoYID7BTruDz4lCd2Z7rM1BdkUehk2g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@remix-run/react": "^2",
|
||||||
|
"@sveltejs/kit": "^1 || ^2",
|
||||||
|
"next": ">= 13",
|
||||||
|
"nuxt": ">= 3",
|
||||||
|
"react": "^18 || ^19 || ^19.0.0-rc",
|
||||||
|
"svelte": ">= 4",
|
||||||
|
"vue": "^3",
|
||||||
|
"vue-router": "^4"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@remix-run/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@sveltejs/kit": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"next": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"nuxt": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"svelte": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"vue": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"vue-router": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@vitest/coverage-v8": {
|
"node_modules/@vitest/coverage-v8": {
|
||||||
"version": "4.1.5",
|
"version": "4.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.5.tgz",
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
"@dnd-kit/utilities": "^3.2.2",
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
"@prisma/adapter-pg": "^7.8.0",
|
"@prisma/adapter-pg": "^7.8.0",
|
||||||
"@prisma/client": "^7.8.0",
|
"@prisma/client": "^7.8.0",
|
||||||
|
"@vercel/analytics": "^2.0.1",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue