ST-1229: UI: "Alles op done" knop met AlertDialog in sprint-header.tsx (#121)
* feat(ST-n1csfo4j): AlertDialog imports, state en transition voor Alles-op-done knop Voegt AlertDialog-imports, setAllSprintTasksDoneAction-import, productId-prop (hernoemd van _productId) en showAllDoneConfirm/isSettingAllDone state toe aan sprint-header.tsx als voorbereiding op de Alles-op-done AlertDialog-knop. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ST-n1csfo4j): handleAllDone + AlertDialog + Alles-op-done knop in sprint-afronden-dialog Voegt handleAllDone toe (roept setAllSprintTasksDoneAction aan en zet alle per-story decisions op DONE in de UI), een bevestigende AlertDialog en een 'Alles op done'-knop bovenaan de story-lijst in de sprint-afronden-dialog. Voegt setAllSprintTasksDoneAction ook toe aan actions/sprints.ts omdat die branch nog niet op main staat. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f09f5a2a06
commit
fd02cda207
1 changed files with 60 additions and 2 deletions
|
|
@ -8,6 +8,16 @@ import {
|
|||
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 {
|
||||
|
|
@ -20,7 +30,7 @@ import {
|
|||
entityDialogFooterClasses,
|
||||
entityDialogHeaderClasses,
|
||||
} from '@/components/shared/entity-dialog-layout'
|
||||
import { updateSprintGoalAction, updateSprintDatesAction, completeSprintAction } from '@/actions/sprints'
|
||||
import { updateSprintGoalAction, updateSprintDatesAction, completeSprintAction, setAllSprintTasksDoneAction } from '@/actions/sprints'
|
||||
import type { SprintStory } from './sprint-backlog'
|
||||
|
||||
interface Sprint {
|
||||
|
|
@ -51,12 +61,14 @@ function toDateInputValue(d: Date | null) {
|
|||
return d.toISOString().slice(0, 10)
|
||||
}
|
||||
|
||||
export function SprintHeader({ productId: _productId, productName, sprint, isDemo, sprintStories }: SprintHeaderProps) {
|
||||
export function SprintHeader({ productId, productName, sprint, isDemo, sprintStories }: SprintHeaderProps) {
|
||||
const [editingGoal, setEditingGoal] = useState(false)
|
||||
const [editingDates, setEditingDates] = useState(false)
|
||||
const [completeOpen, setCompleteOpen] = useState(false)
|
||||
const [decisions, setDecisions] = useState<Record<string, 'DONE' | 'OPEN'>>({})
|
||||
const [isCompleting, startCompleting] = useTransition()
|
||||
const [showAllDoneConfirm, setShowAllDoneConfirm] = useState(false)
|
||||
const [isSettingAllDone, startSettingAllDone] = useTransition()
|
||||
const [datesDirty, setDatesDirty] = useState(false)
|
||||
const datesFormRef = useRef<HTMLFormElement>(null)
|
||||
|
||||
|
|
@ -102,6 +114,20 @@ export function SprintHeader({ productId: _productId, productName, sprint, isDem
|
|||
})
|
||||
}
|
||||
|
||||
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<string, 'DONE' | 'OPEN'> = {}
|
||||
sprintStories.forEach(s => { allDone[s.id] = 'DONE' })
|
||||
setDecisions(allDone)
|
||||
}
|
||||
setShowAllDoneConfirm(false)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="px-4 py-3 border-b border-border bg-surface-container-low shrink-0">
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
|
|
@ -208,6 +234,18 @@ export function SprintHeader({ productId: _productId, productName, sprint, isDem
|
|||
<p className="text-sm text-muted-foreground">
|
||||
Geef per story aan wat er mee moet gebeuren:
|
||||
</p>
|
||||
<div className="flex justify-end">
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="border-status-done/40 text-status-done hover:bg-status-done/10"
|
||||
disabled={isSettingAllDone || isCompleting}
|
||||
onClick={() => setShowAllDoneConfirm(true)}
|
||||
>
|
||||
{isSettingAllDone ? 'Bezig…' : 'Alles op done'}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
{sprintStories.map(story => (
|
||||
<div key={story.id} className="flex items-center justify-between gap-3 p-2 bg-surface-container-low rounded-lg">
|
||||
|
|
@ -245,6 +283,26 @@ export function SprintHeader({ productId: _productId, productName, sprint, isDem
|
|||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
<AlertDialog open={showAllDoneConfirm} onOpenChange={setShowAllDoneConfirm}>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Alles op done zetten?</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
Alle taken én stories in de sprint — inclusief taken met status
|
||||
REVIEW — worden op DONE gezet. De per-story toggles hieronder
|
||||
worden daarna bijgewerkt. Je kunt daarna nog per story aanpassen
|
||||
vóór je de sprint afrondt.
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel disabled={isSettingAllDone}>Annuleren</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={handleAllDone} disabled={isSettingAllDone}>
|
||||
{isSettingAllDone ? 'Bezig…' : 'Alles op done'}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue