diff --git a/app/api/realtime/notifications/route.ts b/app/api/realtime/notifications/route.ts index 834ebff..1d32a2f 100644 --- a/app/api/realtime/notifications/route.ts +++ b/app/api/realtime/notifications/route.ts @@ -185,35 +185,55 @@ export async function GET(request: NextRequest) { // Initial state ná LISTEN actief — race-fix conform M10 ST-1004 / ST-1006. // Voorkomt dat een vraag die net vóór SSE-open landt verloren gaat. - const openQuestions = await prisma.claudeQuestion.findMany({ - where: { - status: 'open', - expires_at: { gt: new Date() }, - product_id: { in: products.map((p) => p.id) }, - // Skip idea-questions (story_id NULL) — story-questions only here. - // Narrowing happens in the flatMap below — Prisma 7 rejects - // `story_id: { not: null }` at runtime. - }, - orderBy: { created_at: 'desc' }, - take: 100, - select: { - id: true, - product_id: true, - story_id: true, - task_id: true, - question: true, - options: true, - created_at: true, - expires_at: true, - story: { select: { code: true, title: true, assignee_id: true } }, - }, - }) + // M12 hotfix: óók idea-questions (user-private), zodat de bel + // gehydrateerd blijft na elke close+reconnect-cycle. + const [storyOpen, ideaOpen] = await Promise.all([ + prisma.claudeQuestion.findMany({ + where: { + status: 'open', + expires_at: { gt: new Date() }, + product_id: { in: products.map((p) => p.id) }, + }, + orderBy: { created_at: 'desc' }, + take: 100, + select: { + id: true, + product_id: true, + story_id: true, + task_id: true, + question: true, + options: true, + created_at: true, + expires_at: true, + story: { select: { code: true, title: true, assignee_id: true } }, + }, + }), + prisma.claudeQuestion.findMany({ + where: { + status: 'open', + expires_at: { gt: new Date() }, + idea: { user_id: userId }, + }, + orderBy: { created_at: 'desc' }, + take: 100, + select: { + id: true, + product_id: true, + idea_id: true, + question: true, + options: true, + created_at: true, + expires_at: true, + idea: { select: { id: true, code: true, title: true } }, + }, + }), + ]) - enqueue( - `event: state\ndata: ${JSON.stringify({ - questions: openQuestions.flatMap((q) => { - if (!q.story || q.story_id === null) return [] - return [{ + const stateQuestions = [ + ...storyOpen.flatMap((q) => { + if (!q.story || q.story_id === null) return [] + return [{ + kind: 'story' as const, id: q.id, product_id: q.product_id, story_id: q.story_id, @@ -225,10 +245,26 @@ export async function GET(request: NextRequest) { options: q.options, created_at: q.created_at.toISOString(), expires_at: q.expires_at.toISOString(), - }] - }), - })}\n\n`, - ) + }] + }), + ...ideaOpen.flatMap((q) => { + if (!q.idea || q.idea_id === null) return [] + return [{ + kind: 'idea' as const, + id: q.id, + product_id: q.product_id, + idea_id: q.idea_id, + idea_code: q.idea.code, + idea_title: q.idea.title, + question: q.question, + options: q.options, + created_at: q.created_at.toISOString(), + expires_at: q.expires_at.toISOString(), + }] + }), + ].sort((a, b) => (a.created_at < b.created_at ? 1 : -1)) + + enqueue(`event: state\ndata: ${JSON.stringify({ questions: stateQuestions })}\n\n`) heartbeatTimer = setInterval(() => { enqueue(`: heartbeat\n\n`)