From de70ca5de12e2aab436cba5d16228d408d501f43 Mon Sep 17 00:00:00 2001 From: Madhura68 Date: Sun, 10 May 2026 12:53:04 +0200 Subject: [PATCH] feat(PBI-78): wire CostAnalysisCard onto insights page (T-904) - Parse ?period= from searchParams (default 30d, validates against 7d/30d/90d/mtd) - Parallel-fetch 5 cost queries via Promise.all alongside existing widgets - New "Cost analyse" section between Sprint Health and Plan-quality - Existing TokenUsageCard ("Token gebruik" section) stays as sprint detail verify (lint+typecheck+test) and build pass. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/(app)/insights/page.tsx | 43 +++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/app/(app)/insights/page.tsx b/app/(app)/insights/page.tsx index 9b90641..802258c 100644 --- a/app/(app)/insights/page.tsx +++ b/app/(app)/insights/page.tsx @@ -8,6 +8,14 @@ import { getSprintStatusBreakdown } from '@/lib/insights/sprint-status' import { getVerifyResultStats, getAlignmentTrend } from '@/lib/insights/verify-stats' import { getJobsPerDay } from '@/lib/insights/agent-throughput' import { getTokenStats } from '@/lib/insights/token-stats' +import { + getCostKpi, + getCostByDay, + getCostByModel, + getCostByKind, + getCacheEfficiency, + type Period, +} from '@/lib/insights/cost-analysis' import { getVelocity } from '@/lib/insights/velocity' import { getBacklogHealth } from '@/lib/insights/backlog-health' import { SprintInfoStrip } from './components/sprint-info-strip' @@ -16,6 +24,7 @@ import { SprintStatusDonut } from './components/sprint-status-donut' import { PlanQualityCard } from './components/plan-quality' import { AlignmentTrend } from './components/alignment-trend' import { AgentThroughputCard } from './components/agent-throughput' +import { CostAnalysisCard } from './components/cost-analysis' import { TokenUsageCard } from './components/token-usage' import { VelocityChart } from './components/velocity-chart' import { BacklogHealthCard } from './components/backlog-health' @@ -24,7 +33,13 @@ const DAY_MS = 86_400_000 const ASSUMED_SPRINT_DAYS = 14 interface InsightsPageProps { - searchParams: Promise<{ product?: string }> + searchParams: Promise<{ product?: string; period?: string }> +} + +const VALID_PERIODS = ['7d', '30d', '90d', 'mtd'] as const + +function parsePeriod(raw: string | undefined): Period { + return (VALID_PERIODS as readonly string[]).includes(raw ?? '') ? (raw as Period) : '30d' } function MissingDatesNotice({ productId, productName }: { productId: string; productName: string }) { @@ -41,7 +56,8 @@ function MissingDatesNotice({ productId, productName }: { productId: string; pro export default async function InsightsPage({ searchParams }: InsightsPageProps) { const session = await getIronSession(await cookies(), sessionOptions) const userId = session.userId! - const { product: filterProductId } = await searchParams + const { product: filterProductId, period: rawPeriod } = await searchParams + const period = parsePeriod(rawPeriod) const [ burndownSprints, @@ -53,6 +69,11 @@ export default async function InsightsPage({ searchParams }: InsightsPageProps) jobsPerDay, velocity, backlogHealth, + costKpi, + costByDay, + costByModel, + costByKind, + cacheEff, ] = await Promise.all([ getBurndownData(userId), getSprintStatusBreakdown(userId), @@ -77,6 +98,11 @@ export default async function InsightsPage({ searchParams }: InsightsPageProps) getJobsPerDay(userId, 14, filterProductId), getVelocity(userId, 5), getBacklogHealth(userId), + getCostKpi(userId, period), + getCostByDay(userId, period), + getCostByModel(userId, period), + getCostByKind(userId, period), + getCacheEfficiency(userId, period), ]) const activeSprintId = activeSprints.find(s => s.product.id === filterProductId)?.id ?? '' @@ -134,6 +160,19 @@ export default async function InsightsPage({ searchParams }: InsightsPageProps) )} + {/* Cost analyse */} +
+

Cost analyse

+ +
+ {/* Plan-quality */}

Plan-quality