From 237f8ae6c29bbf485c312b2fed4d5ab4f99a4eff Mon Sep 17 00:00:00 2001 From: Arne Rief Date: Sat, 20 Dec 2025 14:09:20 +0100 Subject: Map and loading robots --- frontend/src/pages/Dashboard.tsx | 89 +++++++++++++++++++++++++++++++++++++++- frontend/src/pages/Login.tsx | 11 ++--- 2 files changed, 92 insertions(+), 8 deletions(-) (limited to 'frontend/src/pages') diff --git a/frontend/src/pages/Dashboard.tsx b/frontend/src/pages/Dashboard.tsx index f306c53..6b43bf8 100644 --- a/frontend/src/pages/Dashboard.tsx +++ b/frontend/src/pages/Dashboard.tsx @@ -1,5 +1,92 @@ +import { useEffect, useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { FadeLoader } from "react-spinners"; +import { io } from "socket.io-client"; +import CityMap from "../components/CityMap"; +import Header from "../components/Header"; +import API_URL from "../config"; +import "../styles/dashboard.css"; +import type { ErrorResponse } from "../types/error"; +import type { AuthorizedUser } from "../types/login"; +import type { Robot, RobotsResponse } from "../types/robot"; + function Dashboard() { - return

Placeholder

; + const [isLoading, setIsLoading] = useState(true); + const [robots, setRobots] = useState([]); + + const navigate = useNavigate(); + + const userString = localStorage.getItem("user"); + const user: AuthorizedUser = userString ? JSON.parse(userString) : null; + const token = localStorage.getItem("token-robot-tracker"); + + async function handleLogout() { + localStorage.removeItem("token-robot-tracker"); + localStorage.removeItem("user"); + navigate("/login", { replace: true }); + } + + useEffect(() => { + // Additional safety check to protect this page from unauthorized access + if (!token || token === "undefined" || token === "null") { + navigate("/login"); + return; + } + + // Request robot data from backend + async function fetchRobots() { + try { + setIsLoading(true); + + const response = await fetch(`${API_URL}/robots`, { + method: "GET", + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "application/json", + }, + }); + + if (!response.ok) { + const errorData: ErrorResponse = await response.json(); + throw new Error( + errorData.message || + `Failed to load the robots: ${response.status}` + ); + } + + const data: RobotsResponse = await response.json(); + setRobots(data.data); + } catch (error) { + console.error("Failed to load the robots:", error); + } finally { + setIsLoading(false); + } + } + + fetchRobots(); + + // Establish WebSocket connection to backend + const socket = io(API_URL); + + // Listen for real-time robot updates + socket.on("robots_update", (updatedRobots) => { + setRobots(updatedRobots); + }); + + // Cleanup when component unmounts + return () => { + socket.disconnect(); + }; + }, [token, navigate]); + + return isLoading ? ( + + ) : ( +
+ +
+
+ ); } export default Dashboard; diff --git a/frontend/src/pages/Login.tsx b/frontend/src/pages/Login.tsx index 2e99a2f..11b7c11 100644 --- a/frontend/src/pages/Login.tsx +++ b/frontend/src/pages/Login.tsx @@ -3,13 +3,10 @@ import { useNavigate } from "react-router-dom"; import { FadeLoader } from "react-spinners"; import Logo from "../components/Logo"; import API_URL from "../config"; -import "../styles/Button.css"; -import "../styles/Login.css"; -import type { - ErrorResponse, - LoginFormData, - LoginResponse, -} from "../types/login"; +import "../styles/button.css"; +import "../styles/login.css"; +import type { ErrorResponse } from "../types/error"; +import type { LoginFormData, LoginResponse } from "../types/login"; const EMPTY_FORM_DATA: LoginFormData = { email: "", -- cgit v1.2.3