feat(ST-354): add bulk-claim button to Sprint Backlog panel header
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
358c88a9d9
commit
802aa58157
1 changed files with 33 additions and 2 deletions
|
|
@ -15,7 +15,7 @@ import { PanelNavBar } from '@/components/shared/panel-nav-bar'
|
||||||
import { UserAvatar } from '@/components/shared/user-avatar'
|
import { UserAvatar } from '@/components/shared/user-avatar'
|
||||||
import { DemoTooltip } from '@/components/shared/demo-tooltip'
|
import { DemoTooltip } from '@/components/shared/demo-tooltip'
|
||||||
import { useSprintStore } from '@/stores/sprint-store'
|
import { useSprintStore } from '@/stores/sprint-store'
|
||||||
import { claimStoryAction, unclaimStoryAction, reassignStoryAction } from '@/actions/stories'
|
import { claimStoryAction, unclaimStoryAction, reassignStoryAction, claimAllUnassignedInActiveSprintAction } from '@/actions/stories'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
const STATUS_COLORS: Record<string, string> = {
|
const STATUS_COLORS: Record<string, string> = {
|
||||||
|
|
@ -222,6 +222,24 @@ export function SprintBacklogLeft({
|
||||||
}: SprintBacklogLeftProps) {
|
}: SprintBacklogLeftProps) {
|
||||||
const { sprintStoryOrder } = useSprintStore()
|
const { sprintStoryOrder } = useSprintStore()
|
||||||
const { setNodeRef, isOver } = useDroppable({ id: 'sprint-zone' })
|
const { setNodeRef, isOver } = useDroppable({ id: 'sprint-zone' })
|
||||||
|
const [isPending, startTransition] = useTransition()
|
||||||
|
|
||||||
|
const unassignedCount = stories.filter(s => s.assignee_id === null).length
|
||||||
|
const currentUserUsername = members.find(m => m.userId === currentUserId)?.username ?? null
|
||||||
|
|
||||||
|
function handleClaimAll() {
|
||||||
|
const unassigned = stories.filter(s => s.assignee_id === null)
|
||||||
|
unassigned.forEach(s => onAssigneeChange(s.id, currentUserId, currentUserUsername))
|
||||||
|
startTransition(async () => {
|
||||||
|
const result = await claimAllUnassignedInActiveSprintAction(productId)
|
||||||
|
if (!result.success) {
|
||||||
|
unassigned.forEach(s => onAssigneeChange(s.id, null, null))
|
||||||
|
toast.error(result.error ?? 'Claimen mislukt')
|
||||||
|
} else {
|
||||||
|
toast.success(`${result.count} ${result.count === 1 ? 'story' : 'stories'} geclaimd`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const storyMap = Object.fromEntries(stories.map(s => [s.id, s]))
|
const storyMap = Object.fromEntries(stories.map(s => [s.id, s]))
|
||||||
const order = sprintStoryOrder[sprintId] ?? stories.map(s => s.id)
|
const order = sprintStoryOrder[sprintId] ?? stories.map(s => s.id)
|
||||||
|
|
@ -229,7 +247,20 @@ export function SprintBacklogLeft({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full">
|
<div className="flex flex-col h-full">
|
||||||
<PanelNavBar title="Sprint Backlog" />
|
<PanelNavBar
|
||||||
|
title="Sprint Backlog"
|
||||||
|
actions={
|
||||||
|
<DemoTooltip show={isDemo}>
|
||||||
|
<button
|
||||||
|
onClick={handleClaimAll}
|
||||||
|
disabled={isDemo || unassignedCount === 0 || isPending}
|
||||||
|
className="text-xs text-primary hover:text-primary/80 disabled:opacity-40 disabled:cursor-not-allowed whitespace-nowrap"
|
||||||
|
>
|
||||||
|
{isPending ? 'Claimen…' : `Claim ongeclaimd (${unassignedCount})`}
|
||||||
|
</button>
|
||||||
|
</DemoTooltip>
|
||||||
|
}
|
||||||
|
/>
|
||||||
<div
|
<div
|
||||||
ref={setNodeRef}
|
ref={setNodeRef}
|
||||||
className={cn(
|
className={cn(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue