From a8b4ac3d60699e2c793d11afba4faeb75c7c20c9 Mon Sep 17 00:00:00 2001 From: Theis Gaedigk Date: Tue, 11 Nov 2025 17:08:45 +0100 Subject: [PATCH] Refactor loan and user management components and backend routes - Updated LoanTable component to fetch loan data from new API endpoint and display notes. - Enhanced UserTable component to include additional user fields (first name, last name, email, admin status) and updated input handling. - Modified fetcher utility to use new user data API endpoint. - Adjusted login functionality to point to the new admin login endpoint and handle unauthorized access. - Refactored user actions utility to align with updated API endpoints for user management. - Updated backend routes for user and loan data management to reflect new structure and naming conventions. - Revised SQL schema and mock data to accommodate new fields and constraints. - Changed Docker configuration to use the new database name. --- admin/src/Layout/Layout.tsx | 21 +- admin/src/Layout/Sidebar.tsx | 44 ++++- admin/src/components/APIKeyTable.tsx | 53 ++--- admin/src/components/AddAPIKey.tsx | 46 +++-- admin/src/components/AddForm.tsx | 187 ++++++++++++------ admin/src/components/ItemTable.tsx | 52 +++-- admin/src/components/LoanTable.tsx | 26 +-- admin/src/components/UserTable.tsx | 96 ++++++++- admin/src/utils/fetcher.ts | 8 +- admin/src/utils/loginUser.ts | 17 +- admin/src/utils/userActions.ts | 146 ++++++++------ backendV2/routes/admin/apiDataMgmt.route.js | 10 +- .../admin/database/apiDataMgmt.database.js | 6 +- .../admin/database/itemDataMgmt.database.js | 32 ++- .../admin/database/userDataMgmt.database.js | 2 +- backendV2/routes/admin/itemDataMgmt.route.js | 9 +- backendV2/routes/admin/loanDataMgmt.route.js | 2 +- backendV2/routes/admin/userDataMgmt.route.js | 4 +- backendV2/routes/admin/userMgmt.route.js | 25 ++- backendV2/routes/app/loanMgmt.route.js | 4 +- backendV2/routes/app/userMgmt.route.js | 3 +- backendV2/schemeV2.mock_data.sql | 110 +++-------- backendV2/schemeV2.sql | 21 +- backendV2/server.js | 22 ++- backendV2/services/authentication.js | 2 +- docker-compose.yml | 4 +- 26 files changed, 605 insertions(+), 347 deletions(-) diff --git a/admin/src/Layout/Layout.tsx b/admin/src/Layout/Layout.tsx index 1bb9fc0..31e54b5 100644 --- a/admin/src/Layout/Layout.tsx +++ b/admin/src/Layout/Layout.tsx @@ -3,11 +3,7 @@ import { useEffect } from "react"; import Dashboard from "./Dashboard"; import Login from "./Login"; import Cookies from "js-cookie"; - -const API_BASE = - (import.meta as any).env?.VITE_BACKEND_URL || - import.meta.env.VITE_BACKEND_URL || - "http://localhost:8002"; +import { API_BASE } from "@/config/api.config"; const Layout: React.FC = () => { const [isLoggedIn, setIsLoggedIn] = useState(false); @@ -15,12 +11,15 @@ const Layout: React.FC = () => { useEffect(() => { if (Cookies.get("token")) { const verifyToken = async () => { - const response = await fetch(`${API_BASE}/api/verifyToken`, { - method: "GET", - headers: { - Authorization: `Bearer ${Cookies.get("token")}`, - }, - }); + const response = await fetch( + `${API_BASE}/api/admin/user-mgmt/verify-token`, + { + method: "GET", + headers: { + Authorization: `Bearer ${Cookies.get("token")}`, + }, + } + ); if (response.ok) { setIsLoggedIn(true); } else { diff --git a/admin/src/Layout/Sidebar.tsx b/admin/src/Layout/Sidebar.tsx index 05817fe..029ba27 100644 --- a/admin/src/Layout/Sidebar.tsx +++ b/admin/src/Layout/Sidebar.tsx @@ -1,5 +1,7 @@ import React from "react"; +import { useEffect, useState } from "react"; import { Box, Flex, VStack, Heading, Text, Link } from "@chakra-ui/react"; +import { API_BASE } from "@/config/api.config"; type SidebarProps = { viewAusleihen: () => void; @@ -15,10 +17,22 @@ const Sidebar: React.FC = ({ viewUser, viewAPI, }) => { + const [info, setInfo] = useState(null); + + const fetchInfo = async () => { + const response = await fetch(`${API_BASE}/`); + const data = await response.json(); + setInfo(data); + }; + + useEffect(() => { + fetchInfo(); + }, []); + return ( = ({ - © Made with ❤️ by Theis Gaedigk + © Made with ❤️ by Theis Gaedigk + {info ? ( + + + Panel {info?.["admin-panel-info"]?.version ?? "—"} + + + Backend {info?.["backend-info"]?.version ?? "—"} + + + ) : ( + Lade Versionsinfos… + )} diff --git a/admin/src/components/APIKeyTable.tsx b/admin/src/components/APIKeyTable.tsx index f90769d..b6c73d9 100644 --- a/admin/src/components/APIKeyTable.tsx +++ b/admin/src/components/APIKeyTable.tsx @@ -17,17 +17,14 @@ import { useState, useEffect } from "react"; import { deleteAPKey } from "@/utils/userActions"; import AddAPIKey from "./AddAPIKey"; import { formatDateTime } from "@/utils/userFuncs"; - -const API_BASE = - (import.meta as any).env?.VITE_BACKEND_URL || - import.meta.env.VITE_BACKEND_URL || - "http://localhost:8002"; +import { API_BASE } from "@/config/api.config"; type Items = { id: number; - apiKey: string; - user: string; + api_key: string; + entry_name: string; entry_created_at: string; + last_used_at: string | null; }; const APIKeyTable: React.FC = () => { @@ -56,13 +53,17 @@ const APIKeyTable: React.FC = () => { const fetchData = async () => { setIsLoading(true); try { - const response = await fetch(`${API_BASE}/api/apiKeys`, { - method: "GET", - headers: { - Authorization: `Bearer ${Cookies.get("token")}`, - }, - }); + const response = await fetch( + `${API_BASE}/api/admin/api-data/get-api-keys`, + { + method: "GET", + headers: { + Authorization: `Bearer ${Cookies.get("token")}`, + }, + } + ); const data = await response.json(); + console.log(data); return data; } catch (error) { setError("error", "Failed to fetch items", "There is an error"); @@ -159,29 +160,37 @@ const APIKeyTable: React.FC = () => { API Key - Benutzer + Name Eintrag erstellt am + + Zuletzt benutzt am + Aktionen - {items.map((apiKey) => ( - - {apiKey.id} - {apiKey.apiKey} - {apiKey.user} - {formatDateTime(apiKey.entry_created_at)} + {items.map((item) => ( + + {item.id} + {item.api_key} + {item.entry_name} + {formatDateTime(item.entry_created_at)} + + {!item.last_used_at + ? "Nie benutzt" + : formatDateTime(item.last_used_at)} + - + - - + + if (res.success) { + alert( + "success", + "Nutzer erstellt", + "Der Nutzer wurde erfolgreich erstellt." + ); + onClose(); + } else { + alert( + "error", + "Fehler beim Erstellen des Nutzers", + "Es gab einen Fehler beim Erstellen des Nutzers. Vielleicht gibt es bereits einen Nutzer mit diesem Benutzernamen." + ); + onClose(); + } + }} + > + Erstellen + + + + ); }; diff --git a/admin/src/components/ItemTable.tsx b/admin/src/components/ItemTable.tsx index d62e0b8..74bdaae 100644 --- a/admin/src/components/ItemTable.tsx +++ b/admin/src/components/ItemTable.tsx @@ -30,18 +30,17 @@ import { } from "@/utils/userActions"; import AddItemForm from "./AddItemForm"; import { formatDateTime } from "@/utils/userFuncs"; - -const API_BASE = - (import.meta as any).env?.VITE_BACKEND_URL || - import.meta.env.VITE_BACKEND_URL || - "http://localhost:8002"; +import { API_BASE } from "@/config/api.config"; type Items = { id: number; item_name: string; can_borrow_role: string; - inSafe: boolean; + in_safe: boolean; entry_created_at: string; + entry_updated_at: string; + last_borrowed_person: string | null; + currently_borrowing: string | null; }; const ItemTable: React.FC = () => { @@ -82,12 +81,15 @@ const ItemTable: React.FC = () => { const fetchData = async () => { setIsLoading(true); try { - const response = await fetch(`${API_BASE}/api/allItems`, { - method: "GET", - headers: { - Authorization: `Bearer ${Cookies.get("token")}`, - }, - }); + const response = await fetch( + `${API_BASE}/api/admin/item-data/all-items`, + { + method: "GET", + headers: { + Authorization: `Bearer ${Cookies.get("token")}`, + }, + } + ); const data = await response.json(); return data; } catch (error) { @@ -193,6 +195,15 @@ const ItemTable: React.FC = () => { Eintrag erstellt am + + Eintrag aktualisiert am + + + Letzte ausleihende Person + + + Derzeit ausgeliehen von + Aktionen @@ -229,31 +240,34 @@ const ItemTable: React.FC = () => { py={1} gap={2} variant="ghost" - color={item.inSafe ? "green.600" : "red.600"} + color={item.in_safe ? "green.600" : "red.600"} borderWidth="1px" - borderColor={item.inSafe ? "green.300" : "red.300"} + borderColor={item.in_safe ? "green.300" : "red.300"} _hover={{ - bg: item.inSafe ? "green.50" : "red.50", - borderColor: item.inSafe ? "green.400" : "red.400", + bg: item.in_safe ? "green.50" : "red.50", + borderColor: item.in_safe ? "green.400" : "red.400", transform: "translateY(-1px)", shadow: "sm", }} _active={{ transform: "translateY(0)" }} aria-label={ - item.inSafe ? "Mark as not in safe" : "Mark as in safe" + item.in_safe ? "Mark as not in safe" : "Mark as in safe" } > - {item.inSafe ? "Yes" : "No"} + {item.in_safe ? "Yes" : "No"} {formatDateTime(item.entry_created_at)} + {formatDateTime(item.entry_updated_at)} + {item.last_borrowed_person} + {item.currently_borrowing}