diff --git a/app/(app)/insights/components/token-usage.tsx b/app/(app)/insights/components/token-usage.tsx new file mode 100644 index 0000000..4bb4f90 --- /dev/null +++ b/app/(app)/insights/components/token-usage.tsx @@ -0,0 +1,109 @@ +'use client' + +import { useState, useMemo } from 'react' +import type { TokenKpi, TokenJobRow } from '@/lib/insights/token-stats' + +export interface TokenUsageCardProps { + kpi: TokenKpi + jobs: TokenJobRow[] +} + +type SortKey = 'cost' | 'duration' + +function fmt(n: number | null, decimals = 0): string { + if (n === null) return '—' + return n.toLocaleString(undefined, { minimumFractionDigits: decimals, maximumFractionDigits: decimals }) +} + +function fmtCost(n: number | null): string { + if (n === null) return '—' + return '$' + n.toFixed(4) +} + +function jobLabel(job: TokenJobRow): string { + const label = job.taskTitle ?? job.ideaCode ?? job.jobId + return label.length > 40 ? label.slice(0, 37) + '…' : label +} + +export function TokenUsageCard({ kpi, jobs }: TokenUsageCardProps) { + const [sortKey, setSortKey] = useState('cost') + + const sorted = useMemo(() => { + return [...jobs].sort((a, b) => { + if (sortKey === 'cost') return (b.costUsd ?? -Infinity) - (a.costUsd ?? -Infinity) + return (b.durationSeconds ?? -Infinity) - (a.durationSeconds ?? -Infinity) + }) + }, [jobs, sortKey]) + + if (kpi.jobCount === 0) { + return

Geen token-data

+ } + + return ( +
+ {/* KPI strip */} +
+
+
+ {kpi.totalTokens.toLocaleString()} +
+
Totaal tokens
+
+
+
+ ${kpi.totalCostUsd.toFixed(4)} +
+
Kosten (USD)
+
+
+
+ {kpi.avgCostPerJob ? '$' + kpi.avgCostPerJob.toFixed(4) : '—'} +
+
Gem. per job
+
+
+ + {/* Sortable table */} +
+ + + + + + + + + + + + + + + {sorted.map(job => ( + + + + + + + + + + + ))} + +
TaakModelInputOutputCache-RCache-W setSortKey('cost')} + > + Kosten (USD) {sortKey === 'cost' ? '▾' : ''} + setSortKey('duration')} + > + Duur (s) {sortKey === 'duration' ? '▾' : ''} +
{jobLabel(job)}{job.modelId ?? '—'}{fmt(job.inputTokens)}{fmt(job.outputTokens)}{fmt(job.cacheReadTokens)}{fmt(job.cacheWriteTokens)}{fmtCost(job.costUsd)}{fmt(job.durationSeconds, 1)}
+
+
+ ) +}