Files
gemini-nexus/src/components/usage/ChartCard.tsx
T
Gemini Nexus Bot 20785a8414 feat(usage): redesign Usage Analytics with accurate token averages
- Fix token averaging: calculate over active hours/days only (not full period)
- Enable hourlyData query for all time ranges (not just 24h)
- Rename 'Usage Forecast' to 'Usage Averages' (real data, not projections)
- Add avg/request metric and active hours/days context
- Update terminology: Input/Output Tokens with info tooltips
- Add Estimated Cost stat card with per-model calculation
- Add quota warning alert banner when models exceed 80%
- Add ModelQuotaBar component with color thresholds
2026-05-02 00:28:45 -03:00

71 lines
2.4 KiB
TypeScript

import React from 'react';
import { Skeleton } from '@/components/ui/skeleton';
import { Button } from '@/components/ui/button';
import { AlertTriangle, BarChart3, RotateCcw } from 'lucide-react';
interface ChartStatesProps {
isLoading: boolean;
error: Error | null;
isEmpty: boolean;
onRetry: () => void;
title: string;
emptyTitle: string;
emptyDescription: string;
errorMessage: string;
retryLabel: string;
children: React.ReactNode;
}
export const ChartCard: React.FC<ChartStatesProps> = ({
isLoading,
error,
isEmpty,
onRetry,
title,
emptyTitle,
emptyDescription,
errorMessage,
retryLabel,
children,
}) => {
return (
<div className="bg-card flex flex-col rounded-xl border border-white/[0.06]">
<div className="flex flex-col p-5">
<div className="mb-4 flex items-center justify-between">
<h3 className="text-foreground text-[13px] font-semibold tracking-tight">{title}</h3>
</div>
<div className="flex-1">
{isLoading ? (
<div className="flex h-[280px] flex-col gap-3">
<Skeleton className="h-full w-full rounded-lg" />
</div>
) : error ? (
<div className="flex h-[280px] flex-col items-center justify-center gap-3">
<div className="bg-destructive/10 flex h-10 w-10 items-center justify-center rounded-lg">
<AlertTriangle className="text-destructive h-5 w-5" />
</div>
<p className="text-muted-foreground text-[13px]">{errorMessage}</p>
<Button variant="outline" size="sm" onClick={onRetry}>
<RotateCcw className="mr-1.5 h-3.5 w-3.5" />
{retryLabel}
</Button>
</div>
) : isEmpty ? (
<div className="flex h-[280px] flex-col items-center justify-center gap-2">
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-white/[0.04]">
<BarChart3 className="text-muted-foreground/60 h-5 w-5" />
</div>
<p className="text-foreground text-[13px] font-medium">{emptyTitle}</p>
<p className="text-muted-foreground max-w-[240px] text-center text-xs">
{emptyDescription}
</p>
</div>
) : (
children
)}
</div>
</div>
</div>
);
};