From 8a92201b743282a0633a83d4444be808d35b2c86 Mon Sep 17 00:00:00 2001 From: Marc Wieland Date: Mon, 21 Apr 2025 17:46:44 +0200 Subject: [PATCH] Login integriert --- package-lock.json | 10 ++ package.json | 1 + src/admin/AdminDashboard.tsx | 53 ++++++++ src/admin/NewsManager.tsx | 208 ++++++++++++++++++++++++++++++++ src/components/Navbar.tsx | 54 +++++++-- src/components/PrivateRoute.tsx | 14 +++ src/context/AuthContext.tsx | 58 +++++++++ src/main.tsx | 65 +++++----- src/pages/AlleNeuigkeiten.tsx | 88 ++++---------- src/pages/LoginPage.tsx | 51 +++++++- 10 files changed, 500 insertions(+), 102 deletions(-) create mode 100644 src/admin/AdminDashboard.tsx create mode 100644 src/admin/NewsManager.tsx create mode 100644 src/components/PrivateRoute.tsx create mode 100644 src/context/AuthContext.tsx diff --git a/package-lock.json b/package-lock.json index 5b6042341..936f034ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,6 +43,7 @@ "date-fns": "^3.6.0", "embla-carousel-react": "^8.3.0", "input-otp": "^1.2.4", + "jwt-decode": "^4.0.0", "keen-slider": "^6.8.6", "lucide-react": "^0.462.0", "next-themes": "^0.3.0", @@ -4950,6 +4951,15 @@ "dev": true, "license": "MIT" }, + "node_modules/jwt-decode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", + "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/keen-slider": { "version": "6.8.6", "resolved": "https://registry.npmjs.org/keen-slider/-/keen-slider-6.8.6.tgz", diff --git a/package.json b/package.json index 0bd6bfe97..6f8ae0923 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "date-fns": "^3.6.0", "embla-carousel-react": "^8.3.0", "input-otp": "^1.2.4", + "jwt-decode": "^4.0.0", "keen-slider": "^6.8.6", "lucide-react": "^0.462.0", "next-themes": "^0.3.0", diff --git a/src/admin/AdminDashboard.tsx b/src/admin/AdminDashboard.tsx new file mode 100644 index 000000000..2659bb46d --- /dev/null +++ b/src/admin/AdminDashboard.tsx @@ -0,0 +1,53 @@ +import { Link, useNavigate } 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("/"); + }; + + return ( +
+
+

Willkommen, {username}!

+ +
+ +
+ + + + News verwalten +

News erstellen, bearbeiten und löschen

+
+
+ + + {/* Hier später weitere Admin-Bereiche */} + + + + Benutzer verwalten +

Admins erstellen, bearbeiten und löschen

+
+
+ +
+
+ ); +}; + +export default AdminDashboard; diff --git a/src/admin/NewsManager.tsx b/src/admin/NewsManager.tsx new file mode 100644 index 000000000..a658c613d --- /dev/null +++ b/src/admin/NewsManager.tsx @@ -0,0 +1,208 @@ +import { useEffect, useState } from "react"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; + +type NewsItem = { + id: number; + title: string; + description: string; + image_url: string; + team: string; + created_at: string; +}; + +const NewsManager = () => { + const [news, setNews] = useState([]); + const [showForm, setShowForm] = useState(false); + const [editMode, setEditMode] = useState(false); + const [currentId, setCurrentId] = useState(null); + const [newTitle, setNewTitle] = useState(""); + const [newDescription, setNewDescription] = useState(""); + const [newImageUrl, setNewImageUrl] = useState(""); + const [newTeam, setNewTeam] = useState(""); + const [showDeleteModal, setShowDeleteModal] = useState(false); + const [deleteId, setDeleteId] = useState(null); + + useEffect(() => { + loadNews(); + }, []); + + const loadNews = async () => { + try { + const res = await fetch("http://192.168.50.65:3000/api/news"); + const data = await res.json(); + setNews(data); + } catch (err) { + console.error("Fehler beim Laden der News:", err); + } + }; + + const handleCreateOrUpdateNews = async () => { + const method = editMode ? "PUT" : "POST"; + const url = editMode + ? `http://192.168.50.65:3000/api/news/${currentId}` + : `http://192.168.50.65:3000/api/news`; + + try { + const res = await fetch(url, { + method, + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + title: newTitle, + description: newDescription, + image_url: newImageUrl, + team: newTeam, + }), + }); + + if (res.ok) { + await loadNews(); + setShowForm(false); + setEditMode(false); + resetForm(); + } else { + console.error("Fehler beim Speichern der News"); + } + } catch (err) { + console.error("Fehler beim Speichern der News:", err); + } + }; + + const handleEdit = (item: NewsItem) => { + setNewTitle(item.title); + setNewDescription(item.description); + setNewImageUrl(item.image_url); + setNewTeam(item.team); + setCurrentId(item.id); + setEditMode(true); + setShowForm(true); + }; + + const confirmDelete = (id: number) => { + setDeleteId(id); + setShowDeleteModal(true); + }; + + const handleDelete = async () => { + if (!deleteId) return; + + try { + const res = await fetch(`http://192.168.50.65:3000/api/news/${deleteId}`, { + method: "DELETE", + }); + + if (res.ok) { + await loadNews(); + } else { + console.error("Fehler beim Löschen der News"); + } + } catch (err) { + console.error("Fehler beim Löschen der News:", err); + } finally { + setShowDeleteModal(false); + setDeleteId(null); + } + }; + + + const resetForm = () => { + setNewTitle(""); + setNewDescription(""); + setNewImageUrl(""); + setNewTeam(""); + setCurrentId(null); + }; + + return ( +
+
+

News verwalten

+ +
+ + {showForm && ( +
+

+ {editMode ? "News bearbeiten" : "Neue News anlegen"} +

+
+ setNewTitle(e.target.value)} /> +