158 lines
3.9 KiB
TypeScript
158 lines
3.9 KiB
TypeScript
import { useState, useEffect } from "react";
|
|
import { Game, Source, GameList as GameListProto } from "../items";
|
|
import { apiFetch } from "./api";
|
|
|
|
export function GameList() {
|
|
const [games, setGames] = useState<Game[]>([]);
|
|
const [title, setTitle] = useState("");
|
|
const [source, setSource] = useState<Source>(Source.STEAM);
|
|
const [multiplayer, setMultiplayer] = useState(false);
|
|
const [minPlayers, setMinPlayers] = useState(1);
|
|
const [maxPlayers, setMaxPlayers] = useState(1);
|
|
const [price, setPrice] = useState(0);
|
|
const [remoteId, setRemoteId] = useState(0);
|
|
const [message, setMessage] = useState("");
|
|
|
|
const fetchGames = () => {
|
|
apiFetch("/api/games")
|
|
.then((res) => res.arrayBuffer())
|
|
.then((buffer) => {
|
|
try {
|
|
const list = GameListProto.decode(new Uint8Array(buffer));
|
|
setGames(list.games);
|
|
} catch (e) {
|
|
console.error("Failed to decode games:", e);
|
|
}
|
|
})
|
|
.catch(console.error);
|
|
};
|
|
|
|
useEffect(() => {
|
|
fetchGames();
|
|
}, []);
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
const game = {
|
|
title,
|
|
source,
|
|
multiplayer,
|
|
minPlayers,
|
|
maxPlayers,
|
|
price,
|
|
remoteId,
|
|
};
|
|
|
|
try {
|
|
const encoded = Game.encode(game).finish();
|
|
const res = await apiFetch("/api/game", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/octet-stream",
|
|
},
|
|
body: encoded,
|
|
});
|
|
|
|
if (res.ok) {
|
|
setMessage("Game added successfully!");
|
|
setTitle("");
|
|
fetchGames();
|
|
// Reset other fields if needed
|
|
} else {
|
|
setMessage("Failed to add game.");
|
|
}
|
|
} catch (err) {
|
|
console.error(err);
|
|
setMessage("Error adding game.");
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div>
|
|
<h2>Add New Game</h2>
|
|
{message && <p>{message}</p>}
|
|
<form
|
|
onSubmit={handleSubmit}
|
|
style={{
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
gap: "1rem",
|
|
maxWidth: "400px",
|
|
}}
|
|
>
|
|
<label>
|
|
Title:
|
|
<input
|
|
type="text"
|
|
value={title}
|
|
onChange={(e) => setTitle(e.target.value)}
|
|
required
|
|
/>
|
|
</label>
|
|
<label>
|
|
Source:
|
|
<select
|
|
value={source}
|
|
onChange={(e) => setSource(Number(e.target.value))}
|
|
>
|
|
<option value={Source.STEAM}>Steam</option>
|
|
<option value={Source.ROBLOX}>Roblox</option>
|
|
</select>
|
|
</label>
|
|
<label>
|
|
Multiplayer:
|
|
<input
|
|
type="checkbox"
|
|
checked={multiplayer}
|
|
onChange={(e) => setMultiplayer(e.target.checked)}
|
|
/>
|
|
</label>
|
|
<label>
|
|
Min Players:
|
|
<input
|
|
type="number"
|
|
value={minPlayers}
|
|
onChange={(e) => setMinPlayers(Number(e.target.value))}
|
|
/>
|
|
</label>
|
|
<label>
|
|
Max Players:
|
|
<input
|
|
type="number"
|
|
value={maxPlayers}
|
|
onChange={(e) => setMaxPlayers(Number(e.target.value))}
|
|
/>
|
|
</label>
|
|
<label>
|
|
Price:
|
|
<input
|
|
type="number"
|
|
value={price}
|
|
onChange={(e) => setPrice(Number(e.target.value))}
|
|
/>
|
|
</label>
|
|
<label>
|
|
Remote ID:
|
|
<input
|
|
type="number"
|
|
value={remoteId}
|
|
onChange={(e) => setRemoteId(Number(e.target.value))}
|
|
/>
|
|
</label>
|
|
<button type="submit">Add Game</button>
|
|
</form>
|
|
|
|
<div style={{ marginTop: "2rem" }}>
|
|
<h3>Existing Games</h3>
|
|
<ul>
|
|
{games.map((game) => (
|
|
<li key={game.title}>
|
|
{game.title} ({game.source === Source.STEAM ? "Steam" : "Roblox"})
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|