import { useMemo } from 'react' import { Activity, AlertCircle, CheckCircle2 } from 'lucide-react' import { useTranslation } from 'react-i18next' import { cn } from '@/lib/utils' import { Tooltip, TooltipContent, TooltipTrigger, } from '@/components/ui/tooltip' import { formatUptimePct } from '@/features/performance-metrics/lib/format' import { aggregateUptime, type UptimeDayPoint } from '../lib/mock-stats' // --------------------------------------------------------------------------- // Uptime sparkline // --------------------------------------------------------------------------- // // Compact 30-day uptime visualisation: a row of small coloured bars where: // - Bar colour reflects per-day uptime (green / amber / red) // - Bar height reflects severity (the worse the day, the shorter the bar) // - Hovering a bar reveals the exact date and uptime // // Useful as a header strip ("at-a-glance" status) and as a per-row visual // inside the per-group performance table. type SparklineSize = 'sm' | 'md' type UptimeSparklineProps = { series: UptimeDayPoint[] size?: SparklineSize showOverall?: boolean emptyLabel?: string className?: string } function colourFor(uptime: number): string { if (uptime >= 99.9) return 'bg-emerald-500' if (uptime >= 99.0) return 'bg-emerald-400' if (uptime >= 95.0) return 'bg-amber-500' if (uptime >= 90.0) return 'bg-amber-600' return 'bg-rose-500' } function heightFor(uptime: number): string { if (uptime >= 99.9) return 'h-full' if (uptime >= 99.0) return 'h-[88%]' if (uptime >= 95.0) return 'h-[72%]' if (uptime >= 90.0) return 'h-[55%]' return 'h-[40%]' } function overallTextColour(pct: number): string { if (pct >= 99.9) return 'text-emerald-600 dark:text-emerald-400' if (pct >= 99.0) return 'text-emerald-600 dark:text-emerald-400' if (pct >= 95.0) return 'text-amber-600 dark:text-amber-400' return 'text-rose-600 dark:text-rose-400' } export function UptimeSparkline(props: UptimeSparklineProps) { const size = props.size ?? 'md' const showOverall = props.showOverall ?? true if (props.series.length === 0) { return ( {props.emptyLabel ?? '—'} ) } const overall = props.series.reduce((s, p) => s + p.uptime_pct, 0) / props.series.length const containerHeight = size === 'sm' ? 'h-3.5' : 'h-5' const barWidth = size === 'sm' ? 'w-[3px]' : 'w-1' const gap = size === 'sm' ? 'gap-px' : 'gap-[2px]' return (
{props.series.map((day) => ( } >
{day.date}
{day.uptime_pct.toFixed(2)}%
{day.outage_minutes > 0 && (
{day.outage_minutes} min outage
)}
))}
{showOverall && ( {overall.toFixed(1)}% )}
) } // --------------------------------------------------------------------------- // Uptime status row — sparkline + summary text + status icon // --------------------------------------------------------------------------- export function UptimeStatusRow(props: { series: UptimeDayPoint[] className?: string }) { const { t } = useTranslation() const summary = useMemo(() => aggregateUptime(props.series), [props.series]) const status = useMemo(() => { if (summary.uptime_pct >= 99.9) return 'operational' if (summary.uptime_pct >= 99.0) return 'minor' if (summary.uptime_pct >= 95.0) return 'degraded' return 'major' }, [summary.uptime_pct]) const StatusIcon = status === 'operational' ? CheckCircle2 : status === 'minor' ? Activity : AlertCircle const statusColour = status === 'operational' ? 'text-emerald-600 dark:text-emerald-400' : status === 'minor' ? 'text-emerald-600 dark:text-emerald-400' : status === 'degraded' ? 'text-amber-600 dark:text-amber-400' : 'text-rose-600 dark:text-rose-400' const statusLabel = status === 'operational' ? t('All systems operational') : status === 'minor' ? t('Minor blips in the last 30 days') : status === 'degraded' ? t('Degraded performance recently') : t('Significant outages detected') return (
{t('Last 30 days uptime')}
{statusLabel} {summary.incidents > 0 && ( {summary.incidents}{' '} {summary.incidents === 1 ? t('incident') : t('incidents')} )} {summary.outage_minutes > 0 && ( {summary.outage_minutes} {t('min downtime')} )} {formatUptimePct(summary.uptime_pct)} {t('overall')}
) }