diff --git a/admin/src/Layout/Dashboard.tsx b/admin/src/Layout/Dashboard.tsx index 040254c..7d78cdf 100644 --- a/admin/src/Layout/Dashboard.tsx +++ b/admin/src/Layout/Dashboard.tsx @@ -4,7 +4,6 @@ import { Box, Heading, Text, Flex, Button } from "@chakra-ui/react"; import Sidebar from "./Sidebar"; import UserTable from "../components/UserTable"; import ItemTable from "../components/ItemTable"; -import LockerTable from "../components/LockerTable"; import LoanTable from "../components/LoanTable"; import { MoveLeft } from "lucide-react"; @@ -67,7 +66,6 @@ const Dashboard: React.FC = ({ onLogout }) => { {activeView === "User" && } {activeView === "Ausleihen" && } {activeView === "Gegenstände" && } - {activeView === "Schließfächer" && } diff --git a/admin/src/Layout/Sidebar.tsx b/admin/src/Layout/Sidebar.tsx index 37e2d27..69e5587 100644 --- a/admin/src/Layout/Sidebar.tsx +++ b/admin/src/Layout/Sidebar.tsx @@ -59,15 +59,6 @@ const Sidebar: React.FC = ({ > Gegenstände - - Schließfächer - diff --git a/admin/src/components/AddItemForm.tsx b/admin/src/components/AddItemForm.tsx new file mode 100644 index 0000000..76ec615 --- /dev/null +++ b/admin/src/components/AddItemForm.tsx @@ -0,0 +1,84 @@ +import React from "react"; +import { Button, Card, Field, Input, Stack } from "@chakra-ui/react"; +import { createItem } from "@/utils/userActions"; + +type AddItemFormProps = { + onClose: () => void; + alert: ( + status: "success" | "error", + message: string, + description: string + ) => void; +}; + +const AddItemForm: React.FC = ({ onClose, alert }) => { + return ( +
+ + + Neuen Gegenstand erstellen + + Füllen Sie das folgende Formular aus, um einen Gegenstand zu + erstellen. + + + + + + Gegenstandsname + + + + Ausleih-Berechtigung (Rolle) + + + + + + + + + +
+ ); +}; + +export default AddItemForm; diff --git a/admin/src/components/ItemTable.tsx b/admin/src/components/ItemTable.tsx index 151a05c..a1c9714 100644 --- a/admin/src/components/ItemTable.tsx +++ b/admin/src/components/ItemTable.tsx @@ -1,7 +1,265 @@ import React from "react"; +import { + Table, + Spinner, + Text, + VStack, + Button, + HStack, + IconButton, + Heading, + Icon, + Tag, +} from "@chakra-ui/react"; +import { Tooltip } from "@/components/ui/tooltip"; +import MyAlert from "./myChakra/MyAlert"; +import { + Trash2, + RefreshCcwDot, + CirclePlus, + CheckCircle2, + XCircle, +} from "lucide-react"; +import Cookies from "js-cookie"; +import { useState, useEffect } from "react"; +import { deleteItem } from "@/utils/userActions"; +import AddItemForm from "./AddItemForm"; + +type Items = { + id: number; + item_name: string; + can_borrow_role: string; + inSafe: boolean; + entry_created_at: string; +}; const ItemTable: React.FC = () => { - return <>Item Table; + const [items, setItems] = useState([]); + const [errorStatus, setErrorStatus] = useState<"error" | "success">("error"); + const [errorMessage, setErrorMessage] = useState(""); + const [errorDsc, setErrorDsc] = useState(""); + const [isError, setIsError] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [reload, setReload] = useState(false); + const [addForm, setAddForm] = useState(false); + + const setError = ( + status: "error" | "success", + message: string, + description: string + ) => { + setIsError(false); + setErrorStatus(status); + setErrorMessage(message); + setErrorDsc(description); + setIsError(true); + }; + + useEffect(() => { + const fetchData = async () => { + setIsLoading(true); + try { + const response = await fetch("http://localhost:8002/api/allItems", { + method: "GET", + headers: { + Authorization: `Bearer ${Cookies.get("token")}`, + }, + }); + const data = await response.json(); + return data; + } catch (error) { + setError("error", "Failed to fetch items", "There is an error"); + } finally { + setIsLoading(false); + } + }; + fetchData().then((data) => { + if (Array.isArray(data)) { + setItems(data); + } + }); + }, [reload]); + + return ( + <> + {/* Action toolbar */} + + + setReload(!reload)} + > + + + + + + + + + {/* End action toolbar */} + + + Gegenstände + + {isError && ( + + )} + {isLoading && ( + + + Loading... + + )} + {addForm && ( + { + setAddForm(false); + setReload(!reload); + }} + alert={setError} + /> + )} + + + + + + # + + + Gegenstand + + + Ausleih Berechtigung + + + Im Schließfach + + + Eintrag erstellt am + + + Aktionen + + + + + {items.map((item) => ( + + {item.id} + {item.item_name} + {item.can_borrow_role} + + {item.inSafe ? ( + + + + Yes + + + ) : ( + + + + No + + + )} + + {item.entry_created_at} + + + + + ))} + + + + ); }; export default ItemTable; diff --git a/admin/src/components/LoanTable.tsx b/admin/src/components/LoanTable.tsx index 082a783..43dcb47 100644 --- a/admin/src/components/LoanTable.tsx +++ b/admin/src/components/LoanTable.tsx @@ -1,18 +1,21 @@ import React from "react"; import { Table, - Code, - VStack, Spinner, Text, - Heading, + VStack, Button, + HStack, + IconButton, + Heading, + Code, } from "@chakra-ui/react"; +import { Tooltip } from "@/components/ui/tooltip"; import { useState, useEffect } from "react"; import Cookies from "js-cookie"; import MyAlert from "./myChakra/MyAlert"; import { formatDateTime } from "@/utils/userFuncs"; -import { Trash2 } from "lucide-react"; +import { Trash2, RefreshCcwDot } from "lucide-react"; import { deleteLoan } from "@/utils/userActions"; const LoanTable: React.FC = () => { @@ -22,6 +25,7 @@ const LoanTable: React.FC = () => { const [errorDsc, setErrorDsc] = useState(""); const [isError, setIsError] = useState(false); const [isLoading, setIsLoading] = useState(false); + const [reload, setReload] = useState(false); const setError = ( status: "error" | "success", @@ -70,13 +74,39 @@ const LoanTable: React.FC = () => { setItems(data); } }); - }, []); + }, [reload]); return ( <> Ausleihen + + {/* Action toolbar */} + + + setReload(!reload)} + > + + + + + {/* End action toolbar */} + {isError && ( { - return <>Locker Table; -}; - -export default LockerTable; diff --git a/admin/src/utils/userActions.ts b/admin/src/utils/userActions.ts index a5d280b..4397755 100644 --- a/admin/src/utils/userActions.ts +++ b/admin/src/utils/userActions.ts @@ -93,3 +93,47 @@ export const deleteLoan = async (loanId: number) => { return { success: false }; } }; + +export const deleteItem = async (itemId: number) => { + try { + const response = await fetch( + `http://localhost:8002/api/deleteItem/${itemId}`, + { + method: "DELETE", + headers: { + Authorization: `Bearer ${Cookies.get("token")}`, + }, + } + ); + if (!response.ok) { + throw new Error("Failed to delete item"); + } + return { success: true }; + } catch (error) { + console.error("Error deleting item:", error); + return { success: false }; + } +}; + +export const createItem = async ( + item_name: string, + can_borrow_role: number +) => { + try { + const response = await fetch(`http://localhost:8002/api/createItem`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${Cookies.get("token")}`, + }, + body: JSON.stringify({ item_name, can_borrow_role }), + }); + if (!response.ok) { + throw new Error("Failed to create item"); + } + return { success: true }; + } catch (error) { + console.error("Error creating item:", error); + return { success: false }; + } +}; diff --git a/backend/routes/api.js b/backend/routes/api.js index eaad0a8..a4b12c1 100644 --- a/backend/routes/api.js +++ b/backend/routes/api.js @@ -15,6 +15,9 @@ import { handleEdit, createUser, getAllLoans, + getAllItems, + deleteItemID, + createItem, } from "../services/database.js"; import { authenticate, generateToken } from "../services/tokenService.js"; const router = express.Router(); @@ -247,4 +250,30 @@ router.get("/allLoans", authenticate, async (req, res) => { return res.status(500).json({ message: "Failed to fetch loans" }); }); +router.get("/allItems", authenticate, async (req, res) => { + const result = await getAllItems(); + if (result.success) { + return res.status(200).json(result.data); + } + return res.status(500).json({ message: "Failed to fetch items" }); +}); + +router.delete("/deleteItem/:id", authenticate, async (req, res) => { + const itemId = req.params.id; + const result = await deleteItemID(itemId); + if (result.success) { + return res.status(200).json({ message: "Item deleted successfully" }); + } + return res.status(500).json({ message: "Failed to delete item" }); +}); + +router.post("/createItem", authenticate, async (req, res) => { + const { item_name, can_borrow_role } = req.body || {}; + const result = await createItem(item_name, can_borrow_role); + if (result.success) { + return res.status(201).json({ message: "Item created successfully" }); + } + return res.status(500).json({ message: "Failed to create item" }); +}); + export default router; diff --git a/backend/services/database.js b/backend/services/database.js index c89ee74..d284d8a 100644 --- a/backend/services/database.js +++ b/backend/services/database.js @@ -364,3 +364,24 @@ export const getAllLoans = async () => { if (result.length > 0) return { success: true, data: result }; return { success: false }; }; + +export const getAllItems = async () => { + const [result] = await pool.query("SELECT * FROM items"); + if (result.length > 0) return { success: true, data: result }; + return { success: false }; +}; + +export const deleteItemID = async (itemId) => { + const [result] = await pool.query("DELETE FROM items WHERE id = ?", [itemId]); + if (result.affectedRows > 0) return { success: true }; + return { success: false }; +}; + +export const createItem = async (item_name, can_borrow_role) => { + const [result] = await pool.query( + "INSERT INTO items (item_name, can_borrow_role) VALUES (?, ?)", + [item_name, can_borrow_role] + ); + if (result.affectedRows > 0) return { success: true }; + return { success: false }; +};