From 678069a3d8cf0b0c5fb0f23f0d8be6e4f91e68c6 Mon Sep 17 00:00:00 2001 From: Madhura68 Date: Wed, 6 May 2026 00:21:59 +0200 Subject: [PATCH] feat(T-563): integreer Sync-tab in IdeaDetailLayout + page-loader - 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) --- app/(app)/ideas/[id]/page.tsx | 10 ++++++++++ components/ideas/idea-detail-layout.tsx | 15 +++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/app/(app)/ideas/[id]/page.tsx b/app/(app)/ideas/[id]/page.tsx index 0c1cfb0..10439f2 100644 --- a/app/(app)/ideas/[id]/page.tsx +++ b/app/(app)/ideas/[id]/page.tsx @@ -7,6 +7,7 @@ import { prisma } from '@/lib/prisma' import { productAccessFilter } from '@/lib/product-access' import { ideaToDto } from '@/lib/idea-dto' import { IdeaDetailLayout } from '@/components/ideas/idea-detail-layout' +import { loadIdeaSyncData } from './sync-tab-server' export const dynamic = 'force-dynamic' @@ -76,6 +77,14 @@ export default async function IdeaDetailPage({ params, searchParams }: PageProps select: { id: true, question: true, answer: true, status: true, created_at: true }, }) + // Sync-tab data — alleen geladen als idea PLANNED is en pbi_id gevuld. + // loadIdeaSyncData past zelf user_id-scope toe en retourneert null als + // het idee geen pbi heeft. + const syncData = + idea.status === 'PLANNED' && idea.pbi_id + ? await loadIdeaSyncData(id, session.userId) + : null + return ( ) } diff --git a/components/ideas/idea-detail-layout.tsx b/components/ideas/idea-detail-layout.tsx index f6798d0..4b962d9 100644 --- a/components/ideas/idea-detail-layout.tsx +++ b/components/ideas/idea-detail-layout.tsx @@ -25,7 +25,9 @@ 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 { IdeaSyncTab } from '@/components/ideas/idea-sync-tab' import { DownloadMdButton } from '@/components/ideas/download-md-button' +import type { IdeaSyncData } from '@/app/(app)/ideas/[id]/sync-tab-server' const API_TO_DB: Record[0]> = { draft: 'DRAFT', @@ -38,7 +40,7 @@ const API_TO_DB: Record[0]> planned: 'PLANNED', } -type TabKey = 'idee' | 'grill' | 'plan' | 'timeline' +type TabKey = 'idee' | 'grill' | 'plan' | 'timeline' | 'sync' interface IdeaLog { id: string @@ -82,6 +84,7 @@ interface Props { userQuestions: IdeaUserQuestionDto[] isDemo: boolean initialTab: string + syncData: IdeaSyncData | null } export function IdeaDetailLayout({ @@ -94,12 +97,16 @@ export function IdeaDetailLayout({ userQuestions, isDemo, initialTab, + syncData, }: Props) { const router = useRouter() const searchParams = useSearchParams() const [pending, startTransition] = useTransition() - const TAB_KEYS: TabKey[] = ['idee', 'grill', 'plan', 'timeline'] + const showSync = syncData !== null && idea.status === 'planned' + const TAB_KEYS: TabKey[] = showSync + ? ['idee', 'grill', 'plan', 'timeline', 'sync'] + : ['idee', 'grill', 'plan', 'timeline'] const tab = (TAB_KEYS.includes(initialTab as TabKey) ? initialTab : 'idee') as TabKey function setTab(key: TabKey) { @@ -170,6 +177,9 @@ export function IdeaDetailLayout({ { key: 'grill' as TabKey, label: 'Grill', disabled: !grill_md, hasContent: !!grill_md }, { key: 'plan' as TabKey, label: 'Plan', disabled: !plan_md, hasContent: !!plan_md }, { key: 'timeline' as TabKey, label: 'Timeline', disabled: false, hasContent: true }, + ...(showSync + ? [{ key: 'sync' as TabKey, label: 'Sync', disabled: false, hasContent: true }] + : []), ] as const).map((t) => (