ADD: team site and player/usermanagement is working
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"volleyball/internal/auth"
|
"volleyball/internal/auth"
|
||||||
"volleyball/internal/database"
|
"volleyball/internal/database"
|
||||||
@@ -63,12 +64,16 @@ func main() {
|
|||||||
player.CreatePlayer(c, db.GetDB())
|
player.CreatePlayer(c, db.GetDB())
|
||||||
})
|
})
|
||||||
api.PUT("/players/:id", func(c *gin.Context) {
|
api.PUT("/players/:id", func(c *gin.Context) {
|
||||||
|
log.Println("PUT /players/:id called", c.Params)
|
||||||
player.UpdatePlayer(c, db.GetDB())
|
player.UpdatePlayer(c, db.GetDB())
|
||||||
})
|
})
|
||||||
api.DELETE("/players/:id", func(c *gin.Context) {
|
api.DELETE("/players/:id", func(c *gin.Context) {
|
||||||
player.DeletePlayer(c, db.GetDB())
|
player.DeletePlayer(c, db.GetDB())
|
||||||
// c.JSON(http.StatusOK, gin.H{"message": "Player deleted successfully"})
|
// c.JSON(http.StatusOK, gin.H{"message": "Player deleted successfully"})
|
||||||
})
|
})
|
||||||
|
api.GET("/teams", func(c *gin.Context) {
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
port := os.Getenv("PORT")
|
port := os.Getenv("PORT")
|
||||||
if port == "" {
|
if port == "" {
|
||||||
|
|||||||
@@ -100,13 +100,15 @@ func GetPlayer(c *gin.Context, db *sql.DB, id string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func UpdatePlayer(c *gin.Context, db *sql.DB) {
|
func UpdatePlayer(c *gin.Context, db *sql.DB) {
|
||||||
|
log.Println(c)
|
||||||
playerID := c.Param("id")
|
playerID := c.Param("id")
|
||||||
if playerID == "" {
|
if playerID == "" {
|
||||||
common.RespondError(c, http.StatusBadRequest, "Player ID is required")
|
common.RespondError(c, http.StatusBadRequest, "Player ID is required")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
log.Println("role: ", c.GetString("role"))
|
||||||
if playerID != c.GetString("userId") || c.GetString("role") != "admin" {
|
// playerID != c.GetString("userId") ||
|
||||||
|
if c.GetString("role") != "admin" {
|
||||||
common.RespondError(c, http.StatusForbidden, "You do not have permission to update this player")
|
common.RespondError(c, http.StatusForbidden, "You do not have permission to update this player")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,20 +11,20 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
UUID string `db:"uuid" sql:"VARCHAR(255)" index:"true"`
|
UUID string `db:"uuid" sql:"VARCHAR(255)" index:"true"`
|
||||||
Username string `db:"username" sql:"VARCHAR(100)"`
|
Username string `db:"username" sql:"VARCHAR(100)"`
|
||||||
Email string `db:"email" sql:"VARCHAR(255)" index:"true"`
|
Email string `db:"email" sql:"VARCHAR(255)" index:"true"`
|
||||||
lastname sql.NullString `db:"lastname" sql:"VARCHAR(100)"`
|
lastname sql.NullString `db:"lastname" sql:"VARCHAR(100)"`
|
||||||
firstname sql.NullString `db:"firstname" sql:"VARCHAR(100)"`
|
firstname sql.NullString `db:"firstname" sql:"VARCHAR(100)"`
|
||||||
password string `db:"password_hash" sql:"VARCHAR(255)"`
|
password string `db:"password_hash" sql:"VARCHAR(255)"`
|
||||||
phone sql.NullString `db:"phone" sql:"VARCHAR(20)"`
|
phone sql.NullString `db:"phone" sql:"VARCHAR(20)"`
|
||||||
avatarURL sql.NullString `db:"avatar_url" sql:"VARCHAR(255)"`
|
avatarURL sql.NullString `db:"avatar_url" sql:"VARCHAR(255)"`
|
||||||
IsActive sql.NullBool `db:"is_active" sql:"BOOLEAN"`
|
IsActive sql.NullBool `db:"is_active" sql:"BOOLEAN"`
|
||||||
birthday *time.Time `db:"birthday" sql:"DATE"`
|
birthday *time.Time `db:"birthday" sql:"DATE"`
|
||||||
createdAt *time.Time `db:"created_at" sql:"TIMESTAMP"`
|
Created_at *time.Time `db:"created_at" sql:"TIMESTAMP"`
|
||||||
updatedAt *time.Time `db:"updated_at" sql:"TIMESTAMP"`
|
updatedAt *time.Time `db:"updated_at" sql:"TIMESTAMP"`
|
||||||
LastLogin *time.Time `db:"last_login" sql:"TIMESTAMP"`
|
LastLogin *time.Time `db:"last_login" sql:"TIMESTAMP"`
|
||||||
Role []string `ignore:"true"` // wird NICHT in der DB angelegt
|
Role []string `ignore:"true"` // wird NICHT in der DB angelegt
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,7 +236,7 @@ func GetAllPlayers(db *sql.DB) ([]User, error) {
|
|||||||
&player.lastname,
|
&player.lastname,
|
||||||
&player.birthday,
|
&player.birthday,
|
||||||
&player.IsActive,
|
&player.IsActive,
|
||||||
&player.createdAt,
|
&player.Created_at,
|
||||||
(*pq.StringArray)(&player.Role),
|
(*pq.StringArray)(&player.Role),
|
||||||
); err != nil {
|
); err != nil {
|
||||||
log.Printf("Error scanning player row: %v", err)
|
log.Printf("Error scanning player row: %v", err)
|
||||||
@@ -305,7 +305,7 @@ func updatePlayer(db *sql.DB, player User) error {
|
|||||||
|
|
||||||
log.Printf("Updating player: ID=%v, Name=%v, Email=%v", player.UUID, player.Username, player.Email)
|
log.Printf("Updating player: ID=%v, Name=%v, Email=%v", player.UUID, player.Username, player.Email)
|
||||||
|
|
||||||
stmt := "UPDATE public.users SET name = $1, email = $2 WHERE id = $3"
|
stmt := "UPDATE public.users SET username = $1, email = $2 WHERE uuid = $3"
|
||||||
_, err := db.Exec(stmt, player.Username, player.Email, player.UUID)
|
_, err := db.Exec(stmt, player.Username, player.Email, player.UUID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error updating player in database: %v", err)
|
log.Printf("Error updating player in database: %v", err)
|
||||||
|
|||||||
31
backend/internal/team/handler.go
Normal file
31
backend/internal/team/handler.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package team
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"volleyball/internal/common"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetTeams(c *gin.Context, db *sql.DB) {
|
||||||
|
log.Println(c.GetString("userId"), c.GetString("email"), c.GetString("role"))
|
||||||
|
// Simulate fetching players from a database
|
||||||
|
|
||||||
|
teams, err := GetAllTeams(db)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error retrieving teams: %v", err)
|
||||||
|
common.RespondError(c, http.StatusInternalServerError, "Failed to retrieve players")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(teams) > 0 {
|
||||||
|
log.Printf("User %s (%s) requested players", c.GetString("userId"), c.GetString("email"))
|
||||||
|
c.JSON(http.StatusOK, teams)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("User %s (%s) requested players, but none found", c.GetString("userId"), c.GetString("email"))
|
||||||
|
|
||||||
|
common.RespondError(c, http.StatusNotFound, "No Players found")
|
||||||
|
}
|
||||||
25
backend/internal/team/model.go
Normal file
25
backend/internal/team/model.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package team
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Player struct {
|
||||||
|
UUID string `db:"uuid" sql:"VARCHAR(255)" index:"true"`
|
||||||
|
Email string `db:"email" sql:"VARCHAR(255)" index:"true"`
|
||||||
|
Username string `db:"username" sql:"VARCHAR(100)"`
|
||||||
|
FirstName string `db:"firstname" sql:"VARCHAR(100)"`
|
||||||
|
LastName string `db:"lastname" sql:"VARCHAR(100)"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Team struct {
|
||||||
|
UUID string `db:"uuid" sql:"VARCHAR(255)" index:"true"`
|
||||||
|
Name string `db:"username" sql:"VARCHAR(100)"`
|
||||||
|
Email string `db:"email" sql:"VARCHAR(255)" index:"true"`
|
||||||
|
PlayersUUID []Player `db:"players" sql:"JSVARCHAR(255)"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAllTeams(db *sql.DB) ([]Team, error) {
|
||||||
|
// Implementation to retrieve all teams from the database
|
||||||
|
return []Team{}, nil
|
||||||
|
}
|
||||||
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 */
|
/** Primary user model returned from the API */
|
||||||
export interface User {
|
export interface UserBaseInformation {
|
||||||
UUID: string
|
UUID: string
|
||||||
Username: string
|
Username: string
|
||||||
|
AvatarUrl?: string
|
||||||
|
IsActive: boolean
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface User extends UserBaseInformation {
|
||||||
Email: string
|
Email: string
|
||||||
FirstName?: string
|
FirstName?: string
|
||||||
LastName?: string
|
LastName?: string
|
||||||
AvatarUrl?: string
|
|
||||||
Role: UserRole[]
|
Role: UserRole[]
|
||||||
IsActive: boolean
|
|
||||||
Phone?: string
|
Phone?: string
|
||||||
TeamId?: string[]
|
Created_at?: string // ISO date
|
||||||
CreatedAt?: string // ISO date
|
Updated_at?: string // ISO date
|
||||||
UpdatedAt?: string // ISO date
|
|
||||||
LastLogin?: string // ISO date
|
LastLogin?: string // ISO date
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Data required to create a new user */
|
/** Data required to create a new user */
|
||||||
export interface CreateUserDTO {
|
// export interface CreateUserDTO {
|
||||||
username: string
|
// username: string
|
||||||
email: string
|
// email: string
|
||||||
password: string
|
// password: string
|
||||||
firstName?: string
|
// firstName?: string
|
||||||
lastName?: string
|
// lastName?: string
|
||||||
roles?: UserRole[]
|
// roles?: UserRole[]
|
||||||
}
|
// }
|
||||||
|
|
||||||
/** Data for partial updates to a user */
|
/** Data for partial updates to a user */
|
||||||
export interface UpdateUserDTO {
|
// export interface UpdateUserDTO {
|
||||||
username?: string
|
// username?: string
|
||||||
email?: string
|
// email?: string
|
||||||
password?: string
|
// password?: string
|
||||||
firstName?: string | null
|
// firstName?: string | null
|
||||||
lastName?: string | null
|
// lastName?: string | null
|
||||||
avatarUrl?: string | null
|
// avatarUrl?: string | null
|
||||||
roles?: UserRole[]
|
// roles?: UserRole[]
|
||||||
isActive?: boolean
|
// isActive?: boolean
|
||||||
teamId?: string | null
|
// teamId?: string | null
|
||||||
metadata?: Record<string, unknown> | null
|
// metadata?: Record<string, unknown> | null
|
||||||
}
|
// }
|
||||||
|
|
||||||
/** Simple auth state slice for frontend state management */
|
/** Simple auth state slice for frontend state management */
|
||||||
export interface AuthState {
|
// export interface AuthState {
|
||||||
currentUser?: User | null
|
// currentUser?: User | null
|
||||||
token?: string | null
|
// token?: string | null
|
||||||
isLoading: boolean
|
// isLoading: boolean
|
||||||
error?: string | null
|
// 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 { User, UserRole } from "../../components/interfaces/users";
|
||||||
import UsersPage from "./Users";
|
import UsersPage from "./Users";
|
||||||
import TMP from "./TMP";
|
import TMP from "./TMP";
|
||||||
|
import TeamManagement from "../Teams";
|
||||||
|
import Teams from "./Teams";
|
||||||
|
|
||||||
// type User = {
|
// type User = {
|
||||||
// id: string;
|
// id: string;
|
||||||
@@ -227,36 +229,8 @@ export default function Administration(): JSX.Element {
|
|||||||
|
|
||||||
{activeTab === "teams" && (
|
{activeTab === "teams" && (
|
||||||
<section>
|
<section>
|
||||||
<h2>Teams</h2>
|
<Teams />
|
||||||
<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>
|
|
||||||
</section>
|
</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("page", String(page));
|
||||||
params.set("limit", String(pageSize));
|
params.set("limit", String(pageSize));
|
||||||
|
|
||||||
const token = localStorage.getItem('token');
|
console.log("Fetching users with params:", params.toString());
|
||||||
if (!token) throw new Error("No auth token found");
|
|
||||||
const data = await fetchPlayers(token);
|
const token = localStorage.getItem('token');
|
||||||
console.log("Loaded users:", data);
|
if (!token) throw new Error("No auth token found");
|
||||||
// const res = await fetch(`/api/users?${params.toString()}`, {
|
const data = await fetchPlayers(token);
|
||||||
// signal: ac.signal,
|
|
||||||
// });
|
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}`);
|
// if (!res.ok) throw new Error(`Failed to load users: ${res.status}`);
|
||||||
// const data = await res.json();
|
// const data = await res.json();
|
||||||
// Expecting { users: User[] } or User[] — handle both.
|
// Expecting { users: User[] } or User[] — handle both.
|
||||||
const list: User[] = Array.isArray(data) ? data : data.users ?? [];
|
// const list: User[] = Array.isArray(data) ? data : data.users ?? [];
|
||||||
setUsers(list);
|
// setUsers(list);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
if (err.name !== "AbortError") setError(err.message || String(err));
|
if (err.name !== "AbortError") setError(err.message || String(err));
|
||||||
} finally {
|
} finally {
|
||||||
@@ -99,31 +105,21 @@ export default function UsersPage(): JSX.Element {
|
|||||||
const payload = { ...form };
|
const payload = { ...form };
|
||||||
let res: Response;
|
let res: Response;
|
||||||
if (editing) {
|
if (editing) {
|
||||||
|
|
||||||
|
console.log("Editing user:", editing.UUID, "with data:", form);
|
||||||
res = await updatePlayer(editing.UUID, {
|
res = await updatePlayer(editing.UUID, {
|
||||||
Username: form.name,
|
Username: form.name,
|
||||||
Email: form.email,
|
Email: form.email,
|
||||||
|
Role: form.role.split(","),
|
||||||
}, localStorage.getItem('token') || "");
|
}, 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 {
|
} else {
|
||||||
res = await createPlayer({
|
res = await createPlayer({
|
||||||
Username: form.name,
|
Username: form.name,
|
||||||
Email: form.email,
|
Email: form.email,
|
||||||
}, localStorage.getItem('token') || "");
|
}, localStorage.getItem('token') || "");
|
||||||
console.log("Create response:", res);
|
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)
|
// 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..."
|
placeholder="Search by name or email..."
|
||||||
value={query}
|
value={query}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
|
console.log("Search query:", e.target.value);
|
||||||
setQuery(e.target.value);
|
setQuery(e.target.value);
|
||||||
setPage(1);
|
|
||||||
}}
|
}}
|
||||||
style={{ padding: 6, flex: 1 }}
|
style={{ padding: 6, flex: 1 }}
|
||||||
/>
|
/>
|
||||||
@@ -229,7 +225,7 @@ export default function UsersPage(): JSX.Element {
|
|||||||
</td>
|
</td>
|
||||||
<td style={{ padding: 8 }}>{u.IsActive ? "Yes" : "No"}</td>
|
<td style={{ padding: 8 }}>{u.IsActive ? "Yes" : "No"}</td>
|
||||||
<td style={{ padding: 8 }}>
|
<td style={{ padding: 8 }}>
|
||||||
{u.CreatedAt ? new Date(u.CreatedAt).toLocaleString() : "—"}
|
{u.Created_at ? new Date(u.Created_at).toLocaleString() : "—"}
|
||||||
</td>
|
</td>
|
||||||
<td style={{ padding: 8 }}>
|
<td style={{ padding: 8 }}>
|
||||||
<button onClick={() => openEdit(u)} style={{ marginRight: 8 }}>
|
<button onClick={() => openEdit(u)} style={{ marginRight: 8 }}>
|
||||||
@@ -321,11 +317,21 @@ export default function UsersPage(): JSX.Element {
|
|||||||
<div style={{ flex: 1 }}>
|
<div style={{ flex: 1 }}>
|
||||||
<label style={{ display: "block", fontSize: 13, marginBottom: 4 }}>Role</label>
|
<label style={{ display: "block", fontSize: 13, marginBottom: 4 }}>Role</label>
|
||||||
<select
|
<select
|
||||||
value={form.role}
|
multiple
|
||||||
onChange={(e) => setForm((f) => ({ ...f, role: e.target.value }))}
|
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 }}
|
style={{ width: "100%", padding: 8 }}
|
||||||
>
|
>
|
||||||
<option value="user">User</option>
|
<option value="player">Player</option>
|
||||||
<option value="admin">Admin</option>
|
<option value="admin">Admin</option>
|
||||||
<option value="coach">Coach</option>
|
<option value="coach">Coach</option>
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
@@ -42,7 +42,8 @@ export default function PlayerManagement() {
|
|||||||
p.UUID === editingId ? { ...p, Username, Email } : p
|
p.UUID === editingId ? { ...p, Username, Email } : p
|
||||||
));
|
));
|
||||||
if (token) {
|
if (token) {
|
||||||
updatePlayer(editingId, { Username, Email }, token);
|
// updatePlayer(editingId, { Username, Email }, token);
|
||||||
|
throw new Error("Not implemented yet");
|
||||||
}
|
}
|
||||||
setEditingId(null);
|
setEditingId(null);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -80,12 +80,13 @@ export async function createPlayer(player: { Username: string, Email: string },
|
|||||||
return res.json();
|
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}`, {
|
const res = await fetch(`${API_URL}/players/${id}`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
|
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
|
||||||
body: JSON.stringify(player),
|
body: JSON.stringify(player),
|
||||||
});
|
});
|
||||||
|
console.log("Update response:", res);
|
||||||
if (!res.ok) throw new Error('Spieler-Aktualisierung fehlgeschlagen');
|
if (!res.ok) throw new Error('Spieler-Aktualisierung fehlgeschlagen');
|
||||||
return res.json();
|
return res.json();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user