index.tsx•4.98 kB
import { createFileRoute } from '@tanstack/react-router';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { trpc } from '@/lib/trpc';
import { formatCost, formatNumber } from '@/lib/utils';
import {
LineChart,
Line,
PieChart,
Pie,
Cell,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
ResponsiveContainer,
} from 'recharts';
export const Route = createFileRoute('/')({
component: Dashboard,
});
const COLORS = {
openai: '#10a37f',
google: '#4285f4',
azure: '#0078d4',
};
function Dashboard() {
const { data: stats, isLoading } = trpc.stats.overview.useQuery();
if (isLoading) {
return (
<div className="p-8">
<div className="animate-pulse space-y-4">
<div className="h-32 bg-gray-200 rounded" />
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
{[1, 2, 3, 4].map((i) => (
<div key={i} className="h-24 bg-gray-200 rounded" />
))}
</div>
</div>
</div>
);
}
const pieData = stats?.byProvider.map((p) => ({
name: p.provider,
value: p.totalCost,
})) || [];
const lineData = stats?.last30Days || [];
return (
<div className="p-8">
<div className="mb-8">
<h2 className="text-3xl font-bold tracking-tight">Dashboard Overview</h2>
<p className="text-muted-foreground">
Your AI usage statistics and insights
</p>
</div>
{/* Stats Cards */}
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4 mb-8">
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Total Requests</CardTitle>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">
{formatNumber(stats?.totalRequests || 0)}
</div>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Total Tokens</CardTitle>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">
{formatNumber(stats?.totalTokens || 0)}
</div>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Total Cost</CardTitle>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">
{formatCost(stats?.totalCost || 0)}
</div>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Active Providers</CardTitle>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">
{stats?.byProvider.length || 0}
</div>
</CardContent>
</Card>
</div>
<div className="grid gap-4 md:grid-cols-2">
{/* Usage Trend */}
<Card>
<CardHeader>
<CardTitle>Usage Trend (Last 30 Days)</CardTitle>
</CardHeader>
<CardContent>
<ResponsiveContainer width="100%" height={300}>
<LineChart data={lineData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="date" />
<YAxis />
<Tooltip />
<Line
type="monotone"
dataKey="requests"
stroke="#8884d8"
name="Requests"
/>
</LineChart>
</ResponsiveContainer>
</CardContent>
</Card>
{/* Cost by Provider */}
<Card>
<CardHeader>
<CardTitle>Cost by Provider</CardTitle>
</CardHeader>
<CardContent>
<ResponsiveContainer width="100%" height={300}>
<PieChart>
<Pie
data={pieData}
cx="50%"
cy="50%"
labelLine={false}
label={(entry) => entry.name}
outerRadius={80}
fill="#8884d8"
dataKey="value"
>
{pieData.map((entry, index) => (
<Cell
key={`cell-${index}`}
fill={COLORS[entry.name as keyof typeof COLORS] || '#8884d8'}
/>
))}
</Pie>
<Tooltip formatter={(value: number) => formatCost(value)} />
</PieChart>
</ResponsiveContainer>
</CardContent>
</Card>
</div>
</div>
);
}