* feat(user-settings): voeg IdeasListPrefs schema toe met filterStatuses Nieuw IdeasListPrefs-subschema met filterStatuses (array van IdeaStatusApi-waarden), ingehangen als views.ideasList in ViewsPrefs. Testdekking voor geldig, ongeldig en leeg filterStatuses. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor: extraheer MultiFilterPills naar backlog-filter-popover Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ideas): voeg IdeasFilterPopover component toe Nieuwe client-component met multi-select statusfilter popover voor het Ideeënscherm; hergebruikt MultiFilterPills uit backlog-filter-popover. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ideas): vervang inline statuschips door IdeasFilterPopover met user-settings persistentie Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(ideas): voeg componenttests toe voor IdeasFilterPopover en persistentie Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ideas): voeg activeProductId-prop toe aan IdeaList IdeaListProps uitgebreid met activeProductId: string | null. Create-form initialiseert en reset naar het actieve product na aanmaken. Tests en page.tsx bijgewerkt (page.tsx krijgt echte waarde in volgende taak). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ideas): resolve active_product_id en geef door aan IdeaList Haalt active_product_id op via prisma.user.findUnique en resolveert het tegen de al opgehaalde toegankelijke productenlijst (AC4). Geeft het resultaat als prop door aan IdeaList in plaats van hardcoded null. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ideas): stuur activeProductId mee bij snel idee aanmaken Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(ideas): voeg component-tests toe voor activeProductId-voorvulling AC1: "Nieuw idee"-select voorgevuld met activeProductId AC2: "Nieuw idee"-select leeg bij activeProductId=null AC3: "Snel idee" stuurt product_id=activeProductId mee bij createIdeaAction Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(notifications): toon textarea altijd in answer-modal naast opties Vervang opties-XOR-textarea door twee onafhankelijke blokken: opties alleen wanneer aanwezig, vrij tekstveld altijd zichtbaar. Bij opties een visuele scheiding (border-t) en label 'Of typ een eigen antwoord'. Verstuur-knop nu altijd in footer zichtbaar (was verborgen bij opties). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(notifications): gebruik question.options?.length als conditie Gebruik de kortere optional chaining variant consistent, conform plan. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(notifications): voeg component-tests toe voor AnswerModal Dekt: optieknoppen + textarea + Verstuur zichtbaar met opties, submit via optieknop, submit via vrij tekstveld, disabled Verstuur bij leeg veld, en demo-modus (textarea + Verstuur disabled). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs(notifications): werk answer-modal spec bij voor vrije tekstveld naast opties Beschrijft dat textarea + Verstuur altijd zichtbaar zijn in multiple-choice mode. Corrigeert de Cmd/Ctrl+Enter-bullet: shortcut werkt nu ook daar. Bijgewerkt naar 2026-05-15. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
62 lines
2.1 KiB
TypeScript
62 lines
2.1 KiB
TypeScript
import { cookies } from 'next/headers'
|
|
import { getIronSession } from 'iron-session'
|
|
|
|
import { SessionData, sessionOptions } from '@/lib/session'
|
|
import { prisma } from '@/lib/prisma'
|
|
import { productAccessFilter } from '@/lib/product-access'
|
|
import { ideaToDto } from '@/lib/idea-dto'
|
|
import { IdeaList } from '@/components/ideas/idea-list'
|
|
|
|
export const dynamic = 'force-dynamic'
|
|
|
|
export default async function IdeasPage() {
|
|
const session = await getIronSession<SessionData>(await cookies(), sessionOptions)
|
|
|
|
// M12: idee is strikt user_id-only (geen productAccessFilter — Q8).
|
|
const ideas = await prisma.idea.findMany({
|
|
where: { user_id: session.userId, archived: false },
|
|
orderBy: { created_at: 'desc' },
|
|
include: {
|
|
product: { select: { id: true, name: true, repo_url: true } },
|
|
secondary_products: { include: { product: { select: { id: true, name: true } } } },
|
|
},
|
|
take: 200,
|
|
})
|
|
|
|
// Productenlijst voor de filter-dropdown + voor "Nieuw idee"-form.
|
|
// Producten zijn product-scoped (kan team-shared zijn) — productAccessFilter
|
|
// is hier dus wél juist.
|
|
const products = await prisma.product.findMany({
|
|
where: { ...productAccessFilter(session.userId), archived: false },
|
|
orderBy: { name: 'asc' },
|
|
select: { id: true, name: true, repo_url: true },
|
|
})
|
|
|
|
const user = await prisma.user.findUnique({
|
|
where: { id: session.userId },
|
|
select: { active_product_id: true },
|
|
})
|
|
|
|
const activeProductId =
|
|
user?.active_product_id && products.some((p) => p.id === user.active_product_id)
|
|
? user.active_product_id
|
|
: null
|
|
|
|
return (
|
|
<div className="p-6 max-w-5xl mx-auto w-full">
|
|
<header className="mb-6 flex items-baseline justify-between">
|
|
<h1 className="text-xl font-medium text-foreground">Ideeën</h1>
|
|
<p className="text-sm text-muted-foreground">
|
|
Lichtgewicht voorstellen die je via Grill Me en Make Plan tot een PBI laat groeien.
|
|
</p>
|
|
</header>
|
|
|
|
<IdeaList
|
|
ideas={ideas.map((i) => ideaToDto(i))}
|
|
products={products}
|
|
isDemo={session.isDemo ?? false}
|
|
activeProductId={activeProductId}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|