feat(backlog): migrate StoryPanel to store-driven + selectStory on click
Removes storiesByPbi prop; reads from useBacklogStore. Card click now dispatches selectStory(id) + shows isSelected highlight. Edit moved to inline pencil button. page.tsx drops pbis/storiesByPbi props. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
33d1c2312a
commit
c495d29313
2 changed files with 27 additions and 9 deletions
|
|
@ -137,13 +137,11 @@ export default async function ProductBacklogPage({ params, searchParams }: Props
|
|||
<PbiList
|
||||
key="pbi"
|
||||
productId={id}
|
||||
pbis={pbis.map((p: (typeof pbis)[number]) => ({ id: p.id, code: p.code, title: p.title, priority: p.priority, description: p.description, created_at: p.created_at, status: pbiStatusToApi(p.status) }))}
|
||||
isDemo={isDemo}
|
||||
/>,
|
||||
<StoryPanel
|
||||
key="story"
|
||||
productId={id}
|
||||
storiesByPbi={storiesByPbi}
|
||||
isDemo={isDemo}
|
||||
/>,
|
||||
<TaskPanel
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@
|
|||
import { PanelNavBar } from '@/components/shared/panel-nav-bar'
|
||||
import { useSelectionStore } from '@/stores/selection-store'
|
||||
import { usePlannerStore } from '@/stores/planner-store'
|
||||
import { useBacklogStore } from '@/stores/backlog-store'
|
||||
import { reorderStoriesAction } from '@/actions/stories'
|
||||
import { StoryDialog, type StoryDialogState } from './story-dialog'
|
||||
import { BacklogCard } from './backlog-card'
|
||||
|
|
@ -60,17 +61,20 @@ export interface Story {
|
|||
|
||||
interface StoryPanelProps {
|
||||
productId: string
|
||||
storiesByPbi: Record<string, Story[]>
|
||||
isDemo: boolean
|
||||
}
|
||||
|
||||
// --- Sortable story block ---
|
||||
function SortableStoryBlock({
|
||||
story,
|
||||
onClick,
|
||||
isSelected,
|
||||
onSelect,
|
||||
onEdit,
|
||||
}: {
|
||||
story: Story
|
||||
onClick: () => void
|
||||
isSelected: boolean
|
||||
onSelect: () => void
|
||||
onEdit: () => void
|
||||
}) {
|
||||
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
|
||||
id: story.id,
|
||||
|
|
@ -92,19 +96,33 @@ function SortableStoryBlock({
|
|||
code={story.code}
|
||||
priority={story.priority}
|
||||
isDragging={isDragging}
|
||||
onClick={onClick}
|
||||
isSelected={isSelected}
|
||||
onClick={onSelect}
|
||||
badge={
|
||||
<Badge className={cn('text-[10px] px-1.5 py-0 border', STATUS_COLORS[story.status])}>
|
||||
{STATUS_LABELS[story.status] ?? story.status}
|
||||
</Badge>
|
||||
}
|
||||
actions={
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); onEdit() }}
|
||||
className="text-muted-foreground hover:text-foreground p-0.5 rounded transition-colors"
|
||||
aria-label="Story bewerken"
|
||||
>
|
||||
<svg className="size-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
|
||||
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" />
|
||||
</svg>
|
||||
</button>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
// --- Main component ---
|
||||
export function StoryPanel({ productId, storiesByPbi, isDemo }: StoryPanelProps) {
|
||||
const { selectedPbiId } = useSelectionStore()
|
||||
export function StoryPanel({ productId, isDemo }: StoryPanelProps) {
|
||||
const { selectedPbiId, selectedStoryId, selectStory } = useSelectionStore()
|
||||
const storiesByPbi = useBacklogStore((s) => s.storiesByPbi)
|
||||
const { storyOrder, initStories, reorderStories, rollbackStories } = usePlannerStore()
|
||||
const [filterStatus, setFilterStatus] = useState<string | null>(null)
|
||||
const [filterPriority, setFilterPriority] = useState<number | null>(null)
|
||||
|
|
@ -263,7 +281,9 @@ export function StoryPanel({ productId, storiesByPbi, isDemo }: StoryPanelProps)
|
|||
<SortableStoryBlock
|
||||
key={story.id}
|
||||
story={story}
|
||||
onClick={() => setStoryDialogState({ mode: 'edit', story, productId })}
|
||||
isSelected={selectedStoryId === story.id}
|
||||
onSelect={() => selectStory(story.id)}
|
||||
onEdit={() => setStoryDialogState({ mode: 'edit', story, productId })}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue