'use client' import { useTransition } from 'react' import { useRouter } from 'next/navigation' import { toast } from 'sonner' import { Button } from '@/components/ui/button' import { useProductWorkspaceStore } from '@/stores/product-workspace/store' import { selectIsDirty, selectPendingCount, } from '@/stores/product-workspace/selectors' import { commitSprintMembershipAction } from '@/actions/sprints' interface SaveSprintButtonProps { activeSprintId: string } /** * PBI-79 / ST-1338 / T-940: 'Sprint opslaan'-knop voor state B. * Altijd zichtbaar zolang er een actieve sprint is. Disabled bij clean, * enabled met teller bij dirty. Commit gebeurt via * commitSprintMembershipAction; client patcht gericht via * applyMembershipCommitResult. Geen router.refresh. */ export function SaveSprintButton({ activeSprintId }: SaveSprintButtonProps) { const router = useRouter() const isDirty = useProductWorkspaceStore(selectIsDirty) const count = useProductWorkspaceStore(selectPendingCount) const adds = useProductWorkspaceStore((s) => s.sprintMembership.pending.adds) const removes = useProductWorkspaceStore( (s) => s.sprintMembership.pending.removes, ) const applyMembershipCommitResult = useProductWorkspaceStore( (s) => s.applyMembershipCommitResult, ) const [isPending, startTransition] = useTransition() function handleSave() { startTransition(async () => { const result = await commitSprintMembershipAction({ activeSprintId, adds: [...adds], removes: [...removes], }) if ('error' in result) { toast.error(result.error) return } applyMembershipCommitResult({ activeSprintId, addedStoryIds: adds.filter((id) => result.affectedStoryIds.includes(id), ), removedStoryIds: removes.filter((id) => result.affectedStoryIds.includes(id), ), }) const skipped = result.conflicts.notEligible.length + result.conflicts.alreadyRemoved.length if (skipped > 0) { toast.warning( `${skipped} wijziging${skipped === 1 ? '' : 'en'} overgeslagen — story al in andere sprint of inmiddels verwijderd.`, ) } else { toast.success('Sprint opgeslagen') } // Gericht patchen voldoende voor lokale UI; refresh haalt server-side // counts opnieuw op zodat tri-state in volgende renders klopt. router.refresh() }) } return ( ) }