From 72d72fd648dba21ee4a44f2dd09697d26d7916bf Mon Sep 17 00:00:00 2001 From: Madhura68 Date: Wed, 29 Apr 2026 17:16:44 +0200 Subject: [PATCH] feat(ST-1109.8): show PBI status badge and consolidate filters into popover MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Pbi.status (lowercase API) flows from page.tsx via pbiStatusToApi - Status badge rendered in BacklogCard's badge slot using PBI_STATUS_COLORS - Two old Select dropdowns replaced by single Popover with three pill-button sections (Sorteren, Prioriteit, Status) and a "Wis filters" footer - Filter trigger shows active count "(n)" badge in label - Active priority/status filters still surface as dismissable chips next to the trigger for at-a-glance feedback - onEdit passes the full Pbi (incl. status) so the dialog opens with the correct current status — closes the data flow loop opened in ST-1109.7 Co-Authored-By: Claude Opus 4.7 (1M context) --- app/(app)/products/[id]/page.tsx | 3 +- components/backlog/pbi-list.tsx | 170 +++++++++++++++++++++++++------ 2 files changed, 139 insertions(+), 34 deletions(-) diff --git a/app/(app)/products/[id]/page.tsx b/app/(app)/products/[id]/page.tsx index b6e110b..426cd03 100644 --- a/app/(app)/products/[id]/page.tsx +++ b/app/(app)/products/[id]/page.tsx @@ -2,6 +2,7 @@ import { notFound, redirect } from 'next/navigation' import { getSession } from '@/lib/auth' import { getAccessibleProduct } from '@/lib/product-access' import { prisma } from '@/lib/prisma' +import { pbiStatusToApi } from '@/lib/task-status' import { SplitPane } from '@/components/split-pane/split-pane' import { PbiList } from '@/components/backlog/pbi-list' import { StoryPanel } from '@/components/backlog/story-panel' @@ -94,7 +95,7 @@ export default async function ProductBacklogPage({ params }: Props) { left={ ({ id: p.id, code: p.code, title: p.title, priority: p.priority, description: p.description, created_at: p.created_at }))} + 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} /> } diff --git a/components/backlog/pbi-list.tsx b/components/backlog/pbi-list.tsx index 588706a..538dcaa 100644 --- a/components/backlog/pbi-list.tsx +++ b/components/backlog/pbi-list.tsx @@ -23,7 +23,7 @@ import { CSS } from '@dnd-kit/utilities' import { toast } from 'sonner' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' +import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover' import { PanelNavBar } from '@/components/shared/panel-nav-bar' import { useSelectionStore } from '@/stores/selection-store' import { usePlannerStore } from '@/stores/planner-store' @@ -33,6 +33,8 @@ import { cn } from '@/lib/utils' import { PbiDialog, type PbiDialogState } from './pbi-dialog' import { BacklogCard } from './backlog-card' import { PRIORITY_COLORS } from '@/components/shared/priority-select' +import { PBI_STATUS_LABELS, PBI_STATUS_COLORS } from '@/components/shared/pbi-status-select' +import type { PbiStatusApi } from '@/lib/task-status' const PRIORITY_LABELS: Record = { 1: 'Kritiek', @@ -44,6 +46,62 @@ const PRIORITY_LABELS: Record = { type SortMode = 'priority' | 'code' | 'date' +const SORT_OPTIONS: Array<{ value: SortMode; label: string }> = [ + { value: 'priority', label: 'Prioriteit' }, + { value: 'code', label: 'Code' }, + { value: 'date', label: 'Datum' }, +] + +const PRIORITY_OPTIONS: Array<{ value: number | 'all'; label: string }> = [ + { value: 'all', label: 'Alle' }, + { value: 1, label: 'Kritiek' }, + { value: 2, label: 'Hoog' }, + { value: 3, label: 'Gemiddeld' }, + { value: 4, label: 'Laag' }, +] + +const STATUS_OPTIONS: Array<{ value: PbiStatusApi | 'all'; label: string }> = [ + { value: 'all', label: 'Alle' }, + { value: 'ready', label: 'Klaar' }, + { value: 'blocked', label: 'Geblokkeerd' }, + { value: 'done', label: 'Afgerond' }, +] + +function FilterPills({ + label, + options, + value, + onChange, +}: { + label: string + options: Array<{ value: T; label: string }> + value: T + onChange: (v: T) => void +}) { + return ( +
+

{label}

+
+ {options.map((opt) => ( + + ))} +
+
+ ) +} + interface Pbi { id: string code: string | null @@ -51,6 +109,7 @@ interface Pbi { priority: number description?: string | null created_at: Date + status: PbiStatusApi } interface PbiListProps { @@ -100,6 +159,11 @@ function SortablePbiRow({ aria-selected={isSelected} onClick={onSelect} onKeyDown={(e: React.KeyboardEvent) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onSelect() } }} + badge={ + + {PBI_STATUS_LABELS[pbi.status]} + + } actions={!isDemo ? (
)} - - + {filterStatus !== 'all' && ( + + )} + + + {`Filters${activeFilterCount > 0 ? ` (${activeFilterCount})` : ''}`} + + } + /> + + + + +
+ +
+
+
{!isDemo && (