'use client' import Link from 'next/link' import { usePathname, useRouter } from 'next/navigation' import { useTransition } from 'react' import { ChevronDown } from 'lucide-react' import { toast } from 'sonner' import { Badge } from '@/components/ui/badge' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' import { AppIcon } from '@/components/shared/app-icon' import { UserMenu } from '@/components/shared/user-menu' import { NotificationsBell } from '@/components/shared/notifications-bell' import { SoloNavStatusIndicators } from '@/components/solo/nav-status-indicators' import { cn } from '@/lib/utils' import { setActiveProductAction } from '@/actions/active-product' import { setActiveSprintAction } from '@/actions/active-sprint' import type { SprintStatusApi } from '@/lib/task-status' type SprintItem = { id: string; code: string; status: SprintStatusApi } interface NavBarProps { isDemo: boolean roles: string[] userId: string username: string email: string | null activeProduct: { id: string; name: string } | null products: { id: string; name: string }[] sprints: SprintItem[] activeSprint: SprintItem | null buildingSprintIds: string[] minQuotaPct: number } const SPRINT_STATUS_LABEL: Record = { open: 'Open', closed: 'Gesloten', archived: 'Gearchiveerd', failed: 'Mislukt', } const SPRINT_STATUS_BADGE: Record = { open: 'bg-status-in-progress text-foreground', closed: 'bg-status-done text-foreground', archived: 'bg-surface-container text-muted-foreground', failed: 'bg-status-failed text-foreground', } export function NavBar({ isDemo, roles, userId, username, email, activeProduct, products, sprints, activeSprint, buildingSprintIds, minQuotaPct, }: NavBarProps) { const pathname = usePathname() const router = useRouter() const [isPending, startTransition] = useTransition() const buildingSet = new Set(buildingSprintIds) const hasActiveSprint = !!activeSprint function handleSwitchProduct(productId: string) { startTransition(async () => { const result = await setActiveProductAction(productId) if (result?.error) { toast.error(typeof result.error === 'string' ? result.error : 'Wisselen mislukt') return } const next = products.find(p => p.id === productId) toast.success(`Actief product: ${next?.name ?? 'gewijzigd'}`) router.refresh() }) } function handleSwitchSprint(sprintId: string) { if (!activeProduct) return if (sprintId === activeSprint?.id) return startTransition(async () => { const result = await setActiveSprintAction(activeProduct.id, sprintId) if (result?.error) { toast.error(typeof result.error === 'string' ? result.error : 'Wisselen mislukt') return } if (pathname.includes('/sprint')) { router.push(`/products/${activeProduct.id}/sprint/${sprintId}`) } else { router.refresh() } }) } const activeId = activeProduct?.id ?? null // Nav link helpers const disabledSpan = (label: string) => ( {label} ) const navLink = (href: string, label: string, isActive: boolean) => ( {label} ) const sprintNode = () => { if (!activeId) return disabledSpan('Sprint') const isActive = pathname.includes('/sprint') if (!hasActiveSprint) { return ( Sprint Geen actieve sprint ) } const href = `/products/${activeId}/sprint/${activeSprint!.id}` return navLink(href, 'Sprint', isActive) } return (
{/* Links: logo + nav */}
Scrum4Me {isDemo && ( Demo )}
{/* Midden: actief product + sprint, gestapeld */}
{activeProduct ? ( {activeProduct.name.length > 22 ? activeProduct.name.slice(0, 22) + '…' : activeProduct.name} {products.map(p => ( p.id !== activeProduct.id && handleSwitchProduct(p.id)} className={cn( p.id === activeProduct.id && 'bg-primary-container text-primary-container-foreground font-medium' )} > {p.name} ))} Producten beheren → ) : ( Geen actief product )} {activeProduct && ( sprints.length === 0 ? ( Geen sprints Maak een sprint aan vanuit de Product Backlog ) : ( {activeSprint ? activeSprint.code : 'Selecteer sprint'} {activeSprint && ( {buildingSet.has(activeSprint.id) ? 'BUILDING' : SPRINT_STATUS_LABEL[activeSprint.status]} )} {sprints.map(s => ( handleSwitchSprint(s.id)} className={cn( 'flex items-center justify-between gap-2', s.id === activeSprint?.id && 'bg-primary-container text-primary-container-foreground font-medium', )} > {s.code} {buildingSet.has(s.id) ? 'BUILDING' : SPRINT_STATUS_LABEL[s.status]} ))} ) )}
{/* Rechts: solo-status + notifications + account-menu */}
) }