feat: implement game creation with a new frontend form, backend API endpoint, and protobuf definition.
This commit is contained in:
+16
-1
@@ -3,6 +3,7 @@ import { Person, PersonList as PersonListProto } from "../items";
|
||||
import { Login } from "./Login";
|
||||
import { PersonList } from "./PersonList";
|
||||
import { PersonDetails } from "./PersonDetails";
|
||||
import { GameList } from "./GameList";
|
||||
import { BrowserRouter, Routes, Route, Link } from "react-router-dom";
|
||||
import "./App.css";
|
||||
import { apiFetch } from "./api";
|
||||
@@ -52,14 +53,28 @@ function App() {
|
||||
}}
|
||||
>
|
||||
<h2>
|
||||
<Link to="/" style={{ textDecoration: "none", color: "inherit" }}>
|
||||
<Link
|
||||
to="/"
|
||||
style={{
|
||||
textDecoration: "none",
|
||||
color: "inherit",
|
||||
marginRight: "1rem",
|
||||
}}
|
||||
>
|
||||
People List
|
||||
</Link>
|
||||
<Link
|
||||
to="/games"
|
||||
style={{ textDecoration: "none", color: "inherit" }}
|
||||
>
|
||||
Games
|
||||
</Link>
|
||||
</h2>
|
||||
<button onClick={handleLogout}>Logout</button>
|
||||
</div>
|
||||
<Routes>
|
||||
<Route path="/" element={<PersonList people={people} />} />
|
||||
<Route path="/games" element={<GameList />} />
|
||||
<Route path="/person/:name" element={<PersonDetails />} />
|
||||
</Routes>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
import { useState } from "react";
|
||||
import { Game, Source } from "../items";
|
||||
import { apiFetch } from "./api";
|
||||
|
||||
export function GameList() {
|
||||
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 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("");
|
||||
// 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user