feat: ST-201-ST-210 M2 stories, drag-and-drop en Zustand stores
- usePlannerStore met pbiOrder/storyOrder init/reorder/rollback (ST-201) - useSelectionStore uitgebreid met selectedStoryId en clearSelection (ST-202) - PBI drag-and-drop binnen prioriteitsgroep via dnd-kit (ST-203) - PBI slepen over prioriteitsgrens wijzigt priority (ST-204) - Stories als blokken met prioriteit- en statusbadge (ST-205/ST-206) - Story drag-and-drop horizontaal binnen en tussen groepen (ST-207) - Story detail slide-over met bewerkformulier (ST-208) - Story verwijderen met bevestigingsstap (ST-209) - Filter op status en prioriteit in rechterpaneel (ST-210) - Fix: infinite loop in useEffect door stabiele string dependency Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ffda65490f
commit
4dd62c199c
25 changed files with 1794 additions and 100 deletions
|
|
@ -6,6 +6,8 @@ import { prisma } from '@/lib/prisma'
|
|||
import { SplitPane } from '@/components/split-pane/split-pane'
|
||||
import { PbiList } from '@/components/backlog/pbi-list'
|
||||
import { StoryPanel } from '@/components/backlog/story-panel'
|
||||
import type { Story } from '@/components/backlog/story-panel'
|
||||
import Link from 'next/link'
|
||||
|
||||
interface Props {
|
||||
params: Promise<{ id: string }>
|
||||
|
|
@ -28,16 +30,26 @@ export default async function ProductBacklogPage({ params }: Props) {
|
|||
const stories = await prisma.story.findMany({
|
||||
where: { product_id: id },
|
||||
orderBy: [{ priority: 'asc' }, { sort_order: 'asc' }],
|
||||
select: { id: true, title: true, status: true, pbi_id: true },
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
description: true,
|
||||
acceptance_criteria: true,
|
||||
priority: true,
|
||||
status: true,
|
||||
pbi_id: true,
|
||||
},
|
||||
})
|
||||
|
||||
// Group stories by PBI id
|
||||
const storiesByPbi: Record<string, typeof stories> = {}
|
||||
const storiesByPbi: Record<string, Story[]> = {}
|
||||
for (const story of stories) {
|
||||
if (!storiesByPbi[story.pbi_id]) storiesByPbi[story.pbi_id] = []
|
||||
storiesByPbi[story.pbi_id].push(story)
|
||||
}
|
||||
|
||||
const isDemo = session.isDemo ?? false
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
{/* Product header */}
|
||||
|
|
@ -48,12 +60,12 @@ export default async function ProductBacklogPage({ params }: Props) {
|
|||
<p className="text-xs text-muted-foreground mt-0.5">{product.description}</p>
|
||||
)}
|
||||
</div>
|
||||
<a
|
||||
<Link
|
||||
href={`/products/${id}/settings`}
|
||||
className="text-xs text-muted-foreground hover:text-foreground"
|
||||
>
|
||||
Instellingen
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Split pane */}
|
||||
|
|
@ -64,13 +76,14 @@ export default async function ProductBacklogPage({ params }: Props) {
|
|||
<PbiList
|
||||
productId={id}
|
||||
pbis={pbis.map(p => ({ id: p.id, title: p.title, priority: p.priority }))}
|
||||
isDemo={session.isDemo ?? false}
|
||||
isDemo={isDemo}
|
||||
/>
|
||||
}
|
||||
right={
|
||||
<StoryPanel
|
||||
productId={id}
|
||||
storiesByPbi={storiesByPbi}
|
||||
isDemo={session.isDemo ?? false}
|
||||
isDemo={isDemo}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue