summaryrefslogtreecommitdiff
path: root/frontend/src/pages
diff options
context:
space:
mode:
authorArne Rief <riearn@proton.me>2025-12-20 14:09:20 +0100
committerArne Rief <riearn@proton.me>2025-12-20 14:09:20 +0100
commit237f8ae6c29bbf485c312b2fed4d5ab4f99a4eff (patch)
tree238e82a6dc22372c2f9bb8504ab9160be49bd4f3 /frontend/src/pages
parent655ec610fcce8dd7748f10772d520bdff4f7c78e (diff)
Map and loading robots
Diffstat (limited to 'frontend/src/pages')
-rw-r--r--frontend/src/pages/Dashboard.tsx89
-rw-r--r--frontend/src/pages/Login.tsx11
2 files changed, 92 insertions, 8 deletions
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 <h1>Placeholder</h1>;
+ const [isLoading, setIsLoading] = useState<boolean>(true);
+ const [robots, setRobots] = useState<Robot[]>([]);
+
+ 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 ? (
+ <FadeLoader />
+ ) : (
+ <div className="dashboard-page">
+ <CityMap robots={robots} />
+ <Header user={user} logout={handleLogout} />
+ </div>
+ );
}
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: "",