feat(frontend): replace dashboard with realtime device map
All checks were successful
Deploy to Production / deploy (push) Successful in 17m7s

This commit is contained in:
魏风
2026-03-24 00:18:43 +08:00
parent 20355b0804
commit e195b26b5a

View File

@@ -1,6 +1,8 @@
import { createFileRoute } from "@tanstack/react-router" import { createFileRoute } from "@tanstack/react-router"
import L from "leaflet" import L from "leaflet"
import { import {
ChevronDown,
ChevronUp,
Clock, Clock,
Layers, Layers,
Maximize, Maximize,
@@ -15,13 +17,7 @@ import "leaflet/dist/leaflet.css"
import { Badge } from "@/components/ui/badge" import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
import { import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import { import {
DropdownMenu, DropdownMenu,
DropdownMenuContent, DropdownMenuContent,
@@ -445,6 +441,7 @@ function Dashboard() {
const { snapshot, status, disconnectedAt } = useTelemetryWS() const { snapshot, status, disconnectedAt } = useTelemetryWS()
const [baseMap, setBaseMap] = useState<BaseMapType>("amap") const [baseMap, setBaseMap] = useState<BaseMapType>("amap")
const [activeDeviceId, setActiveDeviceId] = useState<string>("") const [activeDeviceId, setActiveDeviceId] = useState<string>("")
const [isPanelExpanded, setIsPanelExpanded] = useState(true)
const activeDeviceIdRef = useRef(activeDeviceId) const activeDeviceIdRef = useRef(activeDeviceId)
activeDeviceIdRef.current = activeDeviceId activeDeviceIdRef.current = activeDeviceId
@@ -637,52 +634,73 @@ function Dashboard() {
})} })}
</MapContainer> </MapContainer>
</div> </div>
</div>
{/* Selected Device Panel — placed at bottom, compact */} {/* Selected Device Panel Overlay */}
<Card className="rounded-none border-x-0 border-b-0 shadow-none bg-background/50 backdrop-blur-sm shrink-0"> <div
<CardHeader> className={`absolute bottom-6 right-6 z-9999 w-80 max-w-[calc(100%-3rem)] transition-all duration-300 ${isPanelExpanded ? "opacity-80" : "opacity-100"}`}
<CardTitle className="flex items-center gap-2"> >
<Card className="shadow-lg border bg-background/50 backdrop-blur-md overflow-hidden">
<CardHeader
className="p-3 flex flex-row items-center justify-between space-y-0 cursor-pointer select-none"
onClick={() => setIsPanelExpanded(!isPanelExpanded)}
>
<CardTitle className="text-sm font-semibold flex items-center gap-2">
Selected Device Selected Device
{activeDevice?.signal_health && {activeDevice?.signal_health &&
!isPanelExpanded &&
signalHealthBadge(activeDevice.signal_health)} signalHealthBadge(activeDevice.signal_health)}
</CardTitle> </CardTitle>
<CardDescription> <div className="flex items-center gap-1">
Click a marker on the map to view detailed device info {activeDevice?.signal_health &&
</CardDescription> isPanelExpanded &&
signalHealthBadge(activeDevice.signal_health)}
<Button
variant="ghost"
size="icon"
className="h-6 w-6 hover:bg-transparent"
>
{isPanelExpanded ? (
<ChevronDown className="size-4" />
) : (
<ChevronUp className="size-4" />
)}
</Button>
</div>
</CardHeader> </CardHeader>
<CardContent className="grid gap-4 text-sm sm:grid-cols-2 lg:grid-cols-4 xl:grid-cols-8"> {isPanelExpanded && (
<div> <CardContent className="p-3 pt-0 grid grid-cols-2 gap-x-4 gap-y-3 text-[11px]">
<p className="text-muted-foreground mb-1">Device ID</p> <div className="col-span-2">
<p className="font-medium break-all"> <p className="text-muted-foreground mb-0.5">Device ID</p>
<p className="font-medium break-all text-xs">
{activeDevice?.device_id ?? "-"} {activeDevice?.device_id ?? "-"}
</p> </p>
</div> </div>
<div> <div>
<p className="text-muted-foreground mb-1">Latitude</p> <p className="text-muted-foreground mb-0.5">Latitude</p>
<p className="font-medium font-mono"> <p className="font-medium font-mono">
{formatCoord(activeDevice?.lat ?? null)} {formatCoord(activeDevice?.lat ?? null)}
</p> </p>
</div> </div>
<div> <div>
<p className="text-muted-foreground mb-1">Longitude</p> <p className="text-muted-foreground mb-0.5">Longitude</p>
<p className="font-medium font-mono"> <p className="font-medium font-mono">
{formatCoord(activeDevice?.lon ?? null)} {formatCoord(activeDevice?.lon ?? null)}
</p> </p>
</div> </div>
<div> <div>
<p className="text-muted-foreground mb-1 flex items-center gap-1"> <p className="text-muted-foreground mb-0.5 flex items-center gap-1">
<Mountain className="size-3" /> <Mountain className="size-3" />
Altitude Altitude
</p> </p>
<p className="font-medium font-mono"> <p className="font-medium font-mono">
{activeDevice?.alt_m !== null && activeDevice?.alt_m !== undefined {activeDevice?.alt_m !== null &&
activeDevice?.alt_m !== undefined
? `${activeDevice.alt_m.toFixed(1)} m` ? `${activeDevice.alt_m.toFixed(1)} m`
: "-"} : "-"}
</p> </p>
</div> </div>
<div> <div>
<p className="text-muted-foreground mb-1">Fix Status</p> <p className="text-muted-foreground mb-0.5">Fix Status</p>
<p <p
className={`font-medium ${getFixStatusColor(activeDevice?.fix_status ?? null)}`} className={`font-medium ${getFixStatusColor(activeDevice?.fix_status ?? null)}`}
> >
@@ -690,7 +708,7 @@ function Dashboard() {
</p> </p>
</div> </div>
<div> <div>
<p className="text-muted-foreground mb-1 flex items-center gap-1"> <p className="text-muted-foreground mb-0.5 flex items-center gap-1">
<Satellite className="size-3" /> <Satellite className="size-3" />
Satellites Satellites
</p> </p>
@@ -699,9 +717,9 @@ function Dashboard() {
</p> </p>
</div> </div>
<div> <div>
<p className="text-muted-foreground mb-1 flex items-center gap-1"> <p className="text-muted-foreground mb-0.5 flex items-center gap-1">
<Signal className="size-3" /> <Signal className="size-3" />
Signal Score SNR
</p> </p>
<p className="font-medium font-mono"> <p className="font-medium font-mono">
{activeDevice?.signal_health {activeDevice?.signal_health
@@ -709,8 +727,8 @@ function Dashboard() {
: "-"} : "-"}
</p> </p>
</div> </div>
<div> <div className="col-span-2">
<p className="text-muted-foreground mb-1 flex items-center gap-1"> <p className="text-muted-foreground mb-0.5 flex items-center gap-1">
<Clock className="size-3" /> <Clock className="size-3" />
Last Seen Last Seen
</p> </p>
@@ -719,7 +737,10 @@ function Dashboard() {
</p> </p>
</div> </div>
</CardContent> </CardContent>
)}
</Card> </Card>
</div> </div>
</div>
</div>
) )
} }