'use client' import { usePathname, useRouter } from 'next/navigation' import { useState, useTransition } from 'react' import { Check, ChevronDown } from 'lucide-react' import { toast } from 'sonner' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' import { cn } from '@/lib/utils' import { clearActiveSprintAction, switchActiveSprintAction, } from '@/actions/active-sprint' import { useProductWorkspaceStore } from '@/stores/product-workspace/store' import { useUserSettingsStore } from '@/stores/user-settings/store' import type { SprintStatusApi } from '@/lib/task-status' import { debugProps } from '@/lib/debug' type SprintItem = { id: string; code: string; sprint_goal: string; status: SprintStatusApi } interface SprintSwitcherProps { productId: string sprints: SprintItem[] activeSprint: SprintItem | null buildingSprintIds: string[] } const SPRINT_STATUS_LABEL: Record = { open: 'Open', closed: 'Gesloten', archived: 'Gearchiveerd', failed: 'Mislukt', } export function SprintSwitcher({ productId, sprints, activeSprint, buildingSprintIds, }: SprintSwitcherProps) { const pathname = usePathname() const router = useRouter() const [isPending, startTransition] = useTransition() const [showClosed, setShowClosed] = useState(false) const buildingSet = new Set(buildingSprintIds) // PBI-79: zolang er een sprint-draft loopt tonen we 'Concept — [goal]' // bovenaan de dropdown. De draft staat alleen in deze session-store; bij // page-refresh/leave is hij weg. const draftGoal = useUserSettingsStore( (s) => s.entities.settings.workflow?.pendingSprintDraft?.[productId]?.goal ?? null, ) const visibleSprints = sprints.filter(s => { if (showClosed) return true if (s.id === activeSprint?.id) return true return s.status === 'open' }) function handleSwitchSprint(sprintId: string) { if (sprintId === activeSprint?.id) return startTransition(async () => { const result = await switchActiveSprintAction(productId, sprintId) if ('error' in result) { toast.error( typeof result.error === 'string' ? result.error : 'Wisselen mislukt', ) return } // Synchroniseer de client-side workspace-store met de auto-select die // server-side is bepaald — voorkomt korte flash van vorige selectie // voordat router.refresh de SSR-render binnenhaalt. const store = useProductWorkspaceStore.getState() if (result.pbiId) { store.setActivePbi(result.pbiId) if (result.storyId) { store.setActiveStory(result.storyId) } } else { store.setActivePbi(null) } if (pathname.includes('/sprint')) { router.push(`/products/${productId}/sprint/${sprintId}`) } else { router.refresh() } }) } function handleClearActiveSprint() { if (!activeSprint) return startTransition(async () => { const result = await clearActiveSprintAction(productId) if (result?.error) { toast.error(typeof result.error === 'string' ? result.error : 'Wisselen mislukt') return } if (pathname.includes('/sprint')) { router.push(`/products/${productId}`) } else { router.refresh() } }) } if (sprints.length === 0) { return ( Geen sprints Maak een sprint aan vanuit de Product Backlog ) } return ( {activeSprint ? activeSprint.code : 'Selecteer sprint'} {activeSprint && ( {buildingSet.has(activeSprint.id) ? 'BUILDING' : SPRINT_STATUS_LABEL[activeSprint.status]} )} {draftGoal && ( <> ⚙ Concept — {draftGoal} )} — Geen actieve sprint — {visibleSprints.length === 0 ? (
Geen open sprints
) : ( visibleSprints.map(s => ( handleSwitchSprint(s.id)} className={cn( 'flex items-center gap-2', s.id === activeSprint?.id && 'bg-primary-container text-primary-container-foreground font-medium', )} > {s.code} {s.sprint_goal} {buildingSet.has(s.id) ? 'BUILDING' : SPRINT_STATUS_LABEL[s.status]} )) )}
) }