← Back
β˜†
import { useState } from 'react';
import { useLeaderboard } from '../hooks/useLeaderboard';
import type { LeaderboardEntry } from '../hooks/useLeaderboard';

type Period = 'DAY' | 'WEEK' | 'MONTH' | 'ALL';

const PERIODS: { key: Period; label: string }[] = [
  { key: 'DAY', label: '24h' },
  { key: 'WEEK', label: '7d' },
  { key: 'MONTH', label: '30d' },
  { key: 'ALL', label: 'All Time' },
];

function fmtVol(val: number): string {
  if (val >= 1_000_000_000) return `$${(val / 1_000_000_000).toFixed(1)}B`;
  if (val >= 1_000_000) return `$${(val / 1_000_000).toFixed(1)}M`;
  if (val >= 1_000) return `$${(val / 1_000).toFixed(0)}K`;
  return `$${val.toFixed(0)}`;
}

function fmtUsers(val: number): string {
  if (val >= 1_000) return `${(val / 1_000).toFixed(1)}K`;
  return String(val);
}

function RankBadge({ rank }: { rank: string }) {
  const n = parseInt(rank);
  const medal = n === 1 ? 'πŸ₯‡' : n === 2 ? 'πŸ₯ˆ' : n === 3 ? 'πŸ₯‰' : null;
  if (medal) return <span className="lb-medal">{medal}</span>;
  return <span className="lb-rank">#{rank}</span>;
}

export default function LeaderboardPage() {
  const [period, setPeriod] = useState<Period>('ALL');
  const { entries, loading, refresh } = useLeaderboard(period);

  const ourCode = 'SZHub';
  const ourEntry = entries.find(e =>
    e.builder.toLowerCase() === ourCode.toLowerCase() ||
    e.builderCode.toLowerCase().includes(ourCode.toLowerCase())
  );

  return (
    <div className="lb-page">
      <div className="lb-header">
        <h2 className="lb-title">Leaderboard</h2>
        <button className="page-btn" onClick={refresh} disabled={loading} style={{ fontSize: 13 }}>
          {loading ? 'Loading…' : 'Refresh'}
        </button>
      </div>

      {/* Period tabs */}
      <div className="lb-periods">
        {PERIODS.map(p => (
          <button
            key={p.key}
            className={`pill pill-sm ${period === p.key ? 'pill-active' : ''}`}
            onClick={() => setPeriod(p.key)}
          >
            {p.label}
          </button>
        ))}
      </div>

      {/* Our rank highlight */}
      {ourEntry && (
        <div className="lb-our">
          <span className="lb-our-label">Your Builder</span>
          <div className="lb-our-row">
            <RankBadge rank={ourEntry.rank} />
            <span className="lb-our-name">{ourEntry.builder}</span>
            {ourEntry.verified && <span className="lb-verified" title="Verified">βœ“</span>}
            <span className="lb-our-vol">{fmtVol(ourEntry.volume)}</span>
          </div>
        </div>
      )}

      {/* Loading skeleton */}
      {loading && entries.length === 0 && (
        <div className="lb-list">
          {Array.from({ length: 10 }, (_, i) => (
            <div key={i} className="lb-row lb-skeleton">
              <div className="skeleton-text" style={{ width: 24, height: 16 }} />
              <div className="skeleton-img" />
              <div className="skeleton-text" style={{ flex: 1, height: 14 }} />
              <div className="skeleton-text" style={{ width: 60, height: 14 }} />
            </div>
          ))}
        </div>
      )}

      {/* Leaderboard list */}
      {entries.length > 0 && (
        <div className="lb-list">
          {entries.map((e: LeaderboardEntry) => {
            const isOurs = e.builder.toLowerCase() === ourCode.toLowerCase();
            return (
              <div key={e.builderCode} className={`lb-row ${isOurs ? 'lb-row-ours' : ''}`}>
                <RankBadge rank={e.rank} />
                {e.builderLogo ? (
                  <img src={e.builderLogo} alt="" className="lb-logo"
                    onError={ev => { (ev.target as HTMLImageElement).style.display = 'none'; }} />
                ) : (
                  <div className="lb-logo-placeholder" />
                )}
                <div className="lb-info">
                  <span className="lb-name">
                    {e.builder}
                    {e.verified && <span className="lb-verified" title="Verified">βœ“</span>}
                  </span>
                  <span className="lb-users">{fmtUsers(e.activeUsers)} users</span>
                </div>
                <span className="lb-vol">{fmtVol(e.volume)}</span>
              </div>
            );
          })}
        </div>
      )}

      {/* Empty */}
      {!loading && entries.length === 0 && (
        <div className="lb-empty">
          <p>No leaderboard data available</p>
        </div>
      )}
    </div>
  );
}

πŸ“œ Git History

6c47fa4chore: local Polikopi project home + Phase 1 redesign artifacts12 days ago
Show last diff
Loading...