Re-introduce the 3 unique files from closed PRs #37 and #40 that overlap-merged with already-landed sub-PRs (#34, #35, #36, #38, #39): - app/(app)/insights/page.tsx — Server Component dat alle helpers parallel aanroept en de 5 sectie-Cards rendert (Sprint Health, Plan-quality, Agent throughput, Velocity, Backlog health) - app/(app)/insights/components/sprint-info-strip.tsx — chips per active sprint met productname + goal + dagen-over + taakcount - app/(app)/insights/components/alignment-trend.tsx — Recharts LineChart die % ALIGNED jobs per sprint over laatste 5 sprints toont - lib/insights/verify-stats.ts — TrendPoint type + getAlignmentTrend helper (uitgebreid van PR #38) Plus dependency: recharts (was in package.json van #37/#40 die we sloten). Tests: 290/290 groen, tsc clean, lint clean. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
45 lines
1.3 KiB
TypeScript
45 lines
1.3 KiB
TypeScript
'use client'
|
|
|
|
interface SprintInfo {
|
|
sprintId: string
|
|
productName: string
|
|
sprintGoal: string
|
|
taskCount: number
|
|
daysLeft: number
|
|
}
|
|
|
|
interface Props {
|
|
sprints: SprintInfo[]
|
|
}
|
|
|
|
function daysLeftColor(daysLeft: number): string {
|
|
if (daysLeft >= 3) return 'text-[color:var(--status-done)]'
|
|
if (daysLeft >= 1) return 'text-[color:var(--priority-medium)]'
|
|
return 'text-[color:var(--priority-critical)]'
|
|
}
|
|
|
|
function truncate(text: string, max: number): string {
|
|
return text.length > max ? text.slice(0, max) + '…' : text
|
|
}
|
|
|
|
export function SprintInfoStrip({ sprints }: Props) {
|
|
if (sprints.length === 0) return null
|
|
|
|
return (
|
|
<div className="flex flex-wrap gap-2">
|
|
{sprints.map(s => (
|
|
<div
|
|
key={s.sprintId}
|
|
className="flex items-center gap-3 rounded-lg border border-border bg-surface-container px-3 py-2 text-sm"
|
|
>
|
|
<span className="font-medium text-foreground">{s.productName}</span>
|
|
<span className="text-muted-foreground">{truncate(s.sprintGoal, 60)}</span>
|
|
<span className={`font-mono tabular-nums ${daysLeftColor(s.daysLeft)}`}>
|
|
{s.daysLeft > 0 ? `${s.daysLeft}d over` : `${Math.abs(s.daysLeft)}d over tijd`}
|
|
</span>
|
|
<span className="text-muted-foreground">{s.taskCount} tasks</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)
|
|
}
|