codeburn/app/renderer/sections/Overview.tsx
iamtoruk cbddd3a9d7 fix(app): accuracy batch A — provider scoping, honest chart axes, range threading, stale-error banner
- Provider filter: Models-this-period sources current.topModels (history
  days carry empty topModels per provider); StackedBars falls back to a
  cost-proportional single segment like the Swift menubar
- Chart windows align to the CLI (week -7, 30days -30, all = 6 months)
  and zero-fill a contiguous calendar window; history.daily is sparse and
  both sides key local YYYY-MM-DD, so lookups are safe (verified on real
  CLI output)
- Custom range threads into Overview panels (chart, models table) and
  suppresses MTD/projected and vs-last-week comparisons; Compare states
  that custom dates fall back to the period
- usePolled clears errors per attempt and sections show a StaleBanner
  instead of silently rendering last-good data after a failed refresh
- Cache-hit denominator matches the CLI; hero savings relabeled 'Saved by
  applied fixes' + new 'Saved via local models' line; formatCompact
  everywhere; Optimize 'Fixes' count matches the rendered list

App suite 163/163 (was 147), root 1613/1613.
2026-07-16 01:14:41 -07:00

587 lines
26 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
import { createPortal } from 'react-dom'
import { CliErrorPanel } from '../components/CliErrorPanel'
import { ActivityHeatmap } from '../components/ActivityHeatmap'
import { ListRow } from '../components/ListRow'
import { Panel } from '../components/Panel'
import { StaleBanner } from '../components/StaleBanner'
import { type Polled, usePolled } from '../hooks/usePolled'
import { formatCompact, formatUsd } from '../lib/format'
import { codeburn } from '../lib/ipc'
import { contiguousDailyWindow, formatChartDate, localDateKey, sliceDailyToPeriod, sliceDailyToRange } from '../lib/period'
import type {
ActReportJson,
DailyHistoryEntry,
DateRange,
MenubarPayload,
Period,
YieldJsonReport,
} from '../lib/types'
export { localDateKey } from '../lib/period'
function median(values: number[]): number {
if (!values.length) return 0
const sorted = [...values].sort((a, b) => a - b)
const mid = Math.floor(sorted.length / 2)
return sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2
}
function mean(values: number[]): number {
return values.length ? values.reduce((sum, value) => sum + value, 0) / values.length : 0
}
function clamp(value: number, min: number, max: number): number {
return Math.min(max, Math.max(min, value))
}
type EfficiencyGrade = 'A+' | 'A' | 'B' | 'C' | 'D' | 'F'
function efficiencyGrade(score: number): EfficiencyGrade {
if (score >= 93) return 'A+'
if (score >= 85) return 'A'
if (score >= 75) return 'B'
if (score >= 65) return 'C'
if (score >= 55) return 'D'
return 'F'
}
function EfficiencyScorecard({ current, bare = false }: { current: MenubarPayload['current']; bare?: boolean }) {
const oneShot = current.oneShotRate ?? 0.6
const cacheFrac = clamp(current.cacheHitPercent / 100, 0, 1)
const retrySpendFraction = current.retryTax.totalUSD / Math.max(current.cost, 1e-9)
const retryPenalty = clamp(retrySpendFraction * 4, 0, 1)
// score = 100 * (0.45*oneShot + 0.30*cacheFrac + 0.25*(1-retryPenalty))
// Missing one-shot data uses the specified neutral 0.6 and is disclosed below.
const score = 100 * (0.45 * oneShot + 0.30 * cacheFrac + 0.25 * (1 - retryPenalty))
const grade = efficiencyGrade(score)
const gradeTone = grade === 'A+' || grade === 'A'
? 'grade-a'
: grade === 'D'
? 'grade-d'
: grade === 'F'
? 'grade-f'
: 'grade-bc'
return (
<div className={`${bare ? '' : 'ov-card '}ov-efficiency`}>
<div className="ov-efficiency-head">
<div><div className="ov-label">Efficiency</div><div className="ov-efficiency-score">{Math.round(score)} / 100</div></div>
<div className={`ov-grade ${gradeTone}`} aria-label={`Efficiency grade ${grade}`}>{grade}</div>
</div>
<div className="ov-component-list">
<div className="ov-component-row">
<div><span>One-shot</span><strong>{formatRate(current.oneShotRate)}</strong></div>
<div className="ov-component-track"><span style={{ width: `${oneShot * 100}%` }} /></div>
</div>
<div className="ov-component-row">
<div><span>Cache hit</span><strong>{Math.round(current.cacheHitPercent)}%</strong></div>
<div className="ov-component-track"><span style={{ width: `${cacheFrac * 100}%` }} /></div>
</div>
<div className="ov-component-row">
<div><span>Retry tax</span><strong>{formatUsd(current.retryTax.totalUSD)} · {(retrySpendFraction * 100).toFixed(1)}% of spend</strong></div>
<div className="ov-component-track adverse"><span style={{ width: `${retryPenalty * 100}%` }} /></div>
</div>
</div>
<p className="ov-widget-caption">Composite of one-shot, cache hit, and retry tax.{current.oneShotRate === null ? ' Partial grade: one-shot is unavailable.' : ''}</p>
</div>
)
}
function CostPerOutcome({ outcome }: { outcome: Polled<YieldJsonReport> }) {
const report = outcome.data
let body: React.ReactNode
if (!report) {
body = <EmptyNote>{outcome.error ? 'Yield data is unavailable for this period.' : 'Correlating sessions with git…'}</EmptyNote>
} else if (report.summary.total.sessions === 0 && report.details.length === 0) {
body = <EmptyNote>No git-correlated outcomes in this period.</EmptyNote>
} else {
const commits = report.details.reduce((sum, detail) => sum + detail.commitCount, 0)
const costPerCommit = commits > 0 ? report.summary.total.costUSD / commits : null
const productive = report.summary.productive
const costPerProductiveSession = productive.sessions > 0 ? productive.costUSD / productive.sessions : null
body = (
<>
<div className="ov-outcome-metrics">
<div><span>$ / commit</span><strong>{costPerCommit === null ? '—' : formatUsd(costPerCommit)}</strong></div>
<div><span>$ / productive session</span><strong>{costPerProductiveSession === null ? '—' : formatUsd(costPerProductiveSession)}</strong></div>
</div>
<div className="ov-outcome-split">
productive {Math.round(productive.costPercent)}% · reverted {Math.round(report.summary.reverted.costPercent)}% · abandoned {Math.round(report.summary.abandoned.costPercent)}%
</div>
</>
)
}
return (
<div className="ov-card ov-panel">
<div className="ov-panel-head"><h3>Cost per outcome</h3><span className="r">Yield</span></div>
<div className="ov-panel-body">
{body}
<p className="ov-widget-caption">Git-correlated. Reverted/abandoned = spend that didn't ship.</p>
</div>
</div>
)
}
type Anomaly = { lead: string; value: string; tail: string }
function deriveAnomalies(data: MenubarPayload, now: Date, includeWeekOverWeek = true): Anomaly[] {
const anomalies: Anomaly[] = []
const todayKey = localDateKey(now)
const today = data.history.daily.find(day => day.date === todayKey)
const sameWeekdayCosts = data.history.daily
.filter(day => {
if (day.date === todayKey) return false
const [year, month, date] = day.date.split('-').map(Number)
return new Date(year, month - 1, date).getDay() === now.getDay()
})
.map(day => day.cost)
const typicalWeekday = mean(sameWeekdayCosts)
if (today && typicalWeekday > 0 && today.cost > typicalWeekday * 1.8) {
const ratio = today.cost / typicalWeekday
const weekday = now.toLocaleString('en-US', { weekday: 'long' })
anomalies.push({ lead: "Today's spend is ", value: `${ratio.toFixed(1).replace(/\.0$/, '')}×`, tail: ` your typical ${weekday}.` })
}
if (includeWeekOverWeek && data.history.daily.length >= 14) {
const recent14 = data.history.daily.slice(-14)
const currentWeek = mean(recent14.slice(-7).map(day => day.cost))
const priorWeek = mean(recent14.slice(0, 7).map(day => day.cost))
if (priorWeek > 0) {
const change = (currentWeek - priorWeek) / priorWeek * 100
if (Math.abs(change) >= 25) {
anomalies.push({ lead: 'Spend is ', value: `${Math.round(Math.abs(change))}%`, tail: ` ${change >= 0 ? 'higher' : 'lower'} than last week.` })
}
}
}
if (data.current.cacheHitPercent < 50) {
anomalies.push({ lead: 'Cache hit is low (', value: `${Math.round(data.current.cacheHitPercent)}%`, tail: '). More of your context is uncached.' })
}
return anomalies.slice(0, 3)
}
function AnomalyCallouts({ anomalies }: { anomalies: Anomaly[] }) {
if (!anomalies.length) return null
return (
<div className="ov-card ov-anomalies" aria-label="Spend anomalies">
<span className="ov-anomaly-label">Anomalies</span>
<div className="ov-anomaly-list">
{anomalies.map((anomaly, index) => <div key={`${anomaly.value}-${index}`}>{anomaly.lead}<strong>{anomaly.value}</strong>{anomaly.tail}</div>)}
</div>
</div>
)
}
function RoutingWhatIf({ routing, onNavigate }: {
routing: MenubarPayload['current']['routingWaste']
onNavigate?: (section: 'optimize') => void
}) {
if (routing.totalSavingsUSD <= 0 || !routing.baselineModel) return null
return (
<div className="ov-card ov-routing">
<div><span className="ov-label">Routing what-if</span><p>Routing to <strong>{routing.baselineModel}</strong> could save ~<strong>{formatUsd(routing.totalSavingsUSD)}</strong> this period.</p></div>
<button className="ov-link" type="button" onClick={() => onNavigate?.('optimize')}>Optimize →</button>
</div>
)
}
function deriveStats(data: MenubarPayload, now: Date) {
const daily = data.history.daily
const todayKey = localDateKey(now)
const todayEntry = daily.find(day => day.date === todayKey)
const monthPrefix = todayKey.slice(0, 7)
const mtdEntries = daily.filter(day => day.date.startsWith(monthPrefix))
const mtd = mtdEntries.reduce((sum, day) => sum + day.cost, 0)
const medianDaily = median(daily.slice(-7).map(day => day.cost))
const daysInMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0).getDate()
const projected = mtd + medianDaily * Math.max(0, daysInMonth - now.getDate())
const prevMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1)
const prevPrefix = localDateKey(prevMonth).slice(0, 7)
const priorEntries = daily.filter(day => day.date.startsWith(prevPrefix))
const priorAverage = mean(priorEntries.map(day => day.cost))
const currentAverage = mean(mtdEntries.map(day => day.cost))
const pacePct = priorAverage > 0 ? ((currentAverage - priorAverage) / priorAverage) * 100 : null
return {
todayEntry,
todayCost: todayEntry?.cost ?? 0,
mtd,
projected,
pacePct,
prevMonthName: prevMonth.toLocaleString('en-US', { month: 'long' }),
}
}
export function sessionModelKey(project: string, date: string, calls: number, cost: number): string {
return `${project}|${date}|${calls}|${cost}`
}
function buildModelIndex(data: MenubarPayload): Map<string, string> {
const index = new Map<string, string>()
for (const project of data.current.topProjects) {
for (const session of project.sessionDetails) {
const dominant = [...session.models].sort((a, b) => b.cost - a.cost)[0]
if (dominant) index.set(sessionModelKey(project.name, session.date, session.calls, session.cost), dominant.name)
}
}
return index
}
function streakDays(daily: DailyHistoryEntry[], now: Date): number {
const byDate = new Map(daily.map(day => [day.date, day.cost]))
let streak = 0
for (let offset = 0; ; offset++) {
const date = new Date(now.getFullYear(), now.getMonth(), now.getDate() - offset)
if ((byDate.get(localDateKey(date)) ?? 0) <= 0) break
streak++
}
return streak
}
function CountUp({ value }: { value: number }) {
const ref = useRef<HTMLDivElement>(null)
useEffect(() => {
const element = ref.current
if (!element) return
let frame = 0
const start = performance.now()
const duration = 850
const step = (now: number) => {
const t = Math.min(1, (now - start) / duration)
const eased = 1 - Math.pow(1 - t, 3)
element.textContent = formatUsd(value * eased)
if (t < 1) frame = requestAnimationFrame(step)
}
frame = requestAnimationFrame(step)
return () => cancelAnimationFrame(frame)
}, [value])
return <div ref={ref} className="ov-hero-num" data-countup={value}>{formatUsd(value)}</div>
}
function formatShortDay(date: string): string {
const [, month, day] = date.split('-').map(Number)
return `${month}/${day}`
}
type AggregatedModel = {
name: string
cost: number
calls: number
// Absent in provider-filtered mode: `current.topModels` carries no per-model
// token counts, so the table shows "" rather than a misleading zero.
inputTokens?: number
outputTokens?: number
}
/** Provider-filtered source: `current.topModels` is already period/range/provider-scoped by the CLI. */
function topModelsToAggregated(models: MenubarPayload['current']['topModels']): AggregatedModel[] {
return models
.map(model => ({ name: model.name, cost: model.cost, calls: model.calls }))
.sort((a, b) => b.cost - a.cost)
}
function aggregateModels(daily: DailyHistoryEntry[]): AggregatedModel[] {
const byName = new Map<string, AggregatedModel>()
for (const day of daily) {
for (const model of day.topModels) {
const row = byName.get(model.name) ?? {
name: model.name,
cost: 0,
calls: 0,
inputTokens: 0,
outputTokens: 0,
}
row.cost += model.cost
row.calls += model.calls
row.inputTokens = (row.inputTokens ?? 0) + model.inputTokens
row.outputTokens = (row.outputTokens ?? 0) + model.outputTokens
byName.set(model.name, row)
}
}
return [...byName.values()].sort((a, b) => b.cost - a.cost)
}
function ModelsTable({ models }: { models: AggregatedModel[] }) {
if (!models.length) return <EmptyNote>No model usage in this range yet.</EmptyNote>
return (
<div className="ov-model-scroll">
<table className="ov-models" aria-label="Models this period">
<thead>
<tr>
<th>Model</th>
<th className="num">Input tok</th>
<th className="num">Output tok</th>
<th className="num">Cost</th>
<th className="num">Calls</th>
</tr>
</thead>
<tbody>
{models.map(model => (
<tr key={model.name}>
<td className="ov-model-name">{model.name}</td>
<td className="num mono">{model.inputTokens === undefined ? '—' : formatCompact(model.inputTokens)}</td>
<td className="num mono">{model.outputTokens === undefined ? '—' : formatCompact(model.outputTokens)}</td>
<td className="num mono">{formatUsd(model.cost)}</td>
<td className="num">{model.calls.toLocaleString('en-US')}</td>
</tr>
))}
</tbody>
</table>
</div>
)
}
function DailyChart({ daily }: { daily: DailyHistoryEntry[] }) {
const max = Math.max(...daily.map(day => day.cost), 0)
const peakIndex = daily.reduce((peak, day, index) => day.cost > (daily[peak]?.cost ?? -1) ? index : peak, 0)
const peak = daily[peakIndex]
const yesterday = daily.at(-2)
const average = mean(daily.map(day => day.cost))
const ticks = daily.filter((_, index) => index % 7 === 0)
const [tip, setTip] = useState<{ day: DailyHistoryEntry; x: number; y: number } | null>(null)
const [tipPosition, setTipPosition] = useState<{ left: number; top: number } | null>(null)
const tipRef = useRef<HTMLDivElement>(null)
useLayoutEffect(() => {
if (!tip) {
setTipPosition(null)
return
}
const width = tipRef.current?.offsetWidth ?? 220
const height = tipRef.current?.offsetHeight ?? 62
const gutter = 8
const cursorGap = 12
let left = tip.x + cursorGap
if (left + width > window.innerWidth - gutter) left = tip.x - width - cursorGap
left = Math.max(gutter, Math.min(left, window.innerWidth - width - gutter))
let top = tip.y - height - cursorGap
if (top < gutter) top = tip.y + cursorGap
top = Math.max(gutter, Math.min(top, window.innerHeight - height - gutter))
setTipPosition({ left, top })
}, [tip])
return (
<>
<div className="chart">
{daily.map((day, index) => (
<button
type="button"
aria-label={`${day.date}: ${formatUsd(day.cost)}`}
className={`col${index === peakIndex ? ' hi' : ''}`}
key={day.date}
style={{ height: `${max > 0 ? Math.max(2, day.cost / max * 100) : 2}%` }}
data-date={day.date}
data-cost={day.cost}
data-calls={day.calls}
data-led={day.topModels[0]?.name ?? ''}
onMouseEnter={event => setTip({ day, x: event.clientX, y: event.clientY })}
onMouseMove={event => setTip({ day, x: event.clientX, y: event.clientY })}
onMouseLeave={() => setTip(null)}
/>
))}
</div>
<div className="ov-xax">
{ticks.map(day => {
const index = daily.indexOf(day)
return <span key={day.date} style={{ left: `${daily.length > 1 ? index / (daily.length - 1) * 100 : 0}%` }}>{formatChartDate(day.date)}</span>
})}
</div>
<div className="ov-chart-summaries" aria-label="Daily spend summary">
<div className="ov-summary-chip"><span>Avg/day</span><strong>{formatUsd(average)}</strong></div>
<div className="ov-summary-chip"><span>Peak</span><strong>{peak ? `${formatUsd(peak.cost)} · ${formatShortDay(peak.date)}` : '$0.00'}</strong></div>
<div className="ov-summary-chip"><span>Yesterday</span><strong>{formatUsd(yesterday?.cost ?? 0)}</strong></div>
</div>
{tip && createPortal(
<div
ref={tipRef}
className={`chart-tip${tipPosition ? ' on' : ''}`}
style={{ position: 'fixed', ...(tipPosition ?? { left: 0, top: 0 }) }}
role="tooltip"
>
<div className="chart-tip-d">{formatChartDate(tip.day.date)}</div>
<div className="chart-tip-v">{formatUsd(tip.day.cost)}</div>
<div className="chart-tip-s">{tip.day.calls} calls · {tip.day.topModels[0]?.name ?? 'No model'} led</div>
</div>,
document.body,
)}
</>
)
}
function EmptyNote({ children }: { children: React.ReactNode }) {
return <p style={{ color: 'var(--t3)', margin: 0, fontSize: 12 }}>{children}</p>
}
function formatRate(rate: number | null): string {
return rate === null ? '—' : `${Math.round(rate * 100)}%`
}
function TopActivities({ activities }: { activities: MenubarPayload['current']['topActivities'] }) {
const rows = [...activities].sort((a, b) => b.cost - a.cost).slice(0, 6)
if (!rows.length) return <EmptyNote>No activity in this range yet.</EmptyNote>
const maxCost = rows[0].cost
return (
<div className="ov-activities">
{rows.map(activity => (
<div className="ov-activity" key={activity.name}>
<div className="ov-activity-bar" aria-hidden="true">
<span style={{ width: `${maxCost > 0 ? activity.cost / maxCost * 100 : 0}%` }} />
</div>
<div className="ov-activity-main">
<span className="ov-activity-name">{activity.name}</span>
<strong>{formatUsd(activity.cost)}</strong>
</div>
<div className="ov-activity-meta">
<span>{activity.turns.toLocaleString('en-US')} turns</span>
<span>{formatRate(activity.oneShotRate)} one-shot</span>
</div>
</div>
))}
</div>
)
}
export function Overview({ period, provider }: { period: Period; provider: string }) {
const overview = usePolled<MenubarPayload>(() => codeburn.getOverview(period, provider), [period, provider])
return <OverviewContent period={period} provider={provider} overview={overview} />
}
export function OverviewContent({
period,
provider = 'all',
range = null,
overview,
onNavigate,
}: {
period: Period
provider?: string
range?: DateRange | null
overview: Polled<MenubarPayload>
onNavigate?: (section: 'optimize') => void
}) {
const actReport = usePolled<ActReportJson>(() => codeburn.getActReport(), [])
const yieldReport = usePolled<YieldJsonReport>(() => codeburn.getYield(period), [period])
const { data, error } = overview
const modelIndex = useMemo(() => data ? buildModelIndex(data) : new Map<string, string>(), [data])
if (!data) {
if (error) return <CliErrorPanel error={error} subject="your usage" />
return <Panel title="Overview"><EmptyNote>Scanning sessions…</EmptyNote></Panel>
}
const now = new Date()
const rangeActive = !!range
const stats = deriveStats(data, now)
const periodDaily = sliceDailyToPeriod(data.history.daily, period, now)
// Daily chart: contiguous zero-filled calendar window. A custom range spans
// [from..to]; otherwise the trend covers at least the last 30 days, extended
// back to the earliest active day already in the period window.
const defaultChartStart = localDateKey(new Date(now.getFullYear(), now.getMonth(), now.getDate() - 29))
const chartDaily = rangeActive
? contiguousDailyWindow(data.history.daily, range.from, range.to)
: contiguousDailyWindow(
data.history.daily,
periodDaily[0] && periodDaily[0].date < defaultChartStart ? periodDaily[0].date : defaultChartStart,
localDateKey(now),
)
// Provider-filtered history.daily has empty topModels, so source the models
// table from current.topModels (already period/range/provider-scoped) instead.
const models = provider !== 'all'
? topModelsToAggregated(data.current.topModels)
: aggregateModels(rangeActive ? sliceDailyToRange(data.history.daily, range.from, range.to) : periodDaily)
const recent14 = data.history.daily.slice(-14)
const weekNow = mean(recent14.slice(-7).map(day => day.cost))
const weekPrior = mean(recent14.slice(-14, -7).map(day => day.cost))
const weeklyPct = weekPrior > 0 ? Math.round(Math.abs((weekNow - weekPrior) / weekPrior * 100)) : null
const weeklyDirection = weekNow >= weekPrior ? 'higher' : 'lower'
const topModel = data.current.topModels[0]
const saved = actReport.data?.totals.realizedCostUSD ?? 0
const applied = saved > 0 ? (actReport.data?.totals.measuredActions ?? 0) : 0
const localSaved = data.current.localModelSavings.totalUSD
// A custom range has no meaningful "vs last week" or month-to-date baseline.
const anomalies = deriveAnomalies(data, now, !rangeActive)
return (
<div className="ov-dashboard">
{error && <StaleBanner error={error} />}
<div className="ov-card ov-hero-split" aria-label="Key performance indicators">
<div className="ov-hero-main">
<div className="ov-hero-top"><span className="ov-label">{data.current.label}</span><span className="ov-streak"><b>{streakDays(data.history.daily, now)}</b>-day streak</span></div>
<CountUp value={data.current.cost} />
<div className="ov-hero-sub">{data.current.calls.toLocaleString('en-US')} calls · {data.current.sessions.toLocaleString('en-US')} sessions</div>
<div className="ov-saved-line"><span>Saved by applied fixes</span><strong>{formatUsd(saved)}</strong><small>across {applied} {applied === 1 ? 'fix' : 'fixes'}</small></div>
{localSaved > 0 && (
<div className="ov-saved-line"><span>Saved via local models</span><strong>{formatUsd(localSaved)}</strong><small>local-model routing</small></div>
)}
</div>
<ActivityHeatmap daily={data.history.daily} bare />
<EfficiencyScorecard current={data.current} bare />
</div>
{!rangeActive && (
<div className="ov-card ov-stats3">
<div className="ov-stat"><div className="ov-label">Month to date</div><div className="v">{formatUsd(stats.mtd)}</div><div className="d">{stats.pacePct === null ? `No ${stats.prevMonthName} pace yet` : `${stats.pacePct >= 0 ? '+' : ''}${Math.round(stats.pacePct)}% vs ${stats.prevMonthName} pace`}</div></div>
<div className="ov-stat"><div className="ov-label">Projected month</div><div className="v">{formatUsd(stats.projected)} <small>est</small></div><div className="d warn">{formatUsd(Math.max(0, stats.projected - stats.mtd))} to go</div></div>
</div>
)}
<div className="ov-card ov-panel ov-chart-widget">
<div className="ov-panel-head"><h3>Daily spend</h3><span className="r">{topModel ? `Biggest driver: ${topModel.name}` : 'No model driver yet'}</span></div>
<div className="ov-panel-body">{data.history.daily.length ? <DailyChart daily={chartDaily} /> : <EmptyNote>No spend yet.</EmptyNote>}</div>
</div>
<div className="ov-insight-band">
<div className="ov-coach">
<svg viewBox="0 0 24 24" aria-hidden="true"><polyline points="3 17 9 11 13 15 21 7"/><polyline points="15 7 21 7 21 13"/></svg>
<div className="ov-coach-tx">
{rangeActive
? <>{topModel ? <><span className="num">{topModel.name}</span> is the biggest driver in this range</> : 'No single model dominates this range'}. <span className="num">{formatUsd(data.optimize.savingsUSD)}</span> is recoverable.</>
: <>{weeklyPct === null ? <>No prior-week pacing baseline yet</> : <>You're pacing <span className="num">{weeklyPct}% {weeklyDirection}</span> than last week</>}{topModel ? <>; <span className="num">{topModel.name}</span> is the biggest driver</> : ''}. <span className="num">{formatUsd(data.optimize.savingsUSD)}</span> is recoverable.</>}
</div>
<button className="ov-coach-cta" type="button" onClick={() => onNavigate?.('optimize')}>Review →</button>
</div>
<AnomalyCallouts anomalies={anomalies} />
</div>
<div className="ov-analytics-row">
<CostPerOutcome outcome={yieldReport} />
<RoutingWhatIf routing={data.current.routingWaste} onNavigate={onNavigate} />
</div>
<div className="ov-body-grid">
<div className="ov-main-column">
<div className="ov-card ov-panel ov-models-widget">
<div className="ov-panel-head"><h3>Models this period</h3><span className="r">Sorted by cost</span></div>
<div className="ov-panel-body ov-model-panel"><ModelsTable models={models} /></div>
</div>
<div className="ov-card ov-panel ov-sessions-widget">
<div className="ov-panel-head"><h3>Most expensive sessions</h3><span className="r"><button className="ov-link" type="button">See all →</button></span></div>
<div className="ov-panel-body">
{data.current.topSessions.length ? data.current.topSessions.map((session, index) => {
const model = modelIndex.get(sessionModelKey(session.project, session.date, session.calls, session.cost))
const sub = [formatChartDate(session.date), model, `${session.calls} calls`].filter(Boolean).join(' · ')
return <ListRow key={`${session.project}-${session.date}-${index}`} no={String(index + 1).padStart(2, '0')} title={session.project} sub={sub} value={formatUsd(session.cost)} />
}) : <EmptyNote>No sessions in this range.</EmptyNote>}
</div>
</div>
</div>
<div className="ov-side-column">
<div className="ov-card ov-panel ov-activities-widget">
<div className="ov-panel-head"><h3>Top activities</h3><span className="r">Sorted by cost</span></div>
<div className="ov-panel-body"><TopActivities activities={data.current.topActivities} /></div>
</div>
</div>
</div>
</div>
)
}