diff --git a/app/(app)/ideas/[id]/sync-tab-server.ts b/app/(app)/ideas/[id]/sync-tab-server.ts new file mode 100644 index 0000000..ae93465 --- /dev/null +++ b/app/(app)/ideas/[id]/sync-tab-server.ts @@ -0,0 +1,85 @@ +import 'server-only' +import { prisma } from '@/lib/prisma' + +// Server-only loader voor de Sync-tab op /ideas/[id]. +// Joint Idea → PBI → Stories → Tasks → ClaudeJobs + StoryLog. +// Auth-scope: strikt user_id-only conform M12-keuze 2. +// +// Returns null wanneer: +// - idea bestaat niet of behoort niet aan user +// - idea heeft geen pbi_id (status !== PLANNED, dus sync-tab niet relevant) +// +// Caller (page.tsx) moet de tab niet renderen als deze null retourneert. +export async function loadIdeaSyncData(ideaId: string, userId: string) { + const idea = await prisma.idea.findFirst({ + where: { id: ideaId, user_id: userId }, + select: { + id: true, + code: true, + title: true, + status: true, + pbi_id: true, + product: { select: { id: true, name: true, repo_url: true } }, + pbi: { + select: { + id: true, + code: true, + title: true, + pr_url: true, + pr_merged_at: true, + stories: { + orderBy: { sort_order: 'asc' }, + select: { + id: true, + code: true, + title: true, + status: true, + tasks: { + orderBy: { sort_order: 'asc' }, + select: { + id: true, + code: true, + title: true, + status: true, + claude_jobs: { + where: { kind: 'TASK_IMPLEMENTATION' }, + orderBy: { created_at: 'desc' }, + select: { + id: true, + status: true, + branch: true, + pushed_at: true, + pr_url: true, + error: true, + summary: true, + created_at: true, + finished_at: true, + }, + }, + }, + }, + logs: { + orderBy: { created_at: 'desc' }, + take: 20, + select: { + id: true, + type: true, + content: true, + status: true, + commit_hash: true, + commit_message: true, + created_at: true, + }, + }, + }, + }, + }, + }, + }, + }) + + if (!idea || !idea.pbi) return null + return idea +} + +export type IdeaSyncData = NonNullable>>