- Sprint lifecycle: ACTIVE→OPEN, COMPLETED→CLOSED, +ARCHIVED (FAILED behouden) - TaskStatus: +EXCLUDED (overgeslagen door agent-loop via bestaande TO_DO filter) - Cookie-gebaseerde actieve sprint per product (lib/active-sprint.ts) - Route splitsen: /products/[id]/sprint/[sprintId] + /sprint redirect-page - NavBar: gestapelde product/sprint dropdowns + BUILDING-badge derivatie - Backlog selectie-modus + nieuwe-sprint-dialog (createSprintWithPbisAction) - Migratie 20260507210000_sprint_lifecycle: ALTER TYPE RENAME (geen data-rewrite) - Version bump 1.0.0 → 1.2.0 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
66 lines
1.7 KiB
TypeScript
66 lines
1.7 KiB
TypeScript
import { cookies } from 'next/headers'
|
|
import type { SprintStatus } from '@prisma/client'
|
|
import { prisma } from '@/lib/prisma'
|
|
|
|
export type ActiveSprint = {
|
|
id: string
|
|
code: string
|
|
status: SprintStatus
|
|
}
|
|
|
|
function cookieName(productId: string): string {
|
|
return `active_sprint_${productId}`
|
|
}
|
|
|
|
export async function getActiveSprintIdFromCookie(
|
|
productId: string,
|
|
): Promise<string | null> {
|
|
const store = await cookies()
|
|
return store.get(cookieName(productId))?.value ?? null
|
|
}
|
|
|
|
export async function setActiveSprintCookie(
|
|
productId: string,
|
|
sprintId: string,
|
|
): Promise<void> {
|
|
const store = await cookies()
|
|
store.set(cookieName(productId), sprintId, {
|
|
path: '/',
|
|
sameSite: 'lax',
|
|
secure: process.env.NODE_ENV === 'production',
|
|
maxAge: 60 * 60 * 24 * 365,
|
|
})
|
|
}
|
|
|
|
export async function clearActiveSprintCookie(productId: string): Promise<void> {
|
|
const store = await cookies()
|
|
store.delete(cookieName(productId))
|
|
}
|
|
|
|
export async function resolveActiveSprint(
|
|
productId: string,
|
|
): Promise<ActiveSprint | null> {
|
|
const cookieId = await getActiveSprintIdFromCookie(productId)
|
|
|
|
if (cookieId) {
|
|
const sprint = await prisma.sprint.findFirst({
|
|
where: { id: cookieId, product_id: productId },
|
|
select: { id: true, code: true, status: true },
|
|
})
|
|
if (sprint) return sprint
|
|
}
|
|
|
|
const open = await prisma.sprint.findFirst({
|
|
where: { product_id: productId, status: 'OPEN' },
|
|
orderBy: { created_at: 'desc' },
|
|
select: { id: true, code: true, status: true },
|
|
})
|
|
if (open) return open
|
|
|
|
const closed = await prisma.sprint.findFirst({
|
|
where: { product_id: productId, status: 'CLOSED' },
|
|
orderBy: { created_at: 'desc' },
|
|
select: { id: true, code: true, status: true },
|
|
})
|
|
return closed ?? null
|
|
}
|