Files
MineGeo/components/StartGame.tsx

80 lines
2.4 KiB
TypeScript

"use client";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { ArrowRight, Timer, ZoomIn } from "lucide-react";
import { difficulties, type Difficulty } from "@/shared/protocol";
export async function api<T = Record<string, unknown>>(
path: string,
body?: unknown,
method = body ? "POST" : "GET",
): Promise<T> {
const res = await fetch("/api/v1/" + path, {
method,
headers: body ? { "Content-Type": "application/json" } : undefined,
body: body ? JSON.stringify(body) : undefined,
});
const data = await res.json();
if (!res.ok) throw new Error(data.error || "Request failed");
return data;
}
export function StartGame({ datasetId }: { datasetId: string }) {
const [difficulty, setDifficulty] = useState<Difficulty>("normal"),
[loading, setLoading] = useState(false),
[error, setError] = useState("");
const router = useRouter();
async function start() {
setLoading(true);
setError("");
try {
const session = await api<{ id: string }>("sessions", {
datasetId,
difficulty,
});
router.push("/play/" + session.id);
} catch (e) {
setError((e as Error).message);
setLoading(false);
}
}
return (
<div className="start-panel">
<span className="eyebrow">YOUR EXPEDITION</span>
<h2>Choose your challenge.</h2>
<div className="difficulty-options">
{Object.entries(difficulties).map(([key, value]) => (
<button
key={key}
aria-pressed={difficulty === key}
className={difficulty === key ? "selected" : ""}
onClick={() => setDifficulty(key as Difficulty)}
>
<strong>{value.label}</strong>
<span>
<Timer size={13} />
{value.seconds}s per round
</span>
<span>
<ZoomIn size={13} />
{value.zoom ? "Zoom allowed" : "No zoom"}
</span>
</button>
))}
</div>
<p className="muted">5 rounds · 25,000 possible points · No movement</p>
<button
className="button primary full"
onClick={start}
disabled={loading}
>
{loading ? "Preparing your world…" : "Start exploring"}
<ArrowRight size={17} />
</button>
{error && (
<p role="alert" className="error-message">
{error}
</p>
)}
</div>
);
}