feat(insights): add getVelocity helper — DONE-tasks per completed sprint (#47)
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>
This commit is contained in:
parent
ce94fb48c3
commit
219d54b3e5
2 changed files with 134 additions and 0 deletions
57
lib/insights/velocity.ts
Normal file
57
lib/insights/velocity.ts
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
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 }
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue