Scrum4Me/lib/insights/velocity.ts
janpeter visser 27828c8834 feat(insights): add getVelocity helper — DONE-tasks per completed sprint
Aggregates task.status=DONE counts across last N completed sprints
(default 5), filtered by productAccessFilter and returned in
chronological order for x-axis rendering.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-02 16:06:56 +02:00

57 lines
1.6 KiB
TypeScript

import { prisma } from '@/lib/prisma'
import { productAccessFilter } from '@/lib/product-access'
export interface VelocitySprint {
sprintId: 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<VelocityData> {
const sprints = await prisma.sprint.findMany({
where: {
status: 'COMPLETED',
product: productAccessFilter(userId),
},
orderBy: { completed_at: 'desc' },
take: sprintsBack,
select: {
id: 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)
const chronological = [...sprints].reverse()
const result: VelocitySprint[] = chronological.map(sprint => ({
sprintId: sprint.id,
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<string>()
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 }
}