ADD: team site and player/usermanagement is working

This commit is contained in:
hwinkel
2025-11-22 14:51:53 +01:00
parent 846a922a41
commit 139a99d96e
13 changed files with 229 additions and 111 deletions

View File

@@ -0,0 +1,8 @@
import {UserBaseInformation } from "./users";
export interface Team {
teamUUID: string;
name: string;
Player: UserBaseInformation[];
created_at?: string; // ISO date
}

View File

@@ -11,50 +11,54 @@ export enum UserRole {
/** Primary user model returned from the API */
export interface User {
export interface UserBaseInformation {
UUID: string
Username: string
AvatarUrl?: string
IsActive: boolean
}
export interface User extends UserBaseInformation {
Email: string
FirstName?: string
LastName?: string
AvatarUrl?: string
Role: UserRole[]
IsActive: boolean
Phone?: string
TeamId?: string[]
CreatedAt?: string // ISO date
UpdatedAt?: string // ISO date
Created_at?: string // ISO date
Updated_at?: string // ISO date
LastLogin?: string // ISO date
}
/** Data required to create a new user */
export interface CreateUserDTO {
username: string
email: string
password: string
firstName?: string
lastName?: string
roles?: UserRole[]
}
// export interface CreateUserDTO {
// username: string
// email: string
// password: string
// firstName?: string
// lastName?: string
// roles?: UserRole[]
// }
/** Data for partial updates to a user */
export interface UpdateUserDTO {
username?: string
email?: string
password?: string
firstName?: string | null
lastName?: string | null
avatarUrl?: string | null
roles?: UserRole[]
isActive?: boolean
teamId?: string | null
metadata?: Record<string, unknown> | null
}
// export interface UpdateUserDTO {
// username?: string
// email?: string
// password?: string
// firstName?: string | null
// lastName?: string | null
// avatarUrl?: string | null
// roles?: UserRole[]
// isActive?: boolean
// teamId?: string | null
// metadata?: Record<string, unknown> | null
// }
/** Simple auth state slice for frontend state management */
export interface AuthState {
currentUser?: User | null
token?: string | null
isLoading: boolean
error?: string | null
}
// export interface AuthState {
// currentUser?: User | null
// token?: string | null
// isLoading: boolean
// error?: string | null
// }

View File

@@ -0,0 +1,10 @@
const API_URL = 'http://localhost:8080/api';
export async function getTeams(token:string)
{
const res = await fetch(`${API_URL}/team/`, {
headers: { Authorization: `Bearer ${token}` },
});
if (!res.ok) throw new Error('Fehler beim Laden des Spielers');
return res.json();
}

View File

@@ -3,6 +3,8 @@ import { fetchPlayers } from "../api";
import { User, UserRole } from "../../components/interfaces/users";
import UsersPage from "./Users";
import TMP from "./TMP";
import TeamManagement from "../Teams";
import Teams from "./Teams";
// type User = {
// id: string;
@@ -227,36 +229,8 @@ export default function Administration(): JSX.Element {
{activeTab === "teams" && (
<section>
<h2>Teams</h2>
<p>Manage teams and membership.</p>
<div style={{ marginTop: 12 }}>
{teams.length === 0 ? (
<p>No teams found.</p>
) : (
<ul style={{ paddingLeft: 0, listStyle: "none" }}>
{teams.map((t) => (
<li
key={t.id}
style={{
display: "flex",
justifyContent: "space-between",
padding: "8px 0",
borderBottom: "1px solid #f2f2f2",
}}
>
<div>
<strong>{t.name}</strong>
<div style={{ fontSize: 13, color: "#666" }}>{t.members} members</div>
</div>
<div>
<button style={{ marginRight: 8 }}>Edit</button>
<button onClick={() => deleteTeam(t)}>Delete</button>
</div>
</li>
))}
</ul>
)}
</div>
<Teams />
</section>
)}

View File

@@ -0,0 +1,51 @@
import React, { useEffect, useState } from "react";
import { Team } from "../../components/interfaces/Team";
import { get } from "http";
import { getTeams } from "../../components/requests/TeamData";
const Teams: React.FC = () => {
const [teams, setTeams] = useState<Team[]>([]);
useEffect(() => {
// Replace with API call in production
load();
}, []);
async function load()
{
try {
const teams : Team[] = await getTeams(localStorage.getItem("token") || "");
setTeams(teams);
} catch (error) {
console.error("Fehler beim Laden der Teams:", error);
}
finally {
}
}
return (
<div style={{ padding: "2rem" }}>
<h1>Teams Administration</h1>
<table style={{ width: "100%", borderCollapse: "collapse", marginTop: "1rem" }}>
<thead>
<tr>
<th style={{ borderBottom: "1px solid #ccc", textAlign: "left" }}>Team Name</th>
</tr>
</thead>
<tbody>
{teams.map(team => (
<tr key={team.teamUUID}>
<td style={{ padding: "0.5rem 0" }}>{team.name}</td>
</tr>
))}
</tbody>
</table>
</div>
);
};
export default Teams;

View File

@@ -51,19 +51,25 @@ export default function UsersPage(): JSX.Element {
params.set("page", String(page));
params.set("limit", String(pageSize));
const token = localStorage.getItem('token');
if (!token) throw new Error("No auth token found");
const data = await fetchPlayers(token);
console.log("Loaded users:", data);
// const res = await fetch(`/api/users?${params.toString()}`, {
// signal: ac.signal,
// });
console.log("Fetching users with params:", params.toString());
const token = localStorage.getItem('token');
if (!token) throw new Error("No auth token found");
const data = await fetchPlayers(token);
const filteredData = data.filter((user: User) =>
user.Username.toLowerCase().includes(query.toLowerCase()) ||
user.Email.toLowerCase().includes(query.toLowerCase())
);
setUsers(filteredData);
console.log("Loaded users:", data);
// if (!res.ok) throw new Error(`Failed to load users: ${res.status}`);
// const data = await res.json();
// Expecting { users: User[] } or User[] — handle both.
const list: User[] = Array.isArray(data) ? data : data.users ?? [];
setUsers(list);
// const list: User[] = Array.isArray(data) ? data : data.users ?? [];
// setUsers(list);
} catch (err: any) {
if (err.name !== "AbortError") setError(err.message || String(err));
} finally {
@@ -99,31 +105,21 @@ export default function UsersPage(): JSX.Element {
const payload = { ...form };
let res: Response;
if (editing) {
console.log("Editing user:", editing.UUID, "with data:", form);
res = await updatePlayer(editing.UUID, {
Username: form.name,
Email: form.email,
Role: form.role.split(","),
}, localStorage.getItem('token') || "");
// res = await createPlayer({
// Username: form.name,
// Email: form.email,
// }, localStorage.getItem('token') || "");
// res = await fetch(`/api/users/${editing.UUID}`, {
// method: "PUT",
// headers: { "Content-Type": "application/json" },
// body: JSON.stringify(payload),
// });
} else {
res = await createPlayer({
Username: form.name,
Email: form.email,
}, localStorage.getItem('token') || "");
console.log("Create response:", res);
// res = await fetch(`/api/users`, {
// method: "POST",
// headers: { "Content-Type": "application/json" },
// body: JSON.stringify(payload),
// });
}
// Parse response body (support both fetch Response and already-parsed results)
@@ -176,8 +172,8 @@ export default function UsersPage(): JSX.Element {
placeholder="Search by name or email..."
value={query}
onChange={(e) => {
console.log("Search query:", e.target.value);
setQuery(e.target.value);
setPage(1);
}}
style={{ padding: 6, flex: 1 }}
/>
@@ -229,7 +225,7 @@ export default function UsersPage(): JSX.Element {
</td>
<td style={{ padding: 8 }}>{u.IsActive ? "Yes" : "No"}</td>
<td style={{ padding: 8 }}>
{u.CreatedAt ? new Date(u.CreatedAt).toLocaleString() : "—"}
{u.Created_at ? new Date(u.Created_at).toLocaleString() : "—"}
</td>
<td style={{ padding: 8 }}>
<button onClick={() => openEdit(u)} style={{ marginRight: 8 }}>
@@ -321,11 +317,21 @@ export default function UsersPage(): JSX.Element {
<div style={{ flex: 1 }}>
<label style={{ display: "block", fontSize: 13, marginBottom: 4 }}>Role</label>
<select
value={form.role}
onChange={(e) => setForm((f) => ({ ...f, role: e.target.value }))}
multiple
value={form.role.split(",")}
onChange={(e) => {
const options = e.target.options;
const selectedRoles: string[] = [];
for (let i = 0; i < options.length; i++) {
if (options[i].selected) {
selectedRoles.push(options[i].value);
}
}
setForm((f) => ({ ...f, role: selectedRoles.join(",") }));
}}
style={{ width: "100%", padding: 8 }}
>
<option value="user">User</option>
<option value="player">Player</option>
<option value="admin">Admin</option>
<option value="coach">Coach</option>
</select>

View File

@@ -42,7 +42,8 @@ export default function PlayerManagement() {
p.UUID === editingId ? { ...p, Username, Email } : p
));
if (token) {
updatePlayer(editingId, { Username, Email }, token);
// updatePlayer(editingId, { Username, Email }, token);
throw new Error("Not implemented yet");
}
setEditingId(null);
} else {

View File

@@ -80,12 +80,13 @@ export async function createPlayer(player: { Username: string, Email: string },
return res.json();
}
export async function updatePlayer(id: string, player: { Username?: string, Email?: string }, token: string) {
export async function updatePlayer(id: string, player: { Username?: string, Email?: string, Role: string[] }, token: string) {
const res = await fetch(`${API_URL}/players/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
body: JSON.stringify(player),
});
console.log("Update response:", res);
if (!res.ok) throw new Error('Spieler-Aktualisierung fehlgeschlagen');
return res.json();
}