ADD: added team management
This commit is contained in:
6
frontend/package-lock.json
generated
6
frontend/package-lock.json
generated
@@ -5134,9 +5134,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001718",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz",
|
||||
"integrity": "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==",
|
||||
"version": "1.0.30001757",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001757.tgz",
|
||||
"integrity": "sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
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();
|
||||
}
|
||||
@@ -1,12 +1,14 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Team } from "../../components/interfaces/Team";
|
||||
import { get } from "http";
|
||||
import { getTeams } from "../../components/requests/TeamData";
|
||||
import { fetchTeams } from "../api";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
|
||||
|
||||
const Teams: React.FC = () => {
|
||||
const [teams, setTeams] = useState<Team[]>([]);
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
// Replace with API call in production
|
||||
@@ -16,8 +18,10 @@ const Teams: React.FC = () => {
|
||||
async function load()
|
||||
{
|
||||
try {
|
||||
const teams : Team[] = await getTeams(localStorage.getItem("token") || "");
|
||||
setTeams(teams);
|
||||
const teams : Team[] = await fetchTeams(localStorage.getItem("token") || "");
|
||||
console.log("Geladene Teams:", teams);
|
||||
if (teams != null)
|
||||
setTeams(teams);
|
||||
} catch (error) {
|
||||
console.error("Fehler beim Laden der Teams:", error);
|
||||
}
|
||||
@@ -28,6 +32,8 @@ const Teams: React.FC = () => {
|
||||
return (
|
||||
<div style={{ padding: "2rem" }}>
|
||||
<h1>Teams Administration</h1>
|
||||
<button onClick={() => navigate("/teams")
|
||||
}>Create Team</button>
|
||||
<table style={{ width: "100%", borderCollapse: "collapse", marginTop: "1rem" }}>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
@@ -51,7 +51,7 @@ export default function UsersPage(): JSX.Element {
|
||||
params.set("page", String(page));
|
||||
params.set("limit", String(pageSize));
|
||||
|
||||
console.log("Fetching users with params:", params.toString());
|
||||
// console.log("Fetching users with params:", params.toString());
|
||||
|
||||
const token = localStorage.getItem('token');
|
||||
if (!token) throw new Error("No auth token found");
|
||||
@@ -62,7 +62,7 @@ export default function UsersPage(): JSX.Element {
|
||||
user.Email.toLowerCase().includes(query.toLowerCase())
|
||||
);
|
||||
setUsers(filteredData);
|
||||
console.log("Loaded users:", data);
|
||||
// console.log("Loaded users:", data);
|
||||
|
||||
|
||||
// if (!res.ok) throw new Error(`Failed to load users: ${res.status}`);
|
||||
|
||||
@@ -1,140 +1,232 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { getUserFromToken } from '../components/utils/jwt'; // Importiere die Funktion zum Decodieren des Tokens
|
||||
import { getUserFromToken } from '../components/utils/jwt';
|
||||
import { fetchTeams } from './api';
|
||||
|
||||
interface Team {
|
||||
id: string;
|
||||
name: string;
|
||||
players: string[]; // Emails oder Namen
|
||||
createdBy: string;
|
||||
UUID: string;
|
||||
Name: string;
|
||||
Players: string[];
|
||||
OwnerUUID: string;
|
||||
Description: string
|
||||
}
|
||||
|
||||
const TeamManagement = () => {
|
||||
const TeamManagement: React.FC = () => {
|
||||
const [teams, setTeams] = useState<Team[]>([]);
|
||||
const [name, setName] = useState('');
|
||||
const [player2, setPlayer2] = useState('');
|
||||
const [description, setDescription] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
const [editingTeam, setEditingTeam] = useState<Team | null>(null);
|
||||
const [selectedTeam, setSelectedTeam] = useState<Team | null>(null);
|
||||
|
||||
const token = localStorage.getItem('token');
|
||||
const user = token ? getUserFromToken(token) : null;
|
||||
const isAdmin = user?.role?.includes('admin');
|
||||
const API_URL = 'http://localhost:8080/api';
|
||||
|
||||
const fetchTeams = async () => {
|
||||
const res = await fetch('/api/teams', {
|
||||
headers: { Authorization: `Bearer ${token}` }
|
||||
});
|
||||
const data = await res.json();
|
||||
if (res.ok) setTeams(data.data || []);
|
||||
else setError(data.error || 'Fehler beim Laden der Teams');
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (token) fetchTeams();
|
||||
}, [token]);
|
||||
load();
|
||||
}, [token]); // Add token as dependency
|
||||
|
||||
async function load() {
|
||||
if (!token) return;
|
||||
try {
|
||||
const data = await fetchTeams(token);
|
||||
// console.log("Geladene Teams:", data);
|
||||
if (data) {
|
||||
setTeams(data);
|
||||
}
|
||||
} catch (error) {
|
||||
// console.error("Fehler beim Laden der Teams:", error);
|
||||
setError('Fehler beim Laden der Teams');
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!user?.email) {
|
||||
setError('User email not found. Please log in again.');
|
||||
return;
|
||||
}
|
||||
|
||||
const body = {
|
||||
name,
|
||||
players: [user?.email, player2],
|
||||
description: description,
|
||||
owneruuid: user.userId,
|
||||
|
||||
};
|
||||
|
||||
const method = editingTeam ? 'PUT' : 'POST';
|
||||
const url = editingTeam ? `/api/teams/${editingTeam.id}` : '/api/teams';
|
||||
const url = editingTeam ? `/teams/${editingTeam.UUID}` : '/teams';
|
||||
|
||||
const res = await fetch(url, {
|
||||
method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
try {
|
||||
const res = await fetch(API_URL+url, {
|
||||
method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
const data = await res.json();
|
||||
console.log("Response data:", data);
|
||||
|
||||
if (res.ok) {
|
||||
setName('');
|
||||
setPlayer2('');
|
||||
setEditingTeam(null);
|
||||
fetchTeams();
|
||||
} else {
|
||||
setError(data.error || 'Fehler beim Speichern');
|
||||
if (res.ok) {
|
||||
handleCreateNewClick(); // Reset form state
|
||||
load(); // Reload teams
|
||||
} else {
|
||||
setError(data.error || 'Fehler beim Speichern');
|
||||
}
|
||||
} catch (err) {
|
||||
setError('Ein Netzwerkfehler ist aufgetreten.');
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async (id: string) => {
|
||||
const res = await fetch(`/api/teams/${id}`, {
|
||||
method: 'DELETE',
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
if (!window.confirm("Are you sure you want to delete this team?")) return;
|
||||
try {
|
||||
const res = await fetch(`/api/teams/${id}`, {
|
||||
method: 'DELETE',
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
if (res.ok) fetchTeams();
|
||||
else setError('Löschen fehlgeschlagen');
|
||||
if (res.ok) {
|
||||
handleCreateNewClick(); // Reset form
|
||||
load(); // Refresh list
|
||||
} else {
|
||||
const data = await res.json();
|
||||
setError(data.error || 'Löschen fehlgeschlagen');
|
||||
}
|
||||
} catch (err) {
|
||||
setError('Ein Netzwerkfehler ist aufgetreten.');
|
||||
}
|
||||
};
|
||||
|
||||
const canEdit = (team: Team) => isAdmin;
|
||||
const canDelete = (team: Team) =>
|
||||
isAdmin || team.createdBy === user?.userId;
|
||||
const canDelete = (team: Team) => isAdmin || team.OwnerUUID === user?.userId;
|
||||
const canCreate = () => isAdmin || true;
|
||||
|
||||
const canCreate = () => isAdmin || true; // Normale Nutzer dürfen eigene Teams erstellen
|
||||
const handleSelectTeam = (team: Team) => {
|
||||
setSelectedTeam(team);
|
||||
setEditingTeam(team);
|
||||
setName(team.Name);
|
||||
setDescription(team.Description);
|
||||
|
||||
const currentUserEmail = user?.email;
|
||||
// description(otherPlayer || '');
|
||||
};
|
||||
|
||||
const handleCreateNewClick = () => {
|
||||
setSelectedTeam(null);
|
||||
setEditingTeam(null);
|
||||
setName('');
|
||||
setDescription('');
|
||||
setError('');
|
||||
};
|
||||
|
||||
// const myTeams = teams;
|
||||
const myTeams = teams.filter(team =>
|
||||
team.OwnerUUID === user?.userId || (user?.email && team.Players.includes(user.email))
|
||||
);
|
||||
|
||||
// console.log("meine teams ", myTeams);
|
||||
|
||||
return (
|
||||
<div className="max-w-2xl mx-auto p-6 bg-white rounded-xl shadow-md mt-6">
|
||||
<div className="max-w-4xl mx-auto p-6 bg-white rounded-xl shadow-md mt-6">
|
||||
<h2 className="text-2xl font-bold mb-4">Team Management</h2>
|
||||
{error && <p style={{ color: 'red' }}>{error}</p>}
|
||||
{error && <p className="text-red-500 bg-red-100 p-3 rounded mb-4">{error}</p>}
|
||||
|
||||
{canCreate() && (
|
||||
<form onSubmit={handleSubmit} className="grid grid-cols-1 sm:grid-cols-2 gap-4 mb-4">
|
||||
<h3>{editingTeam ? 'Team bearbeiten' : 'Team erstellen'}</h3>
|
||||
<input
|
||||
className="border p-2 rounded"
|
||||
type="text"
|
||||
placeholder="Teamname"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
<input
|
||||
className="border p-2 rounded"
|
||||
type="email"
|
||||
placeholder="E-Mail von Mitspieler"
|
||||
value={player2}
|
||||
onChange={(e) => setPlayer2(e.target.value)}
|
||||
required={!isAdmin}
|
||||
/>
|
||||
<button type="submit"
|
||||
className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700"
|
||||
>{editingTeam ? 'Aktualisieren' : 'Erstellen'}</button>
|
||||
</form>
|
||||
)}
|
||||
|
||||
<hr />
|
||||
<h3>Alle Teams</h3>
|
||||
<ul>
|
||||
{teams.map((team) => (
|
||||
<li key={team.id}>
|
||||
<strong>{team.name}</strong> – Spieler: {team.players.join(', ')}
|
||||
<div style={{ marginTop: '4px' }}>
|
||||
{canEdit(team) && (
|
||||
<button onClick={() => {
|
||||
setName(team.name);
|
||||
setPlayer2(team.players[1] || '');
|
||||
setEditingTeam(team);
|
||||
}}>
|
||||
Bearbeiten
|
||||
<div className="mb-6">
|
||||
<h3 className="text-xl font-semibold mb-2">Meine Teams</h3>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{myTeams.map((team) => (
|
||||
<button
|
||||
key={team.UUID}
|
||||
onClick={() => handleSelectTeam(team)}
|
||||
className={`px-4 py-2 rounded-lg text-sm font-medium
|
||||
${selectedTeam?.UUID === team.UUID
|
||||
? 'bg-blue-600 text-white shadow-lg'
|
||||
: 'bg-gray-200 text-gray-800 hover:bg-gray-300'
|
||||
}`}
|
||||
>
|
||||
{team.Name}
|
||||
</button>
|
||||
)}
|
||||
{canDelete(team) && (
|
||||
<button onClick={() => handleDelete(team.id)}>Löschen</button>
|
||||
)}
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
{canCreate() && (
|
||||
<button
|
||||
onClick={handleCreateNewClick}
|
||||
className="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600"
|
||||
>
|
||||
Neues Team erstellen
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<hr className="my-6"/>
|
||||
|
||||
<div>
|
||||
{!editingTeam && !selectedTeam && (
|
||||
<h3 className="text-xl font-semibold mb-4">Neues Team erstellen</h3>
|
||||
)}
|
||||
{editingTeam && (
|
||||
<h3 className="text-xl font-semibold mb-4">Team ansehen/bearbeiten: {editingTeam.Name}</h3>
|
||||
)}
|
||||
|
||||
{/* Form for creating or editing a team */}
|
||||
{(!selectedTeam && canCreate()) || editingTeam ? (
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div>
|
||||
<label htmlFor="teamname" className="block text-sm font-medium text-gray-700">Teamname</label>
|
||||
<input
|
||||
id="teamname"
|
||||
className="mt-1 block w-full border p-2 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
|
||||
type="text"
|
||||
placeholder="Teamname"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<div>
|
||||
<label htmlFor="description" className="block text-sm font-medium text-gray-700">Beschreibung</label>
|
||||
<input
|
||||
id="description"
|
||||
className="mt-1 block w-full border p-2 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
|
||||
type="text"
|
||||
placeholder="Beschreibung"
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
// required={!isAdmin} // Admin can create single-player teams maybe?
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<button type="submit"
|
||||
className="bg-blue-600 text-white px-6 py-2 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
||||
>
|
||||
{editingTeam ? 'Aktualisieren' : 'Erstellen'}
|
||||
</button>
|
||||
{editingTeam && canDelete(editingTeam) && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleDelete(editingTeam.UUID)}
|
||||
className="bg-red-600 text-white px-6 py-2 rounded-md hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
|
||||
>
|
||||
Löschen
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
) : (
|
||||
<p className="text-gray-500">Wähle ein Team aus der Liste oben aus, um es zu bearbeiten, oder erstelle ein neues Team.</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -111,3 +111,12 @@ export async function registerTeam(id: string, team: { name: string }, token: st
|
||||
if (!res.ok) throw new Error('Team-Anmeldung fehlgeschlagen');
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export async function fetchTeams(token:string)
|
||||
{
|
||||
const res = await fetch(`${API_URL}/teams`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
if (!res.ok) throw new Error('Fehler beim Laden des Spielers');
|
||||
return res.json();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user