'use client' import { useState, useTransition, useActionState, useRef } from 'react' import { Button } from '@/components/ui/button' import { Textarea } from '@/components/ui/textarea' import { Dialog, DialogContent, DialogTitle, } from '@/components/ui/dialog' import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog' import { toast } from 'sonner' import { DemoTooltip } from '@/components/shared/demo-tooltip' import { useDirtyCloseGuard, DirtyCloseGuardDialog, } from '@/components/shared/use-dirty-close-guard' import { useDialogSubmitShortcut } from '@/components/shared/use-dialog-submit-shortcut' import { entityDialogContentClasses, entityDialogFooterClasses, entityDialogHeaderClasses, } from '@/components/shared/entity-dialog-layout' import { updateSprintGoalAction, updateSprintDatesAction, completeSprintAction, setAllSprintTasksDoneAction } from '@/actions/sprints' import type { SprintStory } from './sprint-backlog' import { debugProps } from '@/lib/debug' import { SprintSwitcher } from '@/components/shared/sprint-switcher' import type { SprintSwitcherItem } from '@/lib/sprint-switcher-data' interface Sprint { id: string code: string sprint_goal: string status: string start_date: Date | null end_date: Date | null } interface SprintHeaderProps { productId: string productName: string sprint: Sprint isDemo: boolean sprintStories: SprintStory[] switcherSprints: SprintSwitcherItem[] switcherActiveSprint: SprintSwitcherItem | null switcherBuildingSprintIds: string[] } interface ActionResult { success?: boolean error?: string code?: number fieldErrors?: Record } function toDateInputValue(d: Date | null) { if (!d) return '' return d.toISOString().slice(0, 10) } export function SprintHeader({ productId, productName, sprint, isDemo, sprintStories, switcherSprints, switcherActiveSprint, switcherBuildingSprintIds }: SprintHeaderProps) { const [editingGoal, setEditingGoal] = useState(false) const [editingDates, setEditingDates] = useState(false) const [completeOpen, setCompleteOpen] = useState(false) const [decisions, setDecisions] = useState>({}) const [isCompleting, startCompleting] = useTransition() const [showAllDoneConfirm, setShowAllDoneConfirm] = useState(false) const [isSettingAllDone, startSettingAllDone] = useTransition() const [datesDirty, setDatesDirty] = useState(false) const datesFormRef = useRef(null) const [, goalFormAction, goalPending] = useActionState( async (_prev, fd) => { const result = await updateSprintGoalAction(_prev, fd) as ActionResult if (result?.success) { setEditingGoal(false); toast.success('Sprint goal opgeslagen') } else if (result?.error) toast.error(result.error) return result }, undefined, ) const [datesState, datesFormAction, datesPending] = useActionState( async (_prev, fd) => { const result = await updateSprintDatesAction(_prev, fd) as ActionResult if (result?.success) { setEditingDates(false); setDatesDirty(false); toast.success('Sprint datums opgeslagen') } else if (result?.code !== 422 && result?.error) toast.error(result.error) return result }, undefined, ) const datesFieldError = (field: string) => datesState?.fieldErrors?.[field]?.[0] const datesCloseGuard = useDirtyCloseGuard(datesDirty, () => setEditingDates(false)) const datesKeyDown = useDialogSubmitShortcut(() => datesFormRef.current?.requestSubmit()) function setDecision(storyId: string, value: 'DONE' | 'OPEN') { setDecisions(prev => ({ ...prev, [storyId]: value })) } function handleComplete() { const finalDecisions: Record = {} sprintStories.forEach(s => { finalDecisions[s.id] = decisions[s.id] ?? 'OPEN' }) startCompleting(async () => { const result = await completeSprintAction(sprint.id, finalDecisions) if ('error' in result) toast.error(result.error ?? 'Sprint afronden mislukt') else { toast.success('Sprint afgerond'); setCompleteOpen(false) } }) } function handleAllDone() { startSettingAllDone(async () => { const result = await setAllSprintTasksDoneAction(sprint.id) if (!result.ok) { toast.error(result.error ?? 'Alles op done mislukt') } else { const allDone: Record = {} sprintStories.forEach(s => { allDone[s.id] = 'DONE' }) setDecisions(allDone) } setShowAllDoneConfirm(false) }) } return (
{productName} Sprint actief · {sprint.code}
{editingGoal ? (