Player Statistics Dashboard
Build a player stats dashboard with charts and trends
Fetching Player Data
api/player.ts
const API_BASE = "https://api.citoapi.com/api/v1";
async function citoGet(path: string) {
const response = await fetch(`${API_BASE}${path}`, {
headers: { "x-api-key": process.env.CITO_API_KEY! }
});
if (!response.ok) throw new Error(`Cito API error: ${response.status}`);
return response.json();
}
export async function getPlayer(username: string, game: string) {
return citoGet(`/${game}/players/${encodeURIComponent(username)}`);
}
export async function getPlayerHistory(username: string, game: string) {
return citoGet(`/${game}/players/${encodeURIComponent(username)}/matches?limit=50&sort=desc`);
}Dashboard Component
PlayerDashboard.tsx
import { useQuery } from '@tanstack/react-query';
import { LineChart, Line, XAxis, YAxis, Tooltip } from 'recharts';
export function PlayerDashboard({ username, game }) {
const { data: player, isLoading } = useQuery({
queryKey: ['player', username, game],
queryFn: () => getPlayer(username, game)
});
const { data: history } = useQuery({
queryKey: ['player-history', username, game],
queryFn: () => getPlayerHistory(username, game)
});
if (isLoading) return <Skeleton />;
const kdTrend = history?.map(match => ({
date: new Date(match.played_at).toLocaleDateString(),
kd: match.eliminations / Math.max(match.deaths, 1)
}));
return (
<div className="space-y-6">
{/* Player Header */}
<div className="flex items-center gap-4">
<Avatar username={player.username} />
<div>
<h1 className="text-2xl font-bold">{player.username}</h1>
<p className="text-muted">{player.team || 'Free Agent'}</p>
</div>
</div>
{/* Stats Grid */}
<div className="grid grid-cols-4 gap-4">
<StatCard label="Wins" value={player.stats.career.wins} />
<StatCard label="K/D" value={player.stats.career.kd_ratio.toFixed(2)} />
<StatCard label="Win Rate" value={`${player.stats.career.win_rate}%`} />
<StatCard label="Earnings" value={`$${player.stats.competitive.earnings.toLocaleString()}`} />
</div>
{/* K/D Trend Chart */}
<div className="p-4 bg-secondary rounded-lg">
<h3 className="font-semibold mb-4">K/D Trend (Last 50 Games)</h3>
<LineChart width={600} height={200} data={kdTrend}>
<XAxis dataKey="date" />
<YAxis />
<Tooltip />
<Line type="monotone" dataKey="kd" stroke="#00E5CC" />
</LineChart>
</div>
{/* Recent Matches */}
<div>
<h3 className="font-semibold mb-4">Recent Matches</h3>
<MatchList matches={history?.slice(0, 10)} />
</div>
</div>
);
}Caching Strategies
Player stats don't change frequently. Use aggressive caching:
// Next.js API route with caching
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const username = searchParams.get('username');
const data = await getPlayer(username);
return Response.json(data, {
headers: {
'Cache-Control': 'public, s-maxage=300, stale-while-revalidate=600'
}
});
}
// React Query with stale time
const { data } = useQuery({
queryKey: ['player', username],
queryFn: () => getPlayer(username),
staleTime: 5 * 60 * 1000, // 5 minutes
cacheTime: 30 * 60 * 1000 // 30 minutes
});Player Comparison
export function PlayerComparison({ players }: { players: string[] }) {
const queries = useQueries({
queries: players.map(username => ({
queryKey: ['player', username],
queryFn: () => getPlayer(username)
}))
});
const data = queries.map(q => q.data).filter(Boolean);
return (
<table>
<thead>
<tr>
<th>Stat</th>
{data.map(p => <th key={p.username}>{p.username}</th>)}
</tr>
</thead>
<tbody>
<tr>
<td>Wins</td>
{data.map(p => <td key={p.username}>{p.stats.career.wins}</td>)}
</tr>
<tr>
<td>K/D</td>
{data.map(p => <td key={p.username}>{p.stats.career.kd_ratio.toFixed(2)}</td>)}
</tr>
{/* More stats... */}
</tbody>
</table>
);
}