diff --git a/app/api/products/[id]/claude-context/route.ts b/app/api/products/[id]/claude-context/route.ts
index 5387a0f..f5a5fd5 100644
--- a/app/api/products/[id]/claude-context/route.ts
+++ b/app/api/products/[id]/claude-context/route.ts
@@ -29,14 +29,26 @@ export async function GET(
return Response.json({ error: 'Product niet gevonden' }, { status: 404 })
}
- const [activeSprint, openTodos] = await Promise.all([
+ const [activeSprint, openIdeas] = await Promise.all([
prisma.sprint.findFirst({
where: { product_id: id, status: 'ACTIVE' },
select: { id: true, sprint_goal: true, status: true },
}),
- prisma.todo.findMany({
- where: { user_id: auth.userId, product_id: id, done: false, archived: false },
- select: { id: true, title: true, description: true, created_at: true },
+ prisma.idea.findMany({
+ where: {
+ user_id: auth.userId,
+ product_id: id,
+ archived: false,
+ status: { not: 'PLANNED' },
+ },
+ select: {
+ id: true,
+ code: true,
+ title: true,
+ description: true,
+ status: true,
+ created_at: true,
+ },
orderBy: { created_at: 'asc' },
take: 50,
}),
@@ -89,6 +101,6 @@ export async function GET(
product,
active_sprint: activeSprint,
next_story: nextStoryPayload,
- open_todos: openTodos,
+ open_ideas: openIdeas,
})
}
diff --git a/docs/api.md b/docs/api.md
index 4065a47..c8796e4 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -96,7 +96,7 @@ curl -H "Authorization: Bearer $TOKEN" https://scrum4me.app/api/products
### `GET /api/products/:id/claude-context`
-Bundled context voor Claude Code: product, actieve sprint, volgende story (met tasks) en open todos van de tokengebruiker — in één call.
+Bundled context voor Claude Code: product, actieve sprint, volgende story (met tasks) en open ideas van de tokengebruiker — in één call.
**Response (200):**
```json
@@ -111,13 +111,13 @@ Bundled context voor Claude Code: product, actieve sprint, volgende story (met t
"priority", "sort_order", "status" }
]
} | null,
- "open_todos": [
- { "id", "title", "description", "created_at" }
+ "open_ideas": [
+ { "id", "code", "title", "description", "status", "created_at" }
]
}
```
-`open_todos` is gelimiteerd op 50 items, gesorteerd op `created_at` asc. Demo-tokens kunnen dit endpoint lezen.
+`open_ideas` bevat ideeën van de gebruiker voor dit product die niet gearchiveerd zijn en nog niet de status `PLANNED` hebben (= nog niet als PBI gepromoveerd). Gelimiteerd op 50 items, gesorteerd op `created_at` asc. Demo-tokens kunnen dit endpoint lezen.
```bash
curl -H "Authorization: Bearer $TOKEN" \
diff --git a/docs/api/rest-contract.md b/docs/api/rest-contract.md
index 2feddd4..758d437 100644
--- a/docs/api/rest-contract.md
+++ b/docs/api/rest-contract.md
@@ -105,7 +105,7 @@ curl -H "Authorization: Bearer $TOKEN" https://scrum4me.app/api/products
### `GET /api/products/:id/claude-context`
-Bundled context voor Claude Code: product, actieve sprint, volgende story (met tasks) en open todos van de tokengebruiker — in één call.
+Bundled context voor Claude Code: product, actieve sprint, volgende story (met tasks) en open ideas van de tokengebruiker — in één call.
**Response (200):**
```json
@@ -120,13 +120,13 @@ Bundled context voor Claude Code: product, actieve sprint, volgende story (met t
"priority", "sort_order", "status" }
]
} | null,
- "open_todos": [
- { "id", "title", "description", "created_at" }
+ "open_ideas": [
+ { "id", "code", "title", "description", "status", "created_at" }
]
}
```
-`open_todos` is gelimiteerd op 50 items, gesorteerd op `created_at` asc. Demo-tokens kunnen dit endpoint lezen.
+`open_ideas` bevat ideeën van de gebruiker voor dit product die niet gearchiveerd zijn en nog niet de status `PLANNED` hebben (= nog niet als PBI gepromoveerd). Gelimiteerd op 50 items, gesorteerd op `created_at` asc. Demo-tokens kunnen dit endpoint lezen.
```bash
curl -H "Authorization: Bearer $TOKEN" \
diff --git a/docs/erd.svg b/docs/erd.svg
index dc4f289..7b40b67 100644
--- a/docs/erd.svg
+++ b/docs/erd.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/prisma/migrations/20260507000000_migrate_todos_to_ideas/migration.sql b/prisma/migrations/20260507000000_migrate_todos_to_ideas/migration.sql
new file mode 100644
index 0000000..4d740ae
--- /dev/null
+++ b/prisma/migrations/20260507000000_migrate_todos_to_ideas/migration.sql
@@ -0,0 +1,59 @@
+BEGIN;
+
+WITH active_todos AS (
+ SELECT t.id, t.user_id, t.product_id, t.title, t.description, t.created_at
+ FROM todos t
+ WHERE t.done = false
+ AND t.archived = false
+ AND NOT EXISTS (
+ SELECT 1 FROM ideas i
+ WHERE i.user_id = t.user_id
+ AND lower(trim(i.title)) = lower(trim(t.title))
+ )
+),
+user_base AS (
+ SELECT ut.user_id, u.idea_code_counter AS base_counter
+ FROM (SELECT DISTINCT user_id FROM active_todos) ut
+ JOIN users u ON u.id = ut.user_id
+),
+ranked AS (
+ SELECT
+ at.user_id, at.product_id, at.title, at.description, at.created_at,
+ ub.base_counter,
+ ROW_NUMBER() OVER (PARTITION BY at.user_id ORDER BY at.created_at, at.id) AS rn
+ FROM active_todos at
+ JOIN user_base ub ON ub.user_id = at.user_id
+),
+inserted AS (
+ INSERT INTO ideas (
+ id, user_id, product_id, code, title, description,
+ status, archived, created_at, updated_at
+ )
+ SELECT
+ gen_random_uuid()::text,
+ user_id,
+ product_id,
+ 'IDEA-' || lpad((base_counter + rn)::text, 3, '0'),
+ title,
+ description,
+ 'DRAFT',
+ false,
+ created_at,
+ now()
+ FROM ranked
+ RETURNING user_id,
+ (regexp_replace(code, '^IDEA-0*', ''))::int AS used_counter
+)
+UPDATE users u
+SET idea_code_counter = sub.max_counter
+FROM (
+ SELECT user_id, MAX(used_counter) AS max_counter
+ FROM inserted
+ GROUP BY user_id
+) sub
+WHERE u.id = sub.user_id
+ AND sub.max_counter > u.idea_code_counter;
+
+DROP TABLE todos;
+
+COMMIT;
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 5082a97..61a1d2c 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -127,7 +127,6 @@ model User {
roles UserRole[]
api_tokens ApiToken[]
products Product[]
- todos Todo[]
ideas Idea[]
product_members ProductMember[]
assigned_stories Story[] @relation("StoryAssignee")
@@ -183,7 +182,6 @@ model Product {
sprints Sprint[]
stories Story[]
tasks Task[]
- todos Todo[]
members ProductMember[]
active_for_users User[] @relation("UserActiveProduct")
claude_questions ClaudeQuestion[]
@@ -403,24 +401,6 @@ model ProductMember {
@@map("product_members")
}
-model Todo {
- id String @id @default(cuid())
- user User @relation(fields: [user_id], references: [id], onDelete: Cascade)
- user_id String
- product Product? @relation(fields: [product_id], references: [id], onDelete: SetNull)
- product_id String?
- title String
- description String? @db.VarChar(2000)
- done Boolean @default(false)
- archived Boolean @default(false)
- created_at DateTime @default(now())
- updated_at DateTime @updatedAt
-
- @@index([user_id, done, archived])
- @@index([user_id, product_id])
- @@map("todos")
-}
-
model Idea {
id String @id @default(cuid())
user User @relation(fields: [user_id], references: [id], onDelete: Cascade)