From 723d7ed724cf248281264ab4f1984a15e90204a0 Mon Sep 17 00:00:00 2001 From: Ahnaf Kamal Date: Tue, 29 Sep 2026 00:11:02 -0400 Subject: [PATCH 1/5] refactor --- src/components/panels/MosaicDashboard.tsx | 279 ++++------------------ 1 file changed, 50 insertions(+), 229 deletions(-) diff --git a/src/components/panels/MosaicDashboard.tsx b/src/components/panels/MosaicDashboard.tsx index 176d44d..3225456 100644 --- a/src/components/panels/MosaicDashboard.tsx +++ b/src/components/panels/MosaicDashboard.tsx @@ -34,82 +34,52 @@ import DriveThrottlePanel from './DriveThrottlePanel'; import { ROVER_IP } from '@/constants'; -type TileType = - | 'mapView' - | 'rosMonitor' - | 'waypointList' - | 'videoControls' - | 'rtpStats' - | 'gasSensor' - | 'orientationDisplay' - | 'goalSetter' - | 'networkHealthMonitor' - | 'motorStatusPanel' - | 'driveThrottlePanel' - | 'nodeStatusPanel' - | 'antennaControlPanel' - | 'scienceControlPanel' - | 'scienceSensorPanel' - | 'pdbRails' - | 'armControlPanel' - | 'webRTCClient' - | 'timerPanel' - | 'headlightControlPanel' - | 'morseTransmissionPanel' - | 'echoPanel' - ; +interface TileDefinition { + title: string; // this is what shows up in the 'Pick tile...' drop down + render: () => ReactElement; // renders the body of the tile +} +// Registry of all the tiles. After importing, put them here. Entry order is kept and will reflect on the Pick tile... dropdown +const TILE_REGISTRY = { + mapView: { + title: 'Map View', + render: () => ( +
+ +
+ ), + }, + rosMonitor: { title: 'System Telemetry', render: () => }, + networkHealthMonitor: { title: 'Connection Health', render: () => }, + orientationDisplay: { title: 'Rover Orientation', render: () => }, + videoControls: { title: 'Video Stream', render: () => }, + rtpStats: { title: 'RTP Statistics', render: () => }, + driveThrottlePanel: { title: 'Drive Throttle', render: () => }, + waypointList: { title: 'Waypoint List', render: () => }, + gasSensor: { title: 'Science', render: () => }, + goalSetter: { title: 'Nav2', render: () => }, + motorStatusPanel: { title: 'Motor Status', render: () => }, + nodeStatusPanel: { title: 'Node Status', render: () => }, + antennaControlPanel: { title: 'Antenna Control', render: () => }, + scienceControlPanel: { title: 'Science Motor Control', render: () => }, + scienceSensorPanel: { title: 'Science Sensor Readouts', render: () => }, + pdbRails: { title: 'PDB Rails', render: () => }, + armControlPanel: { title: 'Arm Control', render: () => }, + webRTCClient: { + title: 'WebRTC Client', + render: () => , + }, + timerPanel: { title: 'Multi-Timer', render: () => }, + headlightControlPanel: { title: 'Headlights', render: () => }, + morseTransmissionPanel: { title: 'Morse Transmission', render: () => }, + echoPanel: { title: 'Topic Echo', render: () => }, +} satisfies Record; + +type TileType = keyof typeof TILE_REGISTRY; type TileId = `${TileType}:${number}`; -const TILE_DISPLAY_NAMES: Record = { - mapView: 'Map View', - rosMonitor: 'System Telemetry', - waypointList: 'Waypoint List', - videoControls: 'Video Stream', - rtpStats: 'RTP Statistics', - driveThrottlePanel: 'Drive Throttle', - gasSensor: 'Science', - orientationDisplay: 'Rover Orientation', - goalSetter: 'Nav2', - networkHealthMonitor: 'Connection Health', - motorStatusPanel: 'Motor Status', - nodeStatusPanel: 'Node Status', - antennaControlPanel: 'Antenna Control', - scienceControlPanel: 'Science Motor Control', - scienceSensorPanel: 'Science Sensor Readouts', - pdbRails: 'PDB Rails', - armControlPanel: 'Arm Control', - webRTCClient: 'WebRTC Client', - timerPanel: 'Multi-Timer', - headlightControlPanel: 'Headlights', - morseTransmissionPanel: 'Morse Transmission', - echoPanel: 'Topic Echo', -}; - -const ALL_TILE_TYPES: TileType[] = [ - 'mapView', - 'rosMonitor', - 'networkHealthMonitor', - 'orientationDisplay', - 'videoControls', - 'rtpStats', - 'driveThrottlePanel', - 'waypointList', - 'gasSensor', - 'goalSetter', - 'motorStatusPanel', - 'nodeStatusPanel', - 'antennaControlPanel', - 'scienceControlPanel', - 'scienceSensorPanel', - 'pdbRails', - 'armControlPanel', - 'webRTCClient', - 'timerPanel', - 'headlightControlPanel', - 'morseTransmissionPanel', - 'echoPanel', -]; +// Object.keys preserves insertion order for string keys +const ALL_TILE_TYPES = Object.keys(TILE_REGISTRY) as TileType[]; function tileTypeOf(id: TileId): TileType { return id.split(':', 1)[0] as TileType; @@ -281,7 +251,7 @@ const Controls = memo<{ {ALL_TILE_TYPES.map((t) => ( ))} @@ -353,165 +323,16 @@ const MosaicDashboard: React.FC = () => { path: MosaicPath; additionalControls: ReactElement; } = { - title: TILE_DISPLAY_NAMES[type], + title: TILE_REGISTRY[type]?.title ?? type, path, additionalControls: controls, }; - switch (type) { - case 'mapView': - return ( - -
- -
-
- ); - - case 'waypointList': - return ( - - - - ); - - case 'videoControls': - return ( - - - - ); - - case 'rtpStats': - return ( - - - - ); - - case 'driveThrottlePanel': - return ( - - - - ); - - case 'rosMonitor': - return ( - - - - ); - - case 'networkHealthMonitor': - return ( - - - - ); - - case 'orientationDisplay': - return ( - - - - ); - - case 'gasSensor': - return ( - - - - ); - - case 'goalSetter': - return ( - - - - ); - - case 'motorStatusPanel': - return ( - - - - ); - - case 'nodeStatusPanel': - return ( - - - - ); - - case 'antennaControlPanel': - return ( - - - - ); - case 'scienceControlPanel': - return ( - - - - ); - case 'scienceSensorPanel': - return( - - - - ); - case 'pdbRails': - return( - - - - ); - case 'armControlPanel': - return( - - - - ); - case 'webRTCClient': - return( - - - - ); - case 'timerPanel': - return ( - - - - ); - case 'headlightControlPanel': - return ( - - - - ); - - case 'morseTransmissionPanel': - return ( - - - - ); - case 'echoPanel': - return ( - - - - ); - - default: - return
Unknown tile
; - } + return ( + + {TILE_REGISTRY[type]?.render() ??
Unknown tile
} +
+ ); }; if (!mosaicLayout) return null; From 2650e46f9275bcc2a7c81827ca5defe9b2c4868a Mon Sep 17 00:00:00 2001 From: Ahnaf Kamal Date: Tue, 29 Sep 2026 09:24:15 -0400 Subject: [PATCH 2/5] remove unnecessary null safety --- src/components/panels/MosaicDashboard.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/panels/MosaicDashboard.tsx b/src/components/panels/MosaicDashboard.tsx index 3225456..1d0a446 100644 --- a/src/components/panels/MosaicDashboard.tsx +++ b/src/components/panels/MosaicDashboard.tsx @@ -39,7 +39,8 @@ interface TileDefinition { render: () => ReactElement; // renders the body of the tile } -// Registry of all the tiles. After importing, put them here. Entry order is kept and will reflect on the Pick tile... dropdown +// Registry of all the tiles. After importing, put them here. +// Entry order is kept and will reflect on the Pick tile... dropdown const TILE_REGISTRY = { mapView: { title: 'Map View', @@ -323,14 +324,14 @@ const MosaicDashboard: React.FC = () => { path: MosaicPath; additionalControls: ReactElement; } = { - title: TILE_REGISTRY[type]?.title ?? type, + title: TILE_REGISTRY[type].title, path, additionalControls: controls, }; return ( - {TILE_REGISTRY[type]?.render() ??
Unknown tile
} + {TILE_REGISTRY[type].render()}
); }; From 67817c9b130bdd0c4819dbe1761c94e650522aae Mon Sep 17 00:00:00 2001 From: Ahnaf Kamal Date: Tue, 29 Sep 2026 09:48:16 -0400 Subject: [PATCH 3/5] remove gas sensor again --- src/components/panels/GasSensor.tsx | 154 ---------------------- src/components/panels/MosaicDashboard.tsx | 2 - 2 files changed, 156 deletions(-) delete mode 100644 src/components/panels/GasSensor.tsx diff --git a/src/components/panels/GasSensor.tsx b/src/components/panels/GasSensor.tsx deleted file mode 100644 index 60656ee..0000000 --- a/src/components/panels/GasSensor.tsx +++ /dev/null @@ -1,154 +0,0 @@ -'use client'; -import React, { useEffect, useState } from 'react'; -import ROSLIB from 'roslib'; -import { useROS } from '@/ros/ROSContext'; - -interface GasSensorReading { - temperature_c: number | null; - pressure_pa: number | null; - humidity_rh: number | null; - co2_ppm: number | null; - tvoc_ppb: number | null; - ozone_ppb: number | null; - hydrogen_ppb: number | null; - geiger: number | null; - } - -const SciencePanel: React.FC = () => { - const { ros } = useROS(); - const [data, setData] = useState ({ - temperature_c: null, - pressure_pa: null, - humidity_rh: null, - co2_ppm: null, - tvoc_ppb: null, - ozone_ppb: null, - hydrogen_ppb: null, - geiger: null, - }); - - useEffect(() => { - if (!ros) return; - - const gasSensorTopic = new ROSLIB.Topic({ - ros, - name: '/gas_sensor', - messageType: 'interfaces/msg/GasSensorReading', - }); - const ozoneTopic = new ROSLIB.Topic({ - ros, - name: '/ozone_sensor', - messageType: 'std_msgs/Float64', - }); - const hydrogenTopic = new ROSLIB.Topic({ - ros, - name: '/hydrogen_sensor', - messageType: 'std_msgs/Float64', - }); - const geigerTopic = new ROSLIB.Topic({ - ros, - name: '/geiger_counts', - messageType: 'std_msgs/Float32', - }); - - const handleGasReading = (msg: any) => { - setData(prev => ({ - ...prev, - temperature_c: msg.temperature_c, - pressure_pa: msg.pressure_pa, - humidity_rh: msg.humidity_rh, - co2_ppm: msg.co2_ppm, - tvoc_ppb: msg.tvoc_ppb, - })); - }; - - const handleOzoneReading = (msg: any) => { - setData(prev => ({ - ...prev, - ozone_ppb: msg.data, - })); - }; - - const handleHydrogenReading = (msg: any) => { - setData(prev => ({ - ...prev, - hydrogen_ppb: msg.data, - })); - }; - const handleGeigerReading = (msg: any) => { - setData(prev => ({ - ...prev, - geiger: msg.data, - })); - }; - - gasSensorTopic.subscribe(handleGasReading); - ozoneTopic.subscribe(handleOzoneReading); - hydrogenTopic.subscribe(handleHydrogenReading); - geigerTopic.subscribe(handleGeigerReading); - - return () => { - gasSensorTopic.unsubscribe(handleGasReading); - ozoneTopic.unsubscribe(handleOzoneReading); - hydrogenTopic.unsubscribe(handleHydrogenReading); - geigerTopic.unsubscribe(handleGeigerReading); - }; -}, [ros]); - -return ( -
-
    -
  • -
    -
    - - {data.temperature_c !== null ? data.temperature_c.toFixed(1) : 'N/A'}°C -
    -
    - - {data.pressure_pa !== null ? data.pressure_pa.toFixed(0) : 'N/A'} Pa -
    -
    - - {data.humidity_rh !== null ? data.humidity_rh.toFixed(1) : 'N/A'}% -
    -
    - - {data.co2_ppm !== null ? data.co2_ppm : 'N/A'} ppm -
    -
    - - {data.tvoc_ppb !== null ? data.tvoc_ppb : 'N/A'} ppb -
    -
    - - {data.ozone_ppb !== null ? data.ozone_ppb.toFixed(2) : 'N/A'} ppb -
    -
    - - {data.hydrogen_ppb !== null ? data.hydrogen_ppb.toFixed(2) : 'N/A'} ppb -
    -
    - - {data.geiger !== null ? data.geiger.toFixed(2) : 'N/A'} uSv/h -
    -
    -
  • -
-
- ); -}; - export default SciencePanel; \ No newline at end of file diff --git a/src/components/panels/MosaicDashboard.tsx b/src/components/panels/MosaicDashboard.tsx index 1d0a446..0bdfe07 100644 --- a/src/components/panels/MosaicDashboard.tsx +++ b/src/components/panels/MosaicDashboard.tsx @@ -14,7 +14,6 @@ import WaypointList from './WaypointList'; import SystemTelemetryPanel from './SystemTelemetryPanel'; import OrientationDisplayPanel from './OrientationDisplayPanel'; import GoalSetterPanel from './GoalSetterPanel'; -import GasSensor from './GasSensor'; import NetworkHealthTelemetryPanel from './NetworkHealthTelemetryPanel'; import VideoControls from './VideoControls'; import MotorStatusPanel from './MotorStatusPanel'; @@ -57,7 +56,6 @@ const TILE_REGISTRY = { rtpStats: { title: 'RTP Statistics', render: () => }, driveThrottlePanel: { title: 'Drive Throttle', render: () => }, waypointList: { title: 'Waypoint List', render: () => }, - gasSensor: { title: 'Science', render: () => }, goalSetter: { title: 'Nav2', render: () => }, motorStatusPanel: { title: 'Motor Status', render: () => }, nodeStatusPanel: { title: 'Node Status', render: () => }, From d370d1be69d7ce9cae55e468daee88faa98c0842 Mon Sep 17 00:00:00 2001 From: Ahnaf Kamal Date: Tue, 29 Sep 2026 14:35:22 -0400 Subject: [PATCH 4/5] removed comments + safety --- src/components/panels/MosaicDashboard.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/components/panels/MosaicDashboard.tsx b/src/components/panels/MosaicDashboard.tsx index 0bdfe07..dd125d4 100644 --- a/src/components/panels/MosaicDashboard.tsx +++ b/src/components/panels/MosaicDashboard.tsx @@ -34,12 +34,10 @@ import DriveThrottlePanel from './DriveThrottlePanel'; import { ROVER_IP } from '@/constants'; interface TileDefinition { - title: string; // this is what shows up in the 'Pick tile...' drop down - render: () => ReactElement; // renders the body of the tile + title: string; + render: () => ReactElement; } -// Registry of all the tiles. After importing, put them here. -// Entry order is kept and will reflect on the Pick tile... dropdown const TILE_REGISTRY = { mapView: { title: 'Map View', @@ -322,14 +320,14 @@ const MosaicDashboard: React.FC = () => { path: MosaicPath; additionalControls: ReactElement; } = { - title: TILE_REGISTRY[type].title, + title: TILE_REGISTRY[type]?.title ?? type, path, additionalControls: controls, }; return ( - {TILE_REGISTRY[type].render()} + {TILE_REGISTRY[type]?.render() ??
Unknown tile
}
); }; From 045da6f44a3905f1470c622d8eb70e635f29b433 Mon Sep 17 00:00:00 2001 From: Ahnaf Kamal Date: Tue, 29 Sep 2026 18:51:51 -0400 Subject: [PATCH 5/5] unknown tile styling --- src/components/panels/MosaicDashboard.tsx | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/components/panels/MosaicDashboard.tsx b/src/components/panels/MosaicDashboard.tsx index dd125d4..0bf130c 100644 --- a/src/components/panels/MosaicDashboard.tsx +++ b/src/components/panels/MosaicDashboard.tsx @@ -327,7 +327,21 @@ const MosaicDashboard: React.FC = () => { return ( - {TILE_REGISTRY[type]?.render() ??
Unknown tile
} + {TILE_REGISTRY[type]?.render() ?? ( +
+ Unknown tile +
+ )}
); };