'use client' import { useState, useActionState, useRef } from 'react' import { useRouter } from 'next/navigation' import { Button } from '@/components/ui/button' import { Textarea } from '@/components/ui/textarea' import { Dialog, DialogContent, DialogTitle, } from '@/components/ui/dialog' 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 { createSprintAction } from '@/actions/sprints' import { useProductWorkspaceStore } from '@/stores/product-workspace/store' import { selectActivePbi, selectStoriesForActivePbi, } from '@/stores/product-workspace/selectors' import { useShallow } from 'zustand/react/shallow' interface StartSprintButtonProps { productId: string isDemo?: boolean } interface ActionResult { success?: boolean error?: string code?: number fieldErrors?: Record sprintId?: string } function todayLocalDate() { return new Date().toLocaleDateString('en-CA') } export function StartSprintButton({ productId, isDemo = false }: StartSprintButtonProps) { const [open, setOpen] = useState(false) const [dirty, setDirty] = useState(false) const formRef = useRef(null) const router = useRouter() // PBI-74 / T-852: actief PBI + free-story count via workspace-store selectors. const selectedPbiId = useProductWorkspaceStore((s) => s.context.activePbiId) const selectedPbi = useProductWorkspaceStore(selectActivePbi) const stories = useProductWorkspaceStore(useShallow(selectStoriesForActivePbi)) const freeStoryCount = stories.filter((story) => story.sprint_id === null).length const [state, formAction, pending] = useActionState( async (_prev, fd) => { const result = await createSprintAction(_prev, fd) as ActionResult if (result?.success) { setOpen(false) setDirty(false) router.refresh() } else if (result?.code !== 422 && result?.error) { // Toast handled by caller; here we just keep the form open } return result }, undefined, ) const fieldError = (field: string) => state?.fieldErrors?.[field]?.[0] const globalError = state?.code !== 422 ? state?.error : undefined const closeGuard = useDirtyCloseGuard(dirty, () => setOpen(false)) const handleKeyDown = useDialogSubmitShortcut(() => formRef.current?.requestSubmit()) return ( <> { if (!o) closeGuard.attemptClose(); else setOpen(o) }}>
Nieuwe Sprint starten
setDirty(true)} className="flex-1 overflow-y-auto px-6 py-6 space-y-6" > {selectedPbiId && } {!selectedPbi ? (
Geen PBI geselecteerd — de sprint wordt leeg aangemaakt. Je kunt later stories toevoegen via slepen.
) : freeStoryCount === 0 ? (
PBI {selectedPbi.code ?? selectedPbi.id.slice(0, 8)} heeft geen vrije stories (alle stories zitten al in een andere sprint of zijn afgerond) — de sprint wordt leeg aangemaakt.
) : (
{freeStoryCount} {freeStoryCount === 1 ? 'story' : 'stories'} van PBI {selectedPbi.code ?? selectedPbi.id.slice(0, 8)} {selectedPbi.title ? ` (${selectedPbi.title})` : ''} worden toegevoegd aan deze sprint.
)}