From 03fda05be31e27b68076d3e1769e0fd068a38377 Mon Sep 17 00:00:00 2001 From: Madhura68 Date: Sun, 26 Apr 2026 21:36:55 +0200 Subject: [PATCH] feat(ST-512): extend REST API with code, description and implementation_plan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - GET /api/products returns code, description and definition_of_done - GET /api/products/:id/next-story returns story.code and per-task code + implementation_plan - GET /api/sprints/:id/tasks returns description, implementation_plan, story_code and derived per-task code - POST /api/todos accepts and returns optional description (max 2000) All changes are additive — existing clients ignore unknown keys. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/api/products/[id]/next-story/route.ts | 20 +++++++++++-- app/api/products/route.ts | 2 +- app/api/sprints/[id]/tasks/route.ts | 36 +++++++++++++++++++++-- app/api/todos/route.ts | 9 +++++- 4 files changed, 60 insertions(+), 7 deletions(-) diff --git a/app/api/products/[id]/next-story/route.ts b/app/api/products/[id]/next-story/route.ts index 2747f4f..f89af44 100644 --- a/app/api/products/[id]/next-story/route.ts +++ b/app/api/products/[id]/next-story/route.ts @@ -25,8 +25,16 @@ export async function GET( orderBy: [{ priority: 'asc' }, { sort_order: 'asc' }], include: { tasks: { - orderBy: [{ priority: 'asc' }, { sort_order: 'asc' }], - select: { id: true, title: true, description: true, priority: true, sort_order: true, status: true }, + orderBy: { sort_order: 'asc' }, + select: { + id: true, + title: true, + description: true, + implementation_plan: true, + priority: true, + sort_order: true, + status: true, + }, }, }, }) @@ -35,11 +43,17 @@ export async function GET( return Response.json({ error: 'Geen open stories in de Sprint' }, { status: 404 }) } + const tasks = story.tasks.map((t, idx) => ({ + ...t, + code: story.code ? `${story.code}.${idx + 1}` : null, + })) + return Response.json({ id: story.id, + code: story.code, title: story.title, description: story.description, acceptance_criteria: story.acceptance_criteria, - tasks: story.tasks, + tasks, }) } diff --git a/app/api/products/route.ts b/app/api/products/route.ts index 89c7c9e..862d7e7 100644 --- a/app/api/products/route.ts +++ b/app/api/products/route.ts @@ -11,7 +11,7 @@ export async function GET(request: Request) { const products = await prisma.product.findMany({ where: { archived: false, ...productAccessFilter(auth.userId) }, orderBy: { created_at: 'desc' }, - select: { id: true, name: true, repo_url: true }, + select: { id: true, code: true, name: true, description: true, repo_url: true, definition_of_done: true }, }) return Response.json(products) diff --git a/app/api/sprints/[id]/tasks/route.ts b/app/api/sprints/[id]/tasks/route.ts index e2f6d12..f2ad1dc 100644 --- a/app/api/sprints/[id]/tasks/route.ts +++ b/app/api/sprints/[id]/tasks/route.ts @@ -31,8 +31,40 @@ export async function GET( { sort_order: 'asc' }, ], take: limit, - select: { id: true, title: true, story_id: true, priority: true, sort_order: true, status: true }, + select: { + id: true, + title: true, + description: true, + implementation_plan: true, + story_id: true, + priority: true, + sort_order: true, + status: true, + story: { + select: { + code: true, + tasks: { select: { id: true }, orderBy: { sort_order: 'asc' } }, + }, + }, + }, }) - return Response.json(tasks) + const enriched = tasks.map((t) => { + const positionInStory = t.story.tasks.findIndex((st) => st.id === t.id) + const code = t.story.code && positionInStory >= 0 ? `${t.story.code}.${positionInStory + 1}` : null + return { + id: t.id, + code, + title: t.title, + description: t.description, + implementation_plan: t.implementation_plan, + story_id: t.story_id, + story_code: t.story.code, + priority: t.priority, + sort_order: t.sort_order, + status: t.status, + } + }) + + return Response.json(enriched) } diff --git a/app/api/todos/route.ts b/app/api/todos/route.ts index 3836cdc..90fe453 100644 --- a/app/api/todos/route.ts +++ b/app/api/todos/route.ts @@ -4,6 +4,7 @@ import { z } from 'zod' const bodySchema = z.object({ title: z.string().min(1, 'Titel is verplicht').max(500), + description: z.string().max(2000, 'Beschrijving mag maximaal 2000 tekens bevatten').optional(), product_id: z.string().min(1, 'Product is verplicht'), }) @@ -29,13 +30,19 @@ export async function POST(request: Request) { return Response.json({ error: 'Product niet gevonden' }, { status: 404 }) } + const description = parsed.data.description?.trim() || null + const todo = await prisma.todo.create({ data: { user_id: auth.userId, product_id: parsed.data.product_id, title: parsed.data.title, + description, }, }) - return Response.json({ id: todo.id, title: todo.title, created_at: todo.created_at }, { status: 201 }) + return Response.json( + { id: todo.id, title: todo.title, description: todo.description, created_at: todo.created_at }, + { status: 201 }, + ) }