From 02045dc1023ea345b432f7b3d91c48bc88d509ce Mon Sep 17 00:00:00 2001 From: Scrum4Me Agent <30029041+madhura68@users.noreply.github.com> Date: Wed, 6 May 2026 06:36:25 +0200 Subject: [PATCH] =?UTF-8?q?feat(ST-v3leym34):=20sorteerbare=20kolomkoppen?= =?UTF-8?q?=20met=20SortHeader=20in=20idee=C3=ABntabel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- components/ideas/idea-list.tsx | 71 ++++++++++++++++++++++++++++++---- 1 file changed, 64 insertions(+), 7 deletions(-) diff --git a/components/ideas/idea-list.tsx b/components/ideas/idea-list.tsx index 2edac75..2b92ca2 100644 --- a/components/ideas/idea-list.tsx +++ b/components/ideas/idea-list.tsx @@ -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 ( + + ) +} + 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('all') const [statusFilter, setStatusFilter] = useState>(new Set()) + // Sort state + const [sortKey, setSortKey] = useState('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) { - Code - Titel - Product - Status + + + + Acties