* feat(ST-cmovhveef): add PLANNED to GRILL_TRIGGERABLE_FROM and PLANNED→GRILLING transition
- GRILL_TRIGGERABLE_FROM now includes 'PLANNED' in actions/ideas.ts
- ALLOWED_TRANSITIONS PLANNED entry extended with 'GRILLING' in lib/idea-status.ts
- Updated canTransition test to reflect the new re-grill-from-PLANNED behavior
* test(ST-cmovhvef3): add exhaustive re-grill canTransition test covering PLANNED
Adds a loop test that asserts canTransition(status, 'GRILLING') for all
statuses in GRILL_TRIGGERABLE_FROM that support the transition, explicitly
documenting PLANNED as a valid re-grill entry point.
* feat(ST-cmovhvegf): add existingPbi pre-check in materializeIdeaPlanAction
- Adds options.allowAlongside parameter to control behaviour when a PBI
with executed tasks already exists.
- Returns 409 PBI_HAS_ACTIVE_TASKS:<code> when tasks are DONE/IN_PROGRESS
and allowAlongside is not set.
- Auto-deletes the old PBI inside the transaction when no tasks have been
executed (atomic replace).
- Alongside mode (allowAlongside=true) skips deletion and creates a new PBI.
* test(ST-cmovhveh3): add pre-check integration tests for materializeIdeaPlanAction
Three new scenarios in ideas-crud.test.ts:
- auto-vervang: old PBI deleted in transaction when no executed tasks
- conflict-409: returns PBI_HAS_ACTIVE_TASKS:<code> with active tasks
- alongside: skips delete and creates new PBI when allowAlongside=true
Also adds task.count, pbi.findUnique, pbi.delete to prisma mock.
* feat(ST-cmovhveih): remove PLANNED-blokkering in idea-row-actions, add inline Bekijk-PBI button
- Removed grillBlockedReason guard for status==='planned', enabling re-grill from PLANNED
- Removed the early return for PLANNED that hid all standard buttons
- Added conditional 'Bekijk <code>' button at the start of the standard button set,
visible only when status==='planned' and PBI + product_id are present
* feat(ST-cmovhvej7): add PBI_HAS_ACTIVE_TASKS alongside-dialoog in materialize handler
When materializeIdeaPlanAction returns code 409 with PBI_HAS_ACTIVE_TASKS:<code>,
a confirm dialog offers the user a choice: create new PBI alongside the existing one
or cancel. Alongside=true retries the action; cancel leaves the idea in PLAN_READY.
- Voeg showQuick/quickTitle/quickDescription state toe
- Voeg handleQuickCreate toe die createIdeaAction met product_id=null aanroept
- Voeg Snel idee-knop (variant=outline) toe naast Nieuw idee in de top-bar
- Voeg inline snel-form toe zonder product-dropdown, met Enter-to-submit
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ideas): multi-select secundaire producten + badges in IdeaDetailLayout
Voegt checkbox-lijst toe voor extra producten (exclusief primaire) in
de Idee-tab, geïntegreerd in bestaande save/reset flow via
updateSecondaryProductsAction. Toont secundaire product-badges in de
detail-header. Bevat ook schema/dto/action-dependencies (IdeaProduct
junction, secondary_products in IdeaDto).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ideas): lijst-filter matcht op primair én secundaire producten
Breidt productFilter-logica in IdeaList uit: naast product_id
wordt ook idea.secondary_products gecheckt, zodat ideeën zichtbaar
blijven bij filteren op een secundair gekoppeld product.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- TabKey union uitgebreid met 'sync'.
- Sync-tab alleen zichtbaar als syncData !== null && idea.status === 'planned'
(M12 keuze 6: na Materialiseer-actie).
- page.tsx roept loadIdeaSyncData alleen aan bij PLANNED + pbi_id, anders
null doorgeven aan layout.
- showSync-flag bepaalt of de tab in TAB_KEYS array zit en in de UI
gerenderd wordt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Toont per Story onder de gekoppelde PBI: status-badge, taak-rij met
job-status (incl. SKIPPED), branch, pushed_at, pr_url, en bestaande
<StoryLog>-component voor activity-log. PBI-header met PR-link en
gemerged-badge.
Realtime: subscribed op /api/realtime/notifications. Bij story_log-
event waar story_id matcht, of claude_job_status voor dit idea →
router.refresh() (server-render levert nieuwe data).
MD3-tokens overal: bg-status-todo/in-progress/done, bg-surface-
container, bg-muted/60. Geen bg-blue-500.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Nav-label 'Ideeën' hernoemd naar 'Ideas'; breadcrumb idem
- Grill/Plan tabs disabled (grijs, cursor-not-allowed) zolang er geen
content is; groene stip zodra grill_md resp. plan_md beschikbaar is
- SSE hook roept router.refresh() aan bij job done/failed zodat de
Timeline automatisch de nieuwe GRILL_RESULT/PLAN_RESULT logs toont
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three places linked to \`/products/[id]/backlog#pbi-{pbi_code}\` after
materializing or in the planned-state link-card. That route doesn't
exist (product backlog lives at \`/products/[id]\` directly), and the
hash was double-prefixed (\`#pbi-PBI-32\`) since pbi_code already starts
with PBI-. Result: 404 for the user.
Fix: route to \`/products/[id]\` without anchor. The new PBI is the most
recent so visible near the top. Per-PBI anchor scrolling is a follow-up
once we add \`id="pbi-{id}"\` attributes to pbi-list rows.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
\`new Date(...).toLocaleString()\` zonder expliciete locale gebruikt de
default-locale van runtime: server (Node) levert nl-NL formaat
(\`05/05/2026, 13:21:51\`), browser CSR levert en-US (\`5/5/2026, 1:21:51 PM\`).
React detecteert dat als hydration-mismatch en regenereert de tree.
Fix: pass \`'nl-NL'\` met expliciete date/time-style. Server en client
produceren nu identieke output.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two gaps discovered during the first live grill-session of IDEA-002:
the agent posted a question, but the user had no UI to answer it.
1. Idea-questions only appeared on the Timeline-tab as read-only entries
2. Notifications-bell fetched + handled story-questions only
This fix:
**Inline answer-form in IdeaTimeline** (components/ideas/idea-timeline.tsx)
- Open questions now render an AnswerForm directly under the question text
- Multi-choice options become clickable buttons (one-click submit); free-text
fallback via collapsed details/textarea
- Plain free-text questions render textarea + Verzend
- Calls existing answerQuestion server-action; toast + router.refresh on success
**Notifications-bell extended for idea-questions**
- stores/notifications-store.ts: NotificationQuestion → discriminated union
(kind: 'story' | 'idea'); forYouCount treats idea-questions as always-for-you
(idea is strictly user_id-only — only the owner sees them)
- components/notifications/notifications-bridge.tsx: parallel fetch of
story-questions (productAccessFilter) + idea-questions (idea.user_id ===
session.userId); merged + sorted by created_at
- components/notifications/notifications-sheet.tsx: renders idea_code/title
for kind='idea'
- components/notifications/answer-modal.tsx: header + open-link branch on
kind (idea → /ideas/[id]?tab=timeline; story → existing /sprint link)
- lib/realtime/use-notifications-realtime.ts: idea-question events also
trigger close+reconnect on 'open' (loads fresh detail) and remove(id) on
non-open — same pattern story-questions already use
- components/shared/notifications-bell.tsx: badge counts idea-questions as
for-you regardless of assignee
**Security gap closed (actions/questions.ts answerQuestion)**
Before: accepted any answer if user has product-access.
After: idea-questions require idea.user_id === session.userId; story-
questions keep the existing productAccessFilter path. (Prisma 7 rejects
\`{ not: null }\` in WHERE; routing happens app-level after a single fetch.)
Tests: 546/546 still green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
components/ideas/idea-md-editor.tsx:
- Textarea-based editor with monospace styling for grill_md / plan_md
- kind='plan': live yaml-frontmatter validation as derived state via
useMemo (no setState-in-effect); inline errors with line numbers
- kind='grill': free markdown, no validation
- localStorage draft per (ideaId, kind) — lazy initial-value seeded on
mount; toast notice if drift from server
- Cmd/Ctrl+S keyboard shortcut to save
- Server-action 422 details surface as separate submitErrors state
components/ideas/idea-detail-layout.tsx:
- Grill/Plan tabs flip into edit-mode via "Bewerk" button when:
- grill: status in [GRILLED, PLAN_READY] (M12 grill-keuze 12)
- plan: status === PLAN_READY
- Empty-state offers "Schrijf zelf" when md is null + editable
- Demo always read-only
Tests: 546/546 still green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
components/ideas/idea-row-actions.tsx — replaces T-507 placeholder:
- Grill Me: disabled in GRILLING/PLANNING/PLANNED, requires
product-with-repo + connectedWorkers > 0; tooltip shows specific reason
("Grill loopt al", "Idee heeft een product met repo nodig", "Geen
Claude-worker actief")
- Make Plan: enabled only in GRILLED/PLAN_FAILED/PLAN_READY; same
prerequisites as Grill
- Materialiseer: enabled only in PLAN_READY (no worker needed — synchrone
server-side parser); confirm-dialog before action; navigates to product
backlog PBI anchor on success
- *_FAILED: dedicated "Probeer opnieuw" rotate-icon button
- PLANNED: replaces all three with "Bekijk {PBI-code}" link + open-detail
- Demo: every mutating button wrapped in DemoTooltip with disabled state
- connectedWorkers read directly via useSoloStore (per M12 grill-keuze 16)
Tests: 546/546 still green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
app/(app)/ideas/page.tsx (server-component):
- user_id-only fetch (no productAccessFilter — Idee is privé)
- products fetched with productAccessFilter for filter-dropdown + create-form
components/ideas/idea-list.tsx (client-component):
- Search by title, product-dropdown filter, status multi-chip filter
- Inline create form with title/description/product (optional)
- Native shadcn Table + status badge via getIdeaStatusBadge (T-509)
- Row click navigates to /ideas/[id]
- Sonner toasts for success/error; router.refresh() after mutations
- DemoTooltip + disabled on Nieuw + Archive
- Empty-state + filtered-empty messaging
components/ideas/idea-row-actions.tsx (placeholder for T-508):
- "Open" navigation + "Archive" button only — Grill / Make Plan /
Materialiseer come in T-508 with full disabled-rules
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>