From 3818739c5901cc3f1d4596b24cfe1b827a2eca23 Mon Sep 17 00:00:00 2001 From: Arne Rief Date: Mon, 22 Dec 2025 12:28:33 +0100 Subject: FE Sidebar, create & move requests, BE create controller --- frontend/src/components/Sidebar.tsx | 246 ++++++++++++++++++++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100644 frontend/src/components/Sidebar.tsx (limited to 'frontend/src/components') diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx new file mode 100644 index 0000000..2e66865 --- /dev/null +++ b/frontend/src/components/Sidebar.tsx @@ -0,0 +1,246 @@ +import { + useState, + type ChangeEvent, + type Dispatch, + type SetStateAction, +} from "react"; +import "../styles/Button.css"; +import "../styles/Sidebar.css"; +import type { ErrorResponse } from "../types/error"; +import type { CreateRobotResponse, Robot } from "../types/robot"; + +type ExpandedRobotsState = Record; + +type Props = { + activeSimulation: boolean; + errorMessage: string; + setErrorMessage: Dispatch>; + token: string | null; + robots: Robot[]; + onStartAllRobots: () => Promise; + onStopAllRobots: () => Promise; +}; + +const API_URL = import.meta.env.VITE_API_URL; + +function Sidebar({ + activeSimulation, + errorMessage, + setErrorMessage, + token, + robots, + onStartAllRobots, + onStopAllRobots, +}: Props) { + const [expandedRobots, setExpandedRobots] = useState( + {} + ); + const [isAddingRobot, setIsAddingRobot] = useState(false); + const [newRobotName, setNewRobotName] = useState(""); + const [isSubmitting, setIsSubmitting] = useState(false); + + function toggleRobotHistory(robotId: number) { + setExpandedRobots((prev) => ({ + ...prev, + [robotId]: !prev[robotId], + })); + } + + function handleAddClick() { + setIsAddingRobot(true); + setNewRobotName(""); + setErrorMessage(""); + } + + function handleCancel() { + setIsAddingRobot(false); + setNewRobotName(""); + setErrorMessage(""); + } + + async function handleSubmit() { + if (!newRobotName.trim()) { + setErrorMessage("Please enter a name for the robot."); + return; + } + + setIsSubmitting(true); + setErrorMessage(""); + + try { + const response = await fetch(`${API_URL}/robots`, { + method: "POST", + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ name: newRobotName.trim() }), + }); + + if (!response.ok) { + const errorData: ErrorResponse = await response.json(); + throw new Error( + errorData.message || + `Error creating the new robot: ${response.status}` + ); + } + + const data: CreateRobotResponse = await response.json(); + console.log("Robot created successfully: ", data); + + // Reset form and close input field + setNewRobotName(""); + setIsAddingRobot(false); + } catch (error) { + console.error("Error creating the new robot:", error); + + if (error instanceof Error) { + setErrorMessage(error.message); + } else { + setErrorMessage("Error creating the new robot."); + } + } finally { + setIsSubmitting(false); + } + } + + function handleInputChange(event: ChangeEvent) { + setNewRobotName(event.target.value); + if (errorMessage) { + setErrorMessage(""); + } + } + + return ( +
+
+

Your Robots

+ +
+ + {isAddingRobot && ( +
+ +
+ + +
+
+ )} + + {errorMessage && ( +
{errorMessage}
+ )} + +
+ + + +
+ +
    + {robots.map((robot) => { + const isExpanded = expandedRobots[robot.id]; + + return ( +
  • +

    {robot.name}

    + +

    + Status:{" "} + + {robot.status} + +

    + +

    Position:

    +
      +
    • Lat: {robot.lat}
    • +
    • Lon: {robot.lon}
    • +
    + + + +
    +
      + {robot.robot_positions?.length ? ( + robot.robot_positions.map( + (pos, index) => ( +
    • + {`Lat: ${pos.lat}, Lon: ${pos.lon}`} +
    • + ) + ) + ) : ( +
    • + No previous positions. +
    • + )} +
    +
    +
  • + ); + })} +
+
+ ); +} + +export default Sidebar; + -- cgit v1.2.3