diff --git a/FrontendV2/src/App.tsx b/FrontendV2/src/App.tsx index b1d32d2..3efeed6 100644 --- a/FrontendV2/src/App.tsx +++ b/FrontendV2/src/App.tsx @@ -10,6 +10,7 @@ import { setIsLoggedInAtom } from "@/states/Atoms"; import { UserContext, type User } from "./states/Context"; import { triggerLogoutAtom } from "@/states/Atoms"; import { MyLoansPage } from "./pages/MyLoansPage"; +import Landingpage from "./pages/Landingpage"; const API_BASE = (import.meta as any).env?.VITE_BACKEND_URL || @@ -53,6 +54,7 @@ function App() { }> } /> } /> + } /> } /> diff --git a/FrontendV2/src/pages/Landingpage.tsx b/FrontendV2/src/pages/Landingpage.tsx new file mode 100644 index 0000000..8102816 --- /dev/null +++ b/FrontendV2/src/pages/Landingpage.tsx @@ -0,0 +1,245 @@ +import React, { useEffect, useState } from "react"; +import { + Spinner, + Text, + VStack, + Table, + Heading, + HStack, + Card, + SimpleGrid, + Button, +} from "@chakra-ui/react"; +import { Lock, LockOpen } from "lucide-react"; +import MyAlert from "@/components/myChakra/MyAlert"; + +export const formatDateTime = (value: string | null | undefined) => { + if (!value) return "N/A"; + const m = value.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})/); + if (!m) return "N/A"; + const [, y, M, d, h, min] = m; + return `${d}.${M}.${y} ${h}:${min} Uhr`; +}; + +const API_BASE = + (import.meta as any).env?.VITE_BACKEND_URL || + import.meta.env.VITE_BACKEND_URL || + "http://localhost:8002"; + +type Loan = { + id: number; + username: string; + start_date: string; + end_date: string; + returned_date: string | null; + take_date: string | null; + loaned_items_name: string[] | string; +}; + +type Device = { + id: number; + item_name: string; + can_borrow_role: string; + inSafe: number; + entry_created_at: string; +}; + +const Landingpage: React.FC = () => { + const [isLoading, setIsLoading] = useState(false); + const [loans, setLoans] = useState([]); + const [devices, setDevices] = useState([]); + const [isError, setIsError] = useState(false); + const [errorStatus, setErrorStatus] = useState<"error" | "success">("error"); + const [errorMessage, setErrorMessage] = useState(""); + const [errorDsc, setErrorDsc] = useState(""); + + 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 loanRes = await fetch(`${API_BASE}/apiV2/allLoans`); + const loanData = await loanRes.json(); + if (Array.isArray(loanData)) { + setLoans(loanData); + } else { + setError( + "error", + "Fehler beim Laden", + "Unerwartetes Datenformat erhalten. (Ausleihen)" + ); + } + + const deviceRes = await fetch(`${API_BASE}/apiV2/allItems`); + const deviceData = await deviceRes.json(); + if (Array.isArray(deviceData)) { + setDevices(deviceData); + } else { + setError( + "error", + "Fehler beim Laden", + "Unerwartetes Datenformat erhalten. (Geräte)" + ); + } + } catch (e) { + setError( + "error", + "Fehler beim Laden", + "Die Ausleihen konnten nicht geladen werden." + ); + } finally { + setIsLoading(false); + } + }; + fetchData(); + }, []); + + return ( + <> + + Matthias-Claudius-Schule Technik + + + + Alle Ausleihen + + + {isError && ( + + )} + + {isLoading && ( + + + Loading... + + )} + + {!isLoading && ( + + + + + # + + + Benutzername + + + Startdatum + + + Enddatum + + + Ausgeliehene Artikel + + + Rückgabedatum + + + Ausleihdatum + + + + + {loans.map((loan) => ( + + {loan.id} + {loan.username} + {formatDateTime(loan.start_date)} + {formatDateTime(loan.end_date)} + + {Array.isArray(loan.loaned_items_name) + ? loan.loaned_items_name.join(", ") + : loan.loaned_items_name} + + {formatDateTime(loan.returned_date)} + {formatDateTime(loan.take_date)} + + ))} + + + )} + + {!isLoading && loans.length === 0 && !isError && ( + + Keine Ausleihen vorhanden. + + )} + + + Alle Geräte + + + {/* Responsive Grid mit gleich hohen Karten */} + + {devices.map((device) => ( + + + {device.inSafe ? : } + {device.item_name} + + + Ausleihrolle: {device.can_borrow_role} + + + ))} + + + + Legende: + + + + + + ); +}; + +export default Landingpage; diff --git a/FrontendV2/src/utils/ProtectedRoutes.tsx b/FrontendV2/src/utils/ProtectedRoutes.tsx index d152733..923434b 100644 --- a/FrontendV2/src/utils/ProtectedRoutes.tsx +++ b/FrontendV2/src/utils/ProtectedRoutes.tsx @@ -1,9 +1,20 @@ -import { Navigate, Outlet } from "react-router-dom"; -import { useAtom } from "jotai"; -import { setIsLoggedInAtom } from "@/states/Atoms"; +import { Navigate, Outlet, useLocation } from "react-router-dom"; +import Cookies from "js-cookie"; +import { useContext } from "react"; +import { UserContext } from "@/states/Context"; export const ProtectedRoutes = () => { - const [isLoggedIn] = useAtom(setIsLoggedInAtom); + const user = useContext(UserContext); + const location = useLocation(); + const hasToken = Boolean(Cookies.get("token")); - return isLoggedIn ? : ; + if (hasToken && !user) { + return null; + } + + return user ? ( + + ) : ( + + ); };