ADD: team site and player/usermanagement is working
This commit is contained in:
8
frontend/src/components/interfaces/Team.tsx
Normal file
8
frontend/src/components/interfaces/Team.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import {UserBaseInformation } from "./users";
|
||||
|
||||
export interface Team {
|
||||
teamUUID: string;
|
||||
name: string;
|
||||
Player: UserBaseInformation[];
|
||||
created_at?: string; // ISO date
|
||||
}
|
||||
@@ -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
|
||||
// }
|
||||
10
frontend/src/components/requests/TeamData.tsx
Normal file
10
frontend/src/components/requests/TeamData.tsx
Normal 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();
|
||||
}
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
|
||||
51
frontend/src/pages/Administration/Teams.tsx
Normal file
51
frontend/src/pages/Administration/Teams.tsx
Normal 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;
|
||||
@@ -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>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user