feat: implement game listing API and integrate into frontend for display and selection.

This commit is contained in:
2025-12-03 19:01:38 +01:00
parent e192829fdd
commit 0c3c9e61b6
5 changed files with 204 additions and 8 deletions
+33 -2
View File
@@ -1,8 +1,9 @@
import { useState } from "react";
import { Game, Source } from "../items";
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);
@@ -12,6 +13,24 @@ export function GameList() {
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 = {
@@ -37,6 +56,7 @@ export function GameList() {
if (res.ok) {
setMessage("Game added successfully!");
setTitle("");
fetchGames();
// Reset other fields if needed
} else {
setMessage("Failed to add game.");
@@ -121,6 +141,17 @@ export function GameList() {
</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>
);
}
+27 -5
View File
@@ -1,14 +1,32 @@
import { useState, useEffect } from "react";
import { useParams } from "react-router-dom";
import { Person, AddOpinionRequest } from "../items";
import { Person, AddOpinionRequest, Game, GameList } from "../items";
import { apiFetch } from "./api";
export const PersonDetails = () => {
const { name } = useParams<{ name: string }>();
const [person, setPerson] = useState<Person | null>(null);
const [games, setGames] = useState<Game[]>([]);
const [gameTitle, setGameTitle] = useState("");
const [wouldPlay, setWouldPlay] = useState(false);
useEffect(() => {
apiFetch("/api/games")
.then((res) => res.arrayBuffer())
.then((buffer) => {
try {
const list = GameList.decode(new Uint8Array(buffer));
setGames(list.games);
if (list.games.length > 0) {
setGameTitle(list.games[0].title);
}
} catch (e) {
console.error("Failed to decode games:", e);
}
})
.catch(console.error);
}, []);
useEffect(() => {
if (name) {
apiFetch(`/api/${name}`)
@@ -76,12 +94,16 @@ export const PersonDetails = () => {
>
<h3>Add Opinion</h3>
<div style={{ display: "flex", gap: "1rem", alignItems: "center" }}>
<input
type="text"
placeholder="Game Title"
<select
value={gameTitle}
onChange={(e) => setGameTitle(e.target.value)}
/>
>
{games.map((g) => (
<option key={g.title} value={g.title}>
{g.title}
</option>
))}
</select>
<label>
<input
type="checkbox"