Some checks failed
Deploy to Cloudflare Workers / deploy (push) Has been cancelled
107 lines
3.6 KiB
TypeScript
107 lines
3.6 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { useRouter } from "next/navigation";
|
|
import { api } from "@/lib/api/client";
|
|
import { GlassCard } from "@/components/ui/glass-card";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Label } from "@/components/ui/label";
|
|
|
|
type Player = {
|
|
id: string;
|
|
display_name: string;
|
|
external_id: string | null;
|
|
status: string;
|
|
};
|
|
|
|
export function PlayersRegistry({ initialPlayers }: { initialPlayers: Player[] }) {
|
|
const router = useRouter();
|
|
const [players, setPlayers] = useState(initialPlayers);
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
return (
|
|
<>
|
|
<GlassCard title="Add player">
|
|
<form
|
|
className="flex flex-wrap items-end gap-4"
|
|
onSubmit={async (e) => {
|
|
e.preventDefault();
|
|
if (loading) return;
|
|
const fd = new FormData(e.currentTarget);
|
|
setLoading(true);
|
|
try {
|
|
const created = (await api.players.create({
|
|
display_name: fd.get("display_name") as string,
|
|
external_id: (fd.get("external_id") as string) || undefined,
|
|
})) as Player;
|
|
setPlayers((prev) => [...prev, created].sort((a, b) =>
|
|
a.display_name.localeCompare(b.display_name)
|
|
));
|
|
e.currentTarget.reset();
|
|
router.refresh();
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}}
|
|
>
|
|
<div>
|
|
<Label>Name</Label>
|
|
<Input name="display_name" required className="mt-1" />
|
|
</div>
|
|
<div>
|
|
<Label>External ID</Label>
|
|
<Input name="external_id" className="mt-1" />
|
|
</div>
|
|
<Button type="submit" disabled={loading}>
|
|
Add player
|
|
</Button>
|
|
</form>
|
|
</GlassCard>
|
|
|
|
<GlassCard title="All players">
|
|
<div className="overflow-x-auto">
|
|
<table className="w-full text-sm">
|
|
<thead>
|
|
<tr className="border-b border-white/10 text-left text-[var(--color-muted)]">
|
|
<th className="pb-3 pr-4">Name</th>
|
|
<th className="pb-3 pr-4">Status</th>
|
|
<th className="pb-3">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{players.map((p) => (
|
|
<tr key={p.id} className="border-b border-white/5">
|
|
<td className="py-3 pr-4 font-medium">{p.display_name}</td>
|
|
<td className="py-3 pr-4 capitalize">{p.status}</td>
|
|
<td className="py-3">
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
disabled={loading}
|
|
onClick={async () => {
|
|
setLoading(true);
|
|
const next =
|
|
p.status === "active" ? "inactive" : "active";
|
|
await api.players.setStatus(p.id, next);
|
|
setPlayers((prev) =>
|
|
prev.map((x) =>
|
|
x.id === p.id ? { ...x, status: next } : x
|
|
)
|
|
);
|
|
setLoading(false);
|
|
}}
|
|
>
|
|
Toggle status
|
|
</Button>
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</GlassCard>
|
|
</>
|
|
);
|
|
}
|