Spaces:
Runtime error
Runtime error
File size: 4,864 Bytes
cd8bd0a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | "use client";
import { useTranslations } from "next-intl";
export interface CacheTrendPoint {
timestamp: string;
requests: number;
cachedRequests: number;
inputTokens: number;
cachedTokens: number;
cacheCreationTokens: number;
}
interface CacheTrendsProps {
data?: CacheTrendPoint[] | null;
loading?: boolean;
error?: string | null;
onRetry?: () => void;
}
export default function CacheTrends({
data,
loading = false,
error = null,
onRetry,
}: CacheTrendsProps) {
const t = useTranslations("cache");
const trendData: CacheTrendPoint[] = data ?? [];
const maxRequests = trendData.length > 0 ? Math.max(...trendData.map((p) => p.requests), 1) : 1;
const maxCachedRequests =
trendData.length > 0 ? Math.max(...trendData.map((p) => p.cachedRequests), 0) : 0;
return (
<div
data-testid="cache-trends"
className="rounded-xl border border-border bg-surface p-5 flex flex-col gap-4"
>
<div className="flex items-center gap-2">
<span className="material-symbols-outlined text-base text-text-muted" aria-hidden="true">
timeline
</span>
<h2 className="font-medium text-sm">{t("trend24h")}</h2>
</div>
{loading ? (
<div className="flex flex-col gap-3">
<div
data-testid="skeleton"
className="h-32 w-full rounded bg-text-muted/10 animate-pulse"
/>
<div data-testid="skeleton" className="h-3 w-24 rounded bg-text-muted/10 animate-pulse" />
</div>
) : error ? (
<div className="flex flex-col items-center gap-3 py-6">
<span className="text-sm text-red-500">{error}</span>
{onRetry && (
<button
onClick={onRetry}
className="px-3 py-1.5 text-xs rounded border border-border hover:bg-surface-raised transition-colors"
>
Retry
</button>
)}
</div>
) : trendData.length === 0 ? (
<div className="flex flex-col items-center justify-center h-32 gap-2">
<span className="text-sm text-text-muted">
No data available — no cache activity in the last 24h
</span>
</div>
) : (
<>
{maxCachedRequests > 0 && (
<div className="text-xs text-text-muted">
{t("peakCached")}:{" "}
<span className="font-medium text-foreground">
{maxCachedRequests} / {maxRequests}
</span>
</div>
)}
<div className="flex items-end gap-1 h-32">
{trendData.map((point) => {
const height = Math.max(4, (point.requests / maxRequests) * 100);
const cachedHeight =
point.requests > 0
? Math.max(2, (point.cachedRequests / point.requests) * height)
: 0;
const hour = new Date(point.timestamp).toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
hour12: false,
});
return (
<div
key={point.timestamp}
data-testid="trend-bar"
className="flex-1 flex flex-col items-center gap-1 group relative"
>
<div className="absolute bottom-full mb-1 hidden group-hover:block bg-surface-raised border border-border rounded px-2 py-1 text-xs whitespace-nowrap z-10">
{hour}: {point.requests} {t("requests").toLowerCase()}, {point.cachedRequests}{" "}
{t("cached").toLowerCase()}
</div>
<div className="w-full flex flex-col justify-end h-full gap-px">
<div
className="w-full bg-green-500/30 rounded-t"
style={{ height: `${cachedHeight}%` }}
/>
<div
className="w-full bg-text-muted/20 rounded-t"
style={{ height: `${height - cachedHeight}%` }}
/>
</div>
<span className="text-[10px] text-text-muted truncate w-full text-center">
{hour.split(":")[0]}
</span>
</div>
);
})}
</div>
<div className="flex items-center gap-4 text-xs text-text-muted">
<div className="flex items-center gap-1.5">
<div className="w-3 h-3 rounded bg-text-muted/20" />
<span>{t("total")}</span>
</div>
<div className="flex items-center gap-1.5">
<div className="w-3 h-3 rounded bg-green-500/30" />
<span>{t("cached")}</span>
</div>
</div>
</>
)}
</div>
);
}
|