Skip to content

Commit 60733e0

Browse files
committed
feat(console): asset onboarding/retire, health, coverage + ETA layers; Summit.OS WS/HTTP integration - API: add POST /api/v1/nodes/register, DELETE /api/v1/nodes/{id}, GET /api/v1/coverage; normalize paths to /api/v1/* - State/WS: asset store w/ ONLINE→STALE→OFFLINE via heartbeats; singleton timer w/ cleanup; retire marks shared state; coverage refresh on coverage_updated - Map: coverage layer (client-side union via @turf/turf) + hover tooltip (id, DEM, updated_at); ETA isochrones for drones/UGVs w/ separate toggles; auto-zoom event; remove mock telemetry trails - UI: Add Tower inline form (optimistic zoom + coverage refresh); Retire action; toast notifications replace alert(); settings panel (DEM version, tower FOV presets, bitrate cap, geofence, mission risk gate) - Chore: add @turf/turf dependency; remove unused wsUrl Env: - NEXT_PUBLIC_API_URL, NEXT_PUBLIC_WS_URL, NEXT_PUBLIC_MQTT_WS_URL - Optional: NEXT_PUBLIC_DRONE_SPEED_MS, NEXT_PUBLIC_UGV_SPEED_MS, NEXT_PUBLIC_STALE_MIN, NEXT_PUBLIC_OFFLINE_MIN Refs: WildFire Ops implementation prompt
1 parent f725689 commit 60733e0

13 files changed

Lines changed: 819 additions & 253 deletions

File tree

apps/console/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030
"lucide-react": "^0.303.0",
3131
"paho-mqtt": "^1.1.0",
3232
"recharts": "^2.8.0",
33-
"date-fns": "^2.30.0"
33+
"date-fns": "^2.30.0",
34+
"@turf/turf": "^6.5.0"
3435
},
3536
"devDependencies": {
3637
"@types/node": "^20.10.0",

apps/console/src/app/page.tsx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,17 @@ import {
2222
Menu,
2323
X,
2424
Layers,
25-
Camera
25+
Camera,
26+
Settings
2627
} from 'lucide-react'
2728
import { AlertsPanel } from '../components/panels/AlertsPanel'
2829
import { DevicesPanel } from '../components/panels/DevicesPanel'
2930
import { MissionsPanel } from '../components/panels/MissionsPanel'
3031
import { DroneControlPanel } from '../components/controls/DroneControlPanel'
3132
import { KOFAMissionPanel } from '../components/controls/KOFAMissionPanel'
3233
import { VideoFeedPanel } from '../components/panels/VideoFeedPanel'
34+
import { SettingsPanel } from '../components/panels/SettingsPanel'
35+
import { ToastProvider } from '../components/ui/Toast'
3336
import { useSummitConnection } from '../hooks/useSummit'
3437

3538
const queryClient = new QueryClient()
@@ -193,6 +196,7 @@ function HomePageContent() {
193196

194197
return (
195198
<QueryClientProvider client={queryClient}>
199+
<ToastProvider>
196200
<div className="min-h-screen bg-dark-950 text-dark-100">
197201
{/* Tactical Header */}
198202
<div className="tactical-header border-b border-dark-700 bg-gradient-to-r from-dark-800 to-dark-900">
@@ -364,6 +368,15 @@ function HomePageContent() {
364368
<Radio className="w-5 h-5 text-tactical-400" />
365369
<span className="text-sm font-mono text-tactical-300">MISSIONS</span>
366370
</button>
371+
<button
372+
onClick={() => setActivePanel('settings')}
373+
className={`w-full flex items-center gap-3 px-3 py-3 rounded-lg transition-all ${
374+
activePanel === 'settings' ? 'bg-tactical-500/20 border border-tactical-500/30 shadow-glow' : 'hover:bg-dark-800'
375+
}`}
376+
>
377+
<Settings className="w-5 h-5 text-tactical-400" />
378+
<span className="text-sm font-mono text-tactical-300">SETTINGS</span>
379+
</button>
367380
<button
368381
onClick={() => setActivePanel('control')}
369382
className={`w-full flex items-center gap-3 px-3 py-3 rounded-lg transition-all ${
@@ -641,6 +654,10 @@ function HomePageContent() {
641654

642655
{activePanel === 'devices' && <DevicesPanel />}
643656

657+
{activePanel === 'settings' && (
658+
<SettingsPanel />
659+
)}
660+
644661
{activePanel === 'risk' && (
645662
<div className="h-full flex flex-col">
646663
<div className="px-4 py-3 border-b border-dark-700">
@@ -1102,6 +1119,7 @@ function HomePageContent() {
11021119
</div>
11031120

11041121
</div>
1122+
</ToastProvider>
11051123
</QueryClientProvider>
11061124
)
11071125
}

apps/console/src/components/map/MapControls.tsx

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
'use client'
22

3-
import React from 'react'
3+
import React, { useState } from 'react'
44
import { NavigationControl, ScaleControl } from 'react-map-gl/maplibre'
5-
import { ZoomIn, ZoomOut, RotateCcw, Layers } from 'lucide-react'
5+
import { RotateCcw, Layers } from 'lucide-react'
66

77
export interface MapControlsProps {
88
onLayerToggle?: (layer: string, visible: boolean) => void
99
onResetView?: () => void
1010
}
1111

1212
export function MapControls({ onLayerToggle, onResetView }: MapControlsProps) {
13+
const [coverage, setCoverage] = useState(true)
14+
const [etaDrones, setEtaDrones] = useState(true)
15+
const [etaUgvs, setEtaUgvs] = useState(true)
1316
return (
1417
<div className="absolute top-4 right-4 flex flex-col gap-2">
1518
<NavigationControl />
@@ -25,13 +28,23 @@ export function MapControls({ onLayerToggle, onResetView }: MapControlsProps) {
2528
</button>
2629
</div>
2730

28-
<div className="bg-white rounded-lg shadow-lg p-2">
29-
<button
30-
className="p-2 hover:bg-gray-100 rounded transition-colors"
31-
title="Layer Controls"
32-
>
31+
<div className="bg-white rounded-lg shadow-lg p-3 space-y-2 min-w-[200px]">
32+
<div className="flex items-center gap-2 text-sm">
3333
<Layers className="w-4 h-4" />
34-
</button>
34+
<span className="font-semibold">Layers</span>
35+
</div>
36+
<label className="flex items-center gap-2 text-sm">
37+
<input type="checkbox" checked={coverage} onChange={(e) => { setCoverage(e.target.checked); onLayerToggle?.('coverage', e.target.checked) }} />
38+
<span>Tower coverage</span>
39+
</label>
40+
<label className="flex items-center gap-2 text-sm">
41+
<input type="checkbox" checked={etaDrones} onChange={(e) => { setEtaDrones(e.target.checked); onLayerToggle?.('eta-drones', e.target.checked) }} />
42+
<span>Drone ETA (5/10/15)</span>
43+
</label>
44+
<label className="flex items-center gap-2 text-sm">
45+
<input type="checkbox" checked={etaUgvs} onChange={(e) => { setEtaUgvs(e.target.checked); onLayerToggle?.('eta-ugvs', e.target.checked) }} />
46+
<span>UGV ETA (5/10/15)</span>
47+
</label>
3548
</div>
3649
</div>
3750
)

0 commit comments

Comments
 (0)