'use client' // IdeaDetailLayout — top-level container voor /ideas/[id]. // Bevat: header (titel + status-badge + row-actions), tab-switcher // (Idee/Grill/Plan/Timeline), en per-tab content. // // URL-based tabs (?tab=grill) — bookmarkable + refresh-safe. // Md-editor (T-511), timeline (T-512), pbi-link-card (T-512) komen later. import { useState, useTransition } from 'react' import { useRouter, useSearchParams } from 'next/navigation' import Link from 'next/link' import { ArrowLeft, ExternalLink } 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 { getIdeaStatusBadge } from '@/lib/idea-status-colors' import type { IdeaStatusApi } from '@/lib/idea-status' import { isIdeaEditable } from '@/lib/idea-status' import type { IdeaDto } from '@/lib/idea-dto' import { updateIdeaAction, archiveIdeaAction } from '@/actions/ideas' import { IdeaRowActions } from '@/components/ideas/idea-row-actions' import { IdeaMdEditor } from '@/components/ideas/idea-md-editor' import { IdeaPbiLinkCard } from '@/components/ideas/idea-pbi-link-card' import { IdeaTimeline } from '@/components/ideas/idea-timeline' import { DownloadMdButton } from '@/components/ideas/download-md-button' 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', } type TabKey = 'idee' | 'grill' | 'plan' | 'timeline' const TABS: { key: TabKey; label: string }[] = [ { key: 'idee', label: 'Idee' }, { key: 'grill', label: 'Grill' }, { key: 'plan', label: 'Plan' }, { key: 'timeline', label: 'Chat & Timeline' }, ] interface IdeaLog { id: string type: string content: string metadata: unknown created_at: string } interface IdeaQuestion { id: string question: string options: string[] | null status: 'open' | 'answered' | 'cancelled' | 'expired' answer: string | null created_at: string expires_at: string } interface ProductOption { id: string name: string repo_url: string | null } export interface IdeaUserQuestionDto { id: string question: string answer: string | null status: 'pending' | 'answered' created_at: string } interface Props { idea: IdeaDto grill_md: string | null plan_md: string | null products: ProductOption[] logs: IdeaLog[] questions: IdeaQuestion[] userQuestions: IdeaUserQuestionDto[] isDemo: boolean initialTab: string } export function IdeaDetailLayout({ idea, grill_md, plan_md, products, logs, questions, userQuestions, isDemo, initialTab, }: Props) { const router = useRouter() const searchParams = useSearchParams() const [pending, startTransition] = useTransition() const tab = (TABS.some((t) => t.key === initialTab) ? initialTab : 'idee') as TabKey function setTab(key: TabKey) { const params = new URLSearchParams(searchParams.toString()) params.set('tab', key) router.replace(`/ideas/${idea.id}?${params.toString()}`, { scroll: false }) } function handleArchive() { if (isDemo) return if (!confirm('Idee archiveren?')) return startTransition(async () => { const r = await archiveIdeaAction(idea.id) if ('error' in r) { toast.error(r.error) return } toast.success('Idee gearchiveerd') router.push('/ideas') }) } const badge = getIdeaStatusBadge(API_TO_DB[idea.status]) return (
{/* Breadcrumb / back-link */} Alle ideeën {/* Header */}

{idea.code}

{idea.title}

{badge.label} {idea.product ? ( {idea.product.name} ) : ( geen product )}
{/* PBI-link card / Re-link banner bij PLANNED */} {/* Tab-switcher */} {/* Tab content */} {tab === 'idee' && ( )} {tab === 'grill' && ( )} {tab === 'plan' && ( )} {tab === 'timeline' && ( )}
) } // --------------------------------------------------------------------------- // Idee-tab: inline form (geen modal — de detailpagina IS de form). interface FormProps { idea: IdeaDto products: ProductOption[] isDemo: boolean pending: boolean } function IdeaFormSection({ idea, products, isDemo, pending }: FormProps) { const router = useRouter() const editable = !isDemo && isIdeaEditable(API_TO_DB[idea.status]) const [title, setTitle] = useState(idea.title) const [description, setDescription] = useState(idea.description ?? '') const [productId, setProductId] = useState(idea.product_id ?? '') const [submitting, startSubmit] = useTransition() const dirty = title !== idea.title || description !== (idea.description ?? '') || productId !== (idea.product_id ?? '') function save() { startSubmit(async () => { const r = await updateIdeaAction(idea.id, { title, description: description || null, product_id: productId || null, }) if ('error' in r) { toast.error(r.error) return } toast.success('Opgeslagen') router.refresh() }) } return (
setTitle(e.target.value)} disabled={!editable || pending || submitting} />