import { prisma } from '@/lib/prisma' import { productAccessFilter } from '@/lib/product-access' export interface VelocitySprint { sprintId: string sprintCode: string sprintGoal: string productId: string productName: string doneCount: number completedAt: string } export interface VelocityData { sprints: VelocitySprint[] productNames: { id: string; name: string }[] } export async function getVelocity(userId: string, sprintsBack = 5): Promise { const sprints = await prisma.sprint.findMany({ where: { status: 'CLOSED', product: productAccessFilter(userId), }, orderBy: { completed_at: 'desc' }, take: sprintsBack, select: { id: true, code: true, sprint_goal: true, completed_at: true, product: { select: { id: true, name: true } }, tasks: { select: { status: true } }, }, }) // Reverse to chronological order (oldest first, for x-axis) // Type-guard so the narrowed array carries `completed_at: Date` (not Date | null). // A `.filter(s => s.completed_at != null)` alone does NOT narrow the element type. type SprintWithCompletedAt = (typeof sprints)[number] & { completed_at: Date } const chronological = [...sprints] .filter((s): s is SprintWithCompletedAt => s.completed_at != null) .reverse() const result: VelocitySprint[] = chronological.map(sprint => ({ sprintId: sprint.id, sprintCode: sprint.code, sprintGoal: sprint.sprint_goal, productId: sprint.product.id, productName: sprint.product.name, doneCount: sprint.tasks.filter(t => t.status === 'DONE').length, completedAt: sprint.completed_at.toISOString(), })) const seen = new Set() const productNames: { id: string; name: string }[] = [] for (const s of result) { if (!seen.has(s.productId)) { seen.add(s.productId) productNames.push({ id: s.productId, name: s.productName }) } } return { sprints: result, productNames } }