Backend fixes: - Fix totalTokens defaulting to 0 when API omits totalTokenCount (now computes prompt+completion) - Fix streaming metadata extraction: unwrap response wrapper in Anthropic/Gemini handlers - Fix Zod schemas: handle SQL SUM(null) with nullable().transform() - Add is_estimated column migration to token_usage table Frontend redesign: - Rewrite Usage page with area charts (nivo/line), horizontal bar model ranking - StatCard: add trend indicators (% vs previous period) and inline SVG sparklines - Premium color palette, prompt/completion ratio bar, top models mini-list - Live indicator with 15s auto-refresh Design system migration: - Standardize all components on Gemini Nexus tokens (rounded-xl, border-white/[0.06]) - Refine UI components, proxy tabs, cloud account list, provider groups
54 lines
1.7 KiB
TypeScript
54 lines
1.7 KiB
TypeScript
import { createFileRoute } from '@tanstack/react-router';
|
|
import { CloudAccountList } from '@/components/CloudAccountList';
|
|
import { Button } from '@/components/ui/button';
|
|
import { useToast } from '@/components/ui/use-toast';
|
|
import { getLocalizedErrorMessage } from '@/utils/errorMessages';
|
|
import { RefreshCw } from 'lucide-react';
|
|
import { useEffect, useRef } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
|
|
function HomePage() {
|
|
return (
|
|
<div className="container mx-auto max-w-4xl p-6">
|
|
<CloudAccountList />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function HomePageErrorBoundary({ error, reset }: { error: unknown; reset: () => void }) {
|
|
const { t } = useTranslation();
|
|
const { toast } = useToast();
|
|
const hasShownToastRef = useRef(false);
|
|
|
|
useEffect(() => {
|
|
if (hasShownToastRef.current) {
|
|
return;
|
|
}
|
|
|
|
toast({
|
|
title: t('error.generic'),
|
|
description: getLocalizedErrorMessage(error, t),
|
|
variant: 'destructive',
|
|
});
|
|
hasShownToastRef.current = true;
|
|
}, [error, t, toast]);
|
|
|
|
return (
|
|
<div className="container mx-auto max-w-4xl p-6">
|
|
<div className="rounded-xl border border-dashed border-white/[0.1] p-8 text-center">
|
|
<div className="text-base font-semibold">{t('error.generic')}</div>
|
|
<div className="text-muted-foreground mt-2 text-[13px]">{t('action.retry')}</div>
|
|
<Button className="mt-4" variant="outline" onClick={() => reset()}>
|
|
<RefreshCw className="mr-2 h-4 w-4" />
|
|
{t('action.retry')}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export const Route = createFileRoute('/')({
|
|
component: HomePage,
|
|
errorComponent: HomePageErrorBoundary,
|
|
});
|