From e2fe6cf33ade6b411f0c696b576b01e4fe43efd8 Mon Sep 17 00:00:00 2001 From: Marc Wieland Date: Mon, 21 Apr 2025 18:13:54 +0200 Subject: [PATCH] Userverwaltung integriert --- src/admin/AdminDashboard.tsx | 39 ++++----- src/admin/UserCreatePage.tsx | 89 ++++++++++++++++++++ src/admin/UserEditPage.tsx | 105 ++++++++++++++++++++++++ src/admin/UserManagementPage.tsx | 135 +++++++++++++++++++++++++++++++ src/context/AuthContext.tsx | 24 ++++-- src/main.tsx | 6 ++ 6 files changed, 368 insertions(+), 30 deletions(-) create mode 100644 src/admin/UserCreatePage.tsx create mode 100644 src/admin/UserEditPage.tsx create mode 100644 src/admin/UserManagementPage.tsx diff --git a/src/admin/AdminDashboard.tsx b/src/admin/AdminDashboard.tsx index 2659bb46d..bd9d05237 100644 --- a/src/admin/AdminDashboard.tsx +++ b/src/admin/AdminDashboard.tsx @@ -1,32 +1,25 @@ -import { Link, useNavigate } from "react-router-dom"; +import { Link } from "react-router-dom"; import { Card, CardContent, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { useAuth } from "@/context/AuthContext"; -import { LogOut } from "lucide-react"; const AdminDashboard = () => { - const { logout, username } = useAuth(); - const navigate = useNavigate(); - - const handleLogout = () => { - logout(); - navigate("/"); - }; + const { isAuthenticated, username, isAdmin, logout } = useAuth(); return (

Willkommen, {username}!

-
+ {/* Jeder eingeloggt Benutzer darf News verwalten */} @@ -36,15 +29,17 @@ const AdminDashboard = () => { - {/* Hier später weitere Admin-Bereiche */} - - - - Benutzer verwalten -

Admins erstellen, bearbeiten und löschen

-
-
- + {/* Nur Admins sehen diese Card */} + {isAdmin && ( + + + + Benutzer verwalten +

Admins und Benutzer verwalten

+
+
+ + )}
); diff --git a/src/admin/UserCreatePage.tsx b/src/admin/UserCreatePage.tsx new file mode 100644 index 000000000..16ab12a37 --- /dev/null +++ b/src/admin/UserCreatePage.tsx @@ -0,0 +1,89 @@ +import { useEffect, useState } from "react"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { useNavigate } from "react-router-dom"; +import { useAuth } from "@/context/AuthContext"; + + + +const UserCreatePage = () => { + const [username, setUsername] = useState(""); + const [password, setPassword] = useState(""); + const [role, setRole] = useState("user"); + const { token } = useAuth(); + +const {isAuthenticated, isAdmin} = useAuth(); +const navigate = useNavigate(); + +useEffect(() => { + if(!isAuthenticated || !isAdmin) { + navigate("/admin"); + } +}, [isAuthenticated, isAdmin, navigate]); + + + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + try { + const res = await fetch("http://192.168.50.65:3000/api/users", { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify({ username, password, role }), + }); + + if (!res.ok) { + throw new Error("Fehler beim Anlegen des Benutzers"); + } + + navigate("/admin/users"); + } catch (err) { + console.error(err); + alert("Benutzer konnte nicht angelegt werden"); + } + }; + + return ( +
+ + + Benutzer erstellen + + +
+ setUsername(e.target.value)} + required + /> + setPassword(e.target.value)} + required + /> + + +
+
+
+
+ ); +}; + +export default UserCreatePage; diff --git a/src/admin/UserEditPage.tsx b/src/admin/UserEditPage.tsx new file mode 100644 index 000000000..8ef8962fa --- /dev/null +++ b/src/admin/UserEditPage.tsx @@ -0,0 +1,105 @@ +import { useEffect, useState } from "react"; +import { useParams, useNavigate } from "react-router-dom"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { useAuth } from "@/context/AuthContext"; + +const UserEditPage = () => { + const { id } = useParams<{ id: string }>(); + const { token, isAuthenticated, isAdmin } = useAuth(); + const navigate = useNavigate(); + + const [username, setUsername] = useState(""); + const [password, setPassword] = useState(""); // optional neu setzen + const [role, setRole] = useState("user"); + + useEffect(() => { + if (!isAuthenticated || !isAdmin) { + navigate("/admin"); + } + }, [isAuthenticated, isAdmin, navigate]); + + useEffect(() => { + const fetchUser = async () => { + try { + const res = await fetch(`http://192.168.50.65:3000/api/users/${id}`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + const data = await res.json(); + setUsername(data.username); + setRole(data.role); + } catch (err) { + console.error(err); + } + }; + + if (id) { + fetchUser(); + } + }, [id, token]); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + try { + await fetch(`http://192.168.50.65:3000/api/users/${id}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify({ + username, + password: password || undefined, // Passwort nur mitschicken wenn eingegeben + role, + }), + }); + + navigate("/admin/users"); + } catch (err) { + console.error(err); + alert("Fehler beim Aktualisieren des Benutzers"); + } + }; + + return ( +
+ + + Benutzer bearbeiten + + +
+ setUsername(e.target.value)} + required + /> + setPassword(e.target.value)} + /> + + +
+
+
+
+ ); +}; + +export default UserEditPage; diff --git a/src/admin/UserManagementPage.tsx b/src/admin/UserManagementPage.tsx new file mode 100644 index 000000000..58c4ffbfb --- /dev/null +++ b/src/admin/UserManagementPage.tsx @@ -0,0 +1,135 @@ +import { useEffect, useState } from "react"; +import { Card, CardContent, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"; +import { useAuth } from "@/context/AuthContext"; +import { useNavigate } from "react-router-dom"; + + + +interface User { + id: number; + username: string; + email: string; + role: string; +} + +const UserManagementPage = () => { + const [users, setUsers] = useState([]); + const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); + const [userToDelete, setUserToDelete] = useState(null); + const { token } = useAuth(); + + const {isAuthenticated, isAdmin} = useAuth(); +const navigate = useNavigate(); + +useEffect(() => { + if(!isAuthenticated || !isAdmin) { + navigate("/admin"); + } +}, [isAuthenticated, isAdmin, navigate]); + + useEffect(() => { + const fetchUsers = async () => { + try { + const res = await fetch("http://192.168.50.65:3000/api/users", { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + const data = await res.json(); + setUsers(data); + } catch (err) { + console.error(err); + } + }; + + fetchUsers(); + }, [token]); + + const handleDelete = async () => { + if (!userToDelete) return; + + try { + await fetch(`http://192.168.50.65:3000/api/users/${userToDelete.id}`, { + method: "DELETE", + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + // Nach dem Löschen die Liste neu laden + setUsers((prev) => prev.filter((u) => u.id !== userToDelete.id)); + setIsDeleteModalOpen(false); + } catch (err) { + console.error(err); + } + }; + + return ( +
+
+

Benutzerverwaltung

+ +
+ +
+ {users.map((user) => ( + + + {user.username} +

{user.email}

+

+ Rolle: {user.role} +

+ +
+ + +
+
+
+ ))} +
+ + {/* Delete Confirm Modal */} + + + + Möchtest du {userToDelete?.username} wirklich löschen? + + + + + + + +
+ ); +}; + +export default UserManagementPage; diff --git a/src/context/AuthContext.tsx b/src/context/AuthContext.tsx index 449d4c4de..9f2b6f95c 100644 --- a/src/context/AuthContext.tsx +++ b/src/context/AuthContext.tsx @@ -1,11 +1,11 @@ import { createContext, useContext, useState, useEffect, ReactNode } from "react"; -import {jwtDecode} from "jwt-decode"; - - +import { jwtDecode } from "jwt-decode"; interface AuthContextType { token: string | null; username: string | null; + role: string | null; + isAdmin: boolean; login: (token: string) => void; logout: () => void; isAuthenticated: boolean; @@ -13,15 +13,22 @@ interface AuthContextType { const AuthContext = createContext({ token: null, + username: null, + role: null, + isAdmin: false, login: () => {}, logout: () => {}, isAuthenticated: false, - username: null }); export const AuthProvider = ({ children }: { children: ReactNode }) => { const [token, setToken] = useState(null); const [username, setUsername] = useState(null); + const [role, setRole] = useState(null); + + const isAdmin = role === "admin"; + + const isAuthenticated = !!token; useEffect(() => { const storedToken = localStorage.getItem("token"); @@ -29,7 +36,8 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { setToken(storedToken); try { const decoded: any = jwtDecode(storedToken); - setUsername(decoded.username); // <-- Username speichern + setUsername(decoded.username); + setRole(decoded.role); } catch (error) { console.error("Token konnte nicht gelesen werden"); } @@ -44,12 +52,12 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { const logout = () => { localStorage.removeItem("token"); setToken(null); + setUsername(null); + setRole(null); }; - const isAuthenticated = !!token; - return ( - + {children} ); diff --git a/src/main.tsx b/src/main.tsx index 930cff42c..3efebac59 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -16,6 +16,9 @@ import AdminDashboard from "./admin/AdminDashboard"; import NewsManager from "./admin/NewsManager"; import { AuthProvider } from "./context/AuthContext"; import PrivateRoute from "./components/PrivateRoute"; +import UserManagementPage from "./admin/UserManagementPage"; +import UserCreatePage from "./admin/UserCreatePage"; +import UserEditPage from "./admin/UserEditPage"; @@ -53,6 +56,9 @@ ReactDOM.createRoot(document.getElementById("root")!).render( } /> } /> } /> + } /> + } /> + } />