The first commit, the core and all files is adding to repo
This commit is contained in:
79
components/StartGame.tsx
Normal file
79
components/StartGame.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
"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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user