import { useMemo } from 'react'
import {
Activity,
AlertTriangle,
Gauge,
HeartPulse,
Timer,
TrendingUp,
} from 'lucide-react'
import { useTranslation } from 'react-i18next'
import { cn } from '@/lib/utils'
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table'
import { GroupBadge } from '@/components/group-badge'
import {
aggregateUptime,
buildGroupPerformance,
buildLatencyTimeSeries,
buildUptimeSeries,
formatLatency,
formatThroughput,
formatUptimePct,
type UptimeDayPoint,
} from '../lib/mock-stats'
import type { PricingModel } from '../types'
import {
LatencyTrendChart,
ThroughputBarChart,
UptimeBarChart,
} from './model-details-charts'
import { UptimeSparkline } from './model-details-uptime-sparkline'
const COMPACT_NUMBER = new Intl.NumberFormat(undefined, {
notation: 'compact',
maximumFractionDigits: 1,
})
function StatCard(props: {
icon: React.ComponentType<{ className?: string }>
label: string
value: React.ReactNode
hint?: string
intent?: 'default' | 'warning' | 'success'
}) {
const Icon = props.icon
const intent = props.intent ?? 'default'
return (
{props.label}
{props.value}
{props.hint && (
{props.hint}
)}
)
}
export function ModelDetailsPerformance(props: { model: PricingModel }) {
const { t } = useTranslation()
const performances = useMemo(
() => buildGroupPerformance(props.model),
[props.model]
)
const latencySeries = useMemo(
() => buildLatencyTimeSeries(props.model),
[props.model]
)
const uptimeSeries = useMemo(
() => buildUptimeSeries(props.model),
[props.model]
)
const aggregated = useMemo(
() => aggregateUptime(uptimeSeries),
[uptimeSeries]
)
const uptimeByGroup = useMemo>(() => {
const map: Record = {}
for (const perf of performances) {
map[perf.group] = buildUptimeSeries(props.model, perf.group)
}
return map
}, [performances, props.model])
if (performances.length === 0) {
return (
{t('Performance data is not yet available for this model.')}
)
}
const bestTtft = Math.min(...performances.map((p) => p.ttft_p50_ms))
const bestThroughput = Math.max(...performances.map((p) => p.throughput_tps))
const totalRequests = performances.reduce(
(s, p) => s + p.request_volume_24h,
0
)
const intent =
aggregated.uptime_pct >= 99.9
? 'success'
: aggregated.uptime_pct >= 99
? 'default'
: 'warning'
const headerCellClass =
'text-muted-foreground py-2 text-[10px] font-medium tracking-wider uppercase'
return (
0
? t('{{count}} incidents in the last 30 days', {
count: aggregated.incidents,
})
: t('No incidents in the last 30 days')
}
intent={intent}
/>
{t('Group')}
{t('TTFT P50')}
{t('TTFT P95')}
{t('TTFT P99')}
{t('Throughput')}
{t('Uptime (30d)')}
{t('Requests / 24h')}
{performances.map((perf) => {
const isBestTtft = perf.ttft_p50_ms === bestTtft
const isBestTput = perf.throughput_tps === bestThroughput
return (
{formatLatency(perf.ttft_p50_ms)}
{formatLatency(perf.ttft_p95_ms)}
{formatLatency(perf.ttft_p99_ms)}
0 &&
'text-emerald-600 dark:text-emerald-400'
)}
>
{formatThroughput(perf.throughput_tps)}
{COMPACT_NUMBER.format(perf.request_volume_24h)}
)
})}
{bestThroughput > 0 && (
)}
0
? t(
'Daily uptime; {{incidents}} incidents totalling {{minutes}} minutes',
{
incidents: aggregated.incidents,
minutes: aggregated.outage_minutes,
}
)
: t('Daily uptime over the last 30 days')
}
accent={
aggregated.incidents > 0 ? (
{t('{{count}} incidents', {
count: aggregated.incidents,
})}
) : null
}
/>
{t(
'Performance metrics shown here are simulated for preview purposes and will be replaced with live observability data once the backend integration is complete.'
)}
)
}
function SectionHeader(props: {
icon: React.ComponentType<{ className?: string }>
title: string
description?: string
accent?: React.ReactNode
}) {
const Icon = props.icon
return (
{props.title}
{props.description && (
{props.description}
)}
{props.accent && (
{props.accent}
)}
)
}