import { useMemo } from 'react'; import { useGlobalFilters, Tooltip } from '@cameleer/design-system'; import { useExecutionStats } from '../api/queries/executions'; import type { Scope } from '../hooks/useScope'; import { formatPercent } from '../utils/format-utils'; import styles from './TabKpis.module.css'; interface TabKpisProps { scope: Scope; } function formatNum(n: number): string { if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`; if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`; return n.toLocaleString(); } function formatMs(ms: number): string { if (ms >= 60_000) return `${(ms / 1000).toFixed(0)}s`; if (ms >= 1000) return `${(ms / 1000).toFixed(1)}s`; return `${Math.round(ms)}ms`; } type Trend = 'up' | 'down' | 'flat'; function trend(current: number, previous: number): Trend { if (current > previous) return 'up'; if (current < previous) return 'down'; return 'flat'; } function trendArrow(t: Trend): string { switch (t) { case 'up': return '\u2191'; case 'down': return '\u2193'; case 'flat': return '\u2192'; } } function changePercent(current: number, previous: number): string | null { if (previous === 0 && current === 0) return null; if (previous === 0) return '+\u221e%'; const pct = ((current - previous) / previous) * 100; const sign = pct > 0 ? '+' : ''; return `${sign}${pct.toFixed(1)}%`; } /* ── Time period labels ───────────────────────────── */ function shortTime(d: Date): string { const h = String(d.getHours()).padStart(2, '0'); const m = String(d.getMinutes()).padStart(2, '0'); return `${h}:${m}`; } function shortDate(d: Date): string { const months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; return `${months[d.getMonth()]} ${d.getDate()}`; } function formatRange(start: Date, end: Date): string { const sameDay = start.toDateString() === end.toDateString(); if (sameDay) return `${shortDate(start)} ${shortTime(start)}\u2013${shortTime(end)}`; return `${shortDate(start)} ${shortTime(start)} \u2013 ${shortDate(end)} ${shortTime(end)}`; } function computePreviousPeriod(start: Date, end: Date): { label: string; prevLabel: string } { const durationMs = end.getTime() - start.getTime(); const prevStart = new Date(start.getTime() - durationMs); const prevEnd = new Date(start.getTime()); return { label: formatRange(start, end), prevLabel: formatRange(prevStart, prevEnd), }; } /* ── Metric model ─────────────────────────────────── */ interface Metric { label: string; fullLabel: string; value: string; prevValue: string; change: string | null; trend: Trend; upIsBad?: boolean; } /* ── Tooltip ──────────────────────────────────────── */ function MetricTooltip({ m, currentLabel, prevLabel }: { m: Metric; currentLabel: string; prevLabel: string }) { const trendClass = m.trend === 'flat' ? styles.flat : (m.trend === 'up') === m.upIsBad ? styles.bad : styles.good; return (