78 lines
2.6 KiB
TypeScript
78 lines
2.6 KiB
TypeScript
'use client';
|
||
|
||
import { useEffect, useState } from 'react';
|
||
import Link from 'next/link';
|
||
import { formatMinutes } from '@/lib/steam';
|
||
|
||
interface MapSummary {
|
||
map_name: string;
|
||
times_played: number;
|
||
last_played: string;
|
||
total_minutes: number;
|
||
}
|
||
|
||
export default function MapSearch() {
|
||
const [q, setQ] = useState('');
|
||
const [maps, setMaps] = useState<MapSummary[]>([]);
|
||
const [loading, setLoading] = useState(true);
|
||
|
||
useEffect(() => {
|
||
const handle = setTimeout(async () => {
|
||
setLoading(true);
|
||
const res = await fetch(`/api/maps?q=${encodeURIComponent(q)}`);
|
||
const data = await res.json();
|
||
setMaps(data.maps);
|
||
setLoading(false);
|
||
}, 250);
|
||
|
||
return () => clearTimeout(handle);
|
||
}, [q]);
|
||
|
||
return (
|
||
<div className="space-y-4">
|
||
<input
|
||
type="text"
|
||
placeholder="Search by map name…"
|
||
value={q}
|
||
onChange={(e) => setQ(e.target.value)}
|
||
className="w-full 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"
|
||
/>
|
||
|
||
<div className="panel row-divide">
|
||
{loading ? (
|
||
<div className="p-4 text-sm text-ink-faint font-mono">loading…</div>
|
||
) : maps.length === 0 ? (
|
||
<div className="p-4 text-sm text-ink-muted">No maps found.</div>
|
||
) : (
|
||
maps.map((m) => (
|
||
<Link
|
||
key={m.map_name}
|
||
href={`/maps/${encodeURIComponent(m.map_name)}`}
|
||
className="group flex items-center justify-between px-4 py-3 hover:bg-base transition-colors"
|
||
>
|
||
<div className="text-ink text-sm">{m.map_name}</div>
|
||
<div className="flex items-center gap-4">
|
||
<div className="flex flex-col items-end text-xs text-ink-faint font-mono gap-0.5">
|
||
<span>{m.times_played}× played · {formatMinutes(m.total_minutes)} total</span>
|
||
<span>last played {new Date(m.last_played.replace(' ', 'T')).toLocaleString()}</span>
|
||
</div>
|
||
<svg
|
||
width="16"
|
||
height="16"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
strokeWidth="2"
|
||
className="text-ink-faint group-hover:text-accent group-hover:translate-x-0.5 transition-all shrink-0"
|
||
>
|
||
<path d="M9 6l6 6-6 6" strokeLinecap="round" strokeLinejoin="round" />
|
||
</svg>
|
||
</div>
|
||
</Link>
|
||
))
|
||
)}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|