initial commit of the paytime session frontend

This commit is contained in:
jenz
2026-08-22 14:53:27 +02:00
parent 4ad037bd13
commit 9e8e8378a2
43 changed files with 2936 additions and 0 deletions
@@ -0,0 +1,88 @@
'use client';
import { useEffect, useState } from 'react';
import Link from 'next/link';
import { countryCodeToFlag, formatMinutes } from '@/lib/steam';
interface Player {
steamid: string;
current_name: string;
current_country: string | null;
matched_name: string | null;
total_minutes: number | null;
}
export default function PlayerSearch() {
const [q, setQ] = useState('');
const [sort, setSort] = useState<'recent' | 'name' | 'playtime'>('recent');
const [players, setPlayers] = useState<Player[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
const handle = setTimeout(async () => {
setLoading(true);
const params = new URLSearchParams({ q, sort });
const res = await fetch(`/api/players?${params.toString()}`);
const data = await res.json();
setPlayers(data.players);
setLoading(false);
}, 250); // debounce so we're not hitting the DB on every keystroke
return () => clearTimeout(handle);
}, [q, sort]);
return (
<div className="space-y-4">
<div className="flex gap-3">
<input
type="text"
placeholder="Search by name or SteamID…"
value={q}
onChange={(e) => setQ(e.target.value)}
className="flex-1 bg-base-panel border border-base-border rounded-lg px-4 py-3 text-ink placeholder:text-ink-faint focus:border-accent/50 transition-colors"
/>
<select
value={sort}
onChange={(e) => setSort(e.target.value as typeof sort)}
className="bg-base-panel border border-base-border rounded-lg px-3 text-sm text-ink-muted focus:border-accent/50 transition-colors"
>
<option value="recent">Recently active</option>
<option value="name">Name (AZ)</option>
<option value="playtime">Highest playtime</option>
</select>
</div>
<div className="panel divide-y divide-base-border">
{loading ? (
<div className="p-4 text-sm text-ink-faint font-mono">loading</div>
) : players.length === 0 ? (
<div className="p-4 text-sm text-ink-muted">No players found.</div>
) : (
players.map((p) => (
<Link
key={p.steamid}
href={`/players/${encodeURIComponent(p.steamid)}`}
className="flex items-center justify-between px-4 py-3 hover:bg-base transition-colors"
>
<div className="flex items-center gap-3">
<span className="text-lg" aria-hidden>
{countryCodeToFlag(p.current_country)}
</span>
<div>
<div className="text-ink text-sm">{p.current_name}</div>
{p.matched_name && p.matched_name !== p.current_name && (
<div className="text-xs text-ink-faint">previously: {p.matched_name}</div>
)}
</div>
</div>
<div className="flex items-center gap-4">
<span className="mono text-ink-faint text-xs">{formatMinutes(p.total_minutes)}</span>
<span className="mono text-ink-faint">{p.steamid}</span>
</div>
</Link>
))
)}
</div>
</div>
);
}