Guides/Player Statistics Dashboard

Player Statistics Dashboard

Build a player stats dashboard with charts and trends

Fetching Player Data

api/player.ts
import { CitoAPI } from '@citoapi/sdk';

const cito = new CitoAPI(process.env.CITO_API_KEY!);

export async function getPlayer(username: string, game: string) {
  const { data } = await cito[game].players.get(username);
  return data;
}

export async function getPlayerHistory(username: string, game: string) {
  const { data } = await cito[game].players.matches(username, {
    limit: 50,
    sort: 'desc'
  });
  return data;
}

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>
  );
}