feat(ST-v3leym34): sorteerbare kolomkoppen met SortHeader in ideeëntabel

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Scrum4Me Agent 2026-05-06 06:36:25 +02:00
parent 31dc429b61
commit 02045dc102

View file

@ -10,9 +10,10 @@
import { useMemo, useState, useTransition } from 'react'
import { useRouter } from 'next/navigation'
import { Plus } from 'lucide-react'
import { Plus, ArrowUp, ArrowDown, ArrowUpDown } from 'lucide-react'
import { toast } from 'sonner'
import { cn } from '@/lib/utils'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Textarea } from '@/components/ui/textarea'
@ -50,6 +51,8 @@ interface ProductOption {
repo_url: string | null
}
type SortKey = 'code' | 'title' | 'product' | 'status'
interface IdeaListProps {
ideas: IdeaDto[]
products: ProductOption[]
@ -67,6 +70,38 @@ const STATUS_FILTERS: { value: IdeaStatusApi; label: string }[] = [
{ value: 'plan_failed', label: 'Plan mislukt' },
]
function SortHeader({
col,
label,
sortKey,
sortDir,
onSort,
}: {
col: SortKey
label: string
sortKey: SortKey
sortDir: 'asc' | 'desc'
onSort: (col: SortKey) => void
}) {
const active = sortKey === col
const Icon = active
? sortDir === 'asc' ? ArrowUp : ArrowDown
: ArrowUpDown
return (
<button
type="button"
onClick={() => onSort(col)}
className={cn(
'flex items-center gap-1 text-xs font-medium hover:text-foreground transition-colors',
active ? 'text-foreground' : 'text-muted-foreground'
)}
>
{label}
<Icon className="size-3" />
</button>
)
}
export function IdeaList({ ideas, products, isDemo }: IdeaListProps) {
const router = useRouter()
const [isPending, startTransition] = useTransition()
@ -76,6 +111,10 @@ export function IdeaList({ ideas, products, isDemo }: IdeaListProps) {
const [productFilter, setProductFilter] = useState<string>('all')
const [statusFilter, setStatusFilter] = useState<Set<IdeaStatusApi>>(new Set())
// Sort state
const [sortKey, setSortKey] = useState<SortKey>('code')
const [sortDir, setSortDir] = useState<'asc' | 'desc'>('asc')
// Create-form state
const [showCreate, setShowCreate] = useState(false)
const [newTitle, setNewTitle] = useState('')
@ -84,7 +123,7 @@ export function IdeaList({ ideas, products, isDemo }: IdeaListProps) {
const filtered = useMemo(() => {
const q = search.trim().toLowerCase()
return ideas.filter((idea) => {
const result = ideas.filter((idea) => {
if (q && !idea.title.toLowerCase().includes(q)) return false
if (productFilter !== 'all') {
if (productFilter === 'none') {
@ -99,7 +138,25 @@ export function IdeaList({ ideas, products, isDemo }: IdeaListProps) {
if (statusFilter.size > 0 && !statusFilter.has(idea.status)) return false
return true
})
}, [ideas, search, productFilter, statusFilter])
const dir = sortDir === 'asc' ? 1 : -1
return [...result].sort((a, b) => {
switch (sortKey) {
case 'code': return dir * a.code.localeCompare(b.code)
case 'title': return dir * a.title.localeCompare(b.title)
case 'product': return dir * (a.product?.name ?? '').localeCompare(b.product?.name ?? '')
case 'status': return dir * a.status.localeCompare(b.status)
}
})
}, [ideas, search, productFilter, statusFilter, sortKey, sortDir])
function handleSort(col: SortKey) {
if (sortKey === col) {
setSortDir((d) => (d === 'asc' ? 'desc' : 'asc'))
} else {
setSortKey(col)
setSortDir('asc')
}
}
function toggleStatus(s: IdeaStatusApi) {
setStatusFilter((prev) => {
@ -264,10 +321,10 @@ export function IdeaList({ ideas, products, isDemo }: IdeaListProps) {
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-24">Code</TableHead>
<TableHead>Titel</TableHead>
<TableHead className="w-40">Product</TableHead>
<TableHead className="w-32">Status</TableHead>
<TableHead className="w-24"><SortHeader col="code" label="Code" sortKey={sortKey} sortDir={sortDir} onSort={handleSort} /></TableHead>
<TableHead><SortHeader col="title" label="Titel" sortKey={sortKey} sortDir={sortDir} onSort={handleSort} /></TableHead>
<TableHead className="w-40"><SortHeader col="product" label="Product" sortKey={sortKey} sortDir={sortDir} onSort={handleSort} /></TableHead>
<TableHead className="w-32"><SortHeader col="status" label="Status" sortKey={sortKey} sortDir={sortDir} onSort={handleSort} /></TableHead>
<TableHead className="w-72">Acties</TableHead>
</TableRow>
</TableHeader>