'use client' // IdeaList — top-level lijstpagina voor /ideas. // - Strikt user_id-only data (server haalt al; client filtert binnen die set). // - Filters: zoeken op titel, product-dropdown, status-multiselect. // - Klik op rij navigeert naar /ideas/[id]. Acties (Grill / Make Plan / // Materialiseer) staan in components/ideas/idea-row-actions.tsx (T-508). // - DemoTooltip rondom muteer-acties; bulk-archive blijft achter feature-flag // in T-508 en latere stories. import { useMemo, useState, useTransition } from 'react' import { useRouter } from 'next/navigation' import { Plus } from 'lucide-react' import { toast } from 'sonner' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Textarea } from '@/components/ui/textarea' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table' import { DemoTooltip } from '@/components/shared/demo-tooltip' import { getIdeaStatusBadge } from '@/lib/idea-status-colors' import type { IdeaStatusApi } from '@/lib/idea-status' import type { IdeaDto } from '@/lib/idea-dto' import { createIdeaAction, archiveIdeaAction } from '@/actions/ideas' import { IdeaRowActions } from '@/components/ideas/idea-row-actions' // Reverse mapping voor het renderen van de status-badge — DTO bevat lowercase // API-strings, het badge-helper verwacht DB-enum. const API_TO_DB: Record[0]> = { draft: 'DRAFT', grilling: 'GRILLING', grill_failed: 'GRILL_FAILED', grilled: 'GRILLED', planning: 'PLANNING', plan_failed: 'PLAN_FAILED', plan_ready: 'PLAN_READY', planned: 'PLANNED', } interface ProductOption { id: string name: string repo_url: string | null } interface IdeaListProps { ideas: IdeaDto[] products: ProductOption[] isDemo: boolean } const STATUS_FILTERS: { value: IdeaStatusApi; label: string }[] = [ { value: 'draft', label: 'Concept' }, { value: 'grilling', label: 'Grillen' }, { value: 'grilled', label: 'Gegrilld' }, { value: 'planning', label: 'Plannen' }, { value: 'plan_ready', label: 'Plan klaar' }, { value: 'planned', label: 'Gepland' }, { value: 'grill_failed', label: 'Grill mislukt' }, { value: 'plan_failed', label: 'Plan mislukt' }, ] export function IdeaList({ ideas, products, isDemo }: IdeaListProps) { const router = useRouter() const [isPending, startTransition] = useTransition() // Filter state const [search, setSearch] = useState('') const [productFilter, setProductFilter] = useState('all') const [statusFilter, setStatusFilter] = useState>(new Set()) // Create-form state const [showCreate, setShowCreate] = useState(false) const [newTitle, setNewTitle] = useState('') const [newDescription, setNewDescription] = useState('') const [newProductId, setNewProductId] = useState('') const filtered = useMemo(() => { const q = search.trim().toLowerCase() return ideas.filter((idea) => { if (q && !idea.title.toLowerCase().includes(q)) return false if (productFilter !== 'all') { if (productFilter === 'none' && idea.product_id !== null) return false if (productFilter !== 'none' && idea.product_id !== productFilter) return false } if (statusFilter.size > 0 && !statusFilter.has(idea.status)) return false return true }) }, [ideas, search, productFilter, statusFilter]) function toggleStatus(s: IdeaStatusApi) { setStatusFilter((prev) => { const next = new Set(prev) if (next.has(s)) next.delete(s) else next.add(s) return next }) } function handleCreate() { if (isDemo) return const title = newTitle.trim() if (!title) { toast.error('Titel is verplicht') return } startTransition(async () => { const r = await createIdeaAction({ title, description: newDescription.trim() || null, product_id: newProductId || null, }) if ('error' in r) { toast.error(r.error) return } toast.success(`Idee aangemaakt (${r.data?.code})`) setNewTitle('') setNewDescription('') setNewProductId('') setShowCreate(false) router.refresh() }) } function handleArchive(id: string) { if (isDemo) return startTransition(async () => { const r = await archiveIdeaAction(id) if ('error' in r) { toast.error(r.error) return } toast.success('Idee gearchiveerd') router.refresh() }) } return (
{/* Top-bar: search + nieuw-knop */}
setSearch(e.target.value)} placeholder="Zoek op titel..." className="max-w-sm" />
{/* Status-chips als multi-select filter */}
{STATUS_FILTERS.map((s) => { const active = statusFilter.has(s.value) return ( ) })}
{/* Inline create form */} {showCreate && (
setNewTitle(e.target.value)} placeholder="Titel van het idee..." disabled={isPending} />