'use client' import { useMemo, useState, useTransition } from 'react' import { useRouter } from 'next/navigation' import { toast } from 'sonner' import { Button } from '@/components/ui/button' import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog' import { useUserSettingsStore } from '@/stores/user-settings/store' import { useProductWorkspaceStore } from '@/stores/product-workspace/store' import type { PendingSprintDraft } from '@/lib/user-settings' import { createSprintWithSelectionAction } from '@/actions/sprints' import { debugProps } from '@/lib/debug' interface SprintDefinitionBannerProps { productId: string draft: PendingSprintDraft } type DraftCounts = { pbiCount: number storyCount: number hasUnknownTotal: boolean } function computeCounts( draft: PendingSprintDraft, pbiSummary: Record< string, { totalStoryCount: number; inActiveSprintStoryCount: number } >, ): DraftCounts { let pbiCount = 0 let storyCount = 0 let hasUnknownTotal = false const seenPbis = new Set() for (const [pbiId, intent] of Object.entries(draft.pbiIntent)) { if (intent === 'all') { seenPbis.add(pbiId) const summary = pbiSummary[pbiId] const override = draft.storyOverrides[pbiId] if (!summary) { hasUnknownTotal = true continue } const removed = override?.remove.length ?? 0 storyCount += Math.max(0, summary.totalStoryCount - removed) } } for (const [pbiId, override] of Object.entries(draft.storyOverrides)) { if (override.add.length === 0) continue seenPbis.add(pbiId) storyCount += override.add.length } pbiCount = seenPbis.size return { pbiCount, storyCount, hasUnknownTotal } } export function SprintDefinitionBanner({ productId, draft, }: SprintDefinitionBannerProps) { const clearPendingSprintDraft = useUserSettingsStore( (s) => s.clearPendingSprintDraft, ) const pbiSummary = useProductWorkspaceStore((s) => s.sprintMembership.pbiSummary) const router = useRouter() const [isPending, startTransition] = useTransition() const [confirmCancel, setConfirmCancel] = useState(false) const counts = useMemo( () => computeCounts(draft, pbiSummary), [draft, pbiSummary], ) function handleCancel() { setConfirmCancel(true) } function confirmCancelAction() { setConfirmCancel(false) startTransition(async () => { try { await clearPendingSprintDraft(productId) } catch (err) { const message = err instanceof Error ? err.message : 'Annuleren mislukt' toast.error(message) } }) } function handleCreate() { startTransition(async () => { const result = await createSprintWithSelectionAction({ productId, metadata: { goal: draft.goal, startAt: draft.startAt, endAt: draft.endAt, }, pbiIntent: draft.pbiIntent, storyOverrides: draft.storyOverrides, }) if ('error' in result) { toast.error(result.error) return } const { conflicts } = result if (conflicts.notEligible.length > 0) { toast.warning( `${conflicts.notEligible.length} stor${ conflicts.notEligible.length === 1 ? 'y is' : 'ies zijn' } overgeslagen (al in een andere sprint of afgerond).`, ) } else { toast.success('Sprint aangemaakt') } router.refresh() }) } const storyLabel = counts.hasUnknownTotal ? `${counts.storyCount}+` : counts.storyCount const pbiSuffix = counts.pbiCount === 1 ? '' : "'s" return (
Sprint definiëren — {draft.goal}
{counts.pbiCount} PBI{pbiSuffix} · {storyLabel} stor {counts.storyCount === 1 ? 'y' : 'ies'} geselecteerd
Sprint-definitie annuleren? Je conceptselectie gaat verloren. Het sprint-doel en de gemarkeerde PBI/stories worden verwijderd. setConfirmCancel(false)}> Doorgaan Ja, annuleren
) }