Files
projects-jenz/discord_verificiation/playtime-frontend/components/PlayerSearch.tsx
T

101 lines
3.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import { useEffect, useState } from 'react';
import Link from 'next/link';
import { countryCodeToFlag, formatMinutes } from '@/lib/steam';
import { formatRelativeTime } from '@/lib/dates';
interface Player {
steamid: string;
current_name: string;
current_country: string | null;
matched_name: string | null;
total_minutes: number | null;
last_connected: string | null;
avatarUrl: string;
}
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 row-divide">
{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">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={p.avatarUrl}
alt=""
className="w-9 h-9 rounded-full object-cover ring-1 ring-base-border shrink-0"
/>
<span 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 w-16 text-right">
{formatRelativeTime(p.last_connected)}
</span>
<span className="mono text-ink-faint text-xs w-20 text-right">
{formatMinutes(p.total_minutes)}
</span>
<span className="mono text-ink-faint">{p.steamid}</span>
</div>
</Link>
))
)}
</div>
</div>
);
}