This commit is contained in:
2025-11-30 21:47:03 +01:00
parent d38f8891f5
commit 73791a0760
12 changed files with 3226 additions and 70 deletions
+46 -14
View File
@@ -1,31 +1,63 @@
import { useState, useEffect } from 'react'
import { Person, PersonList } from '../items'
import './App.css'
import { useState, useEffect } from "react";
import { Person, PersonList } from "../items";
import { Login } from "./Login";
import "./App.css";
function App() {
const [people, setPeople] = useState<Person[]>([])
const [people, setPeople] = useState<Person[]>([]);
const [token, setToken] = useState<string>("");
useEffect(() => {
fetch('/api')
if (!token) return;
fetch("/api")
.then((res) => res.arrayBuffer())
.then((buffer) => {
const list = PersonList.decode(new Uint8Array(buffer))
setPeople(list.person)
const list = PersonList.decode(new Uint8Array(buffer));
setPeople(list.person);
})
.catch((err) => console.error('Failed to fetch people:', err))
}, [])
.catch((err) => console.error("Failed to fetch people:", err));
}, [token]);
const handleLogout = () => {
setToken("");
setPeople([]);
};
if (!token) {
return <Login onLogin={setToken} />;
}
return (
<>
<div className="card">
<h2>People List</h2>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: "1rem",
}}
>
<h2>People List</h2>
<button onClick={handleLogout}>Logout</button>
</div>
{people.map((person, index) => (
<div key={index}>
<div
key={index}
style={{
marginBottom: "1rem",
padding: "1rem",
border: "1px solid #ccc",
borderRadius: "8px",
}}
>
<h3>{person.name}</h3>
<ul>
{person.opinion.map((op, i) => (
<li key={i}>
{op.game?.title} - {op.wouldPlay ? 'Would Play' : 'Would Not Play'}
{op.game?.title} -{" "}
{op.wouldPlay ? "Would Play" : "Would Not Play"}
</li>
))}
</ul>
@@ -33,7 +65,7 @@ function App() {
))}
</div>
</>
)
);
}
export default App
export default App;
+77
View File
@@ -0,0 +1,77 @@
import { useState } from "react";
import { LoginRequest, LoginResponse } from "../items";
interface LoginProps {
onLogin: (token: string) => void;
}
export function Login({ onLogin }: LoginProps) {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError("");
try {
const req = LoginRequest.create({ username, password });
const body = LoginRequest.encode(req).finish();
const res = await fetch("/auth/login", {
method: "POST",
body,
headers: {
"Content-Type": "application/octet-stream",
},
});
const buffer = await res.arrayBuffer();
const response = LoginResponse.decode(new Uint8Array(buffer));
if (response.success) {
onLogin(response.token);
} else {
setError(response.message);
}
} catch (err) {
console.error("Login error:", err);
setError("Failed to login");
}
};
return (
<div className="card">
<h2>Login</h2>
<form
onSubmit={handleSubmit}
style={{ display: "flex", flexDirection: "column", gap: "1rem" }}
>
<div>
<label style={{ display: "block", marginBottom: "0.5rem" }}>
Username:{" "}
</label>
<input
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
style={{ padding: "0.5rem" }}
/>
</div>
<div>
<label style={{ display: "block", marginBottom: "0.5rem" }}>
Password:{" "}
</label>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
style={{ padding: "0.5rem" }}
/>
</div>
<button type="submit">Login</button>
</form>
{error && <p style={{ color: "red", marginTop: "1rem" }}>{error}</p>}
</div>
);
}