Files
the-other-dude/frontend/src/components/map/DeviceMarker.tsx
Jason Staack b840047e19 feat: The Other Dude v9.0.1 — full-featured email system
ci: add GitHub Pages deployment workflow for docs site

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 19:30:44 -05:00

94 lines
2.9 KiB
TypeScript

import { Marker, Popup } from 'react-leaflet'
import L from 'leaflet'
import { Link } from '@tanstack/react-router'
import type { FleetDevice } from '@/lib/api'
import { formatUptime } from '@/lib/utils'
interface DeviceMarkerProps {
device: FleetDevice
tenantId: string
}
const STATUS_COLORS: Record<string, string> = {
online: '#22c55e', // green-500
offline: '#ef4444', // red-500
unknown: '#eab308', // yellow-500
}
function getStatusColor(status: string): string {
return STATUS_COLORS[status] ?? STATUS_COLORS.unknown
}
function createMarkerIcon(status: string): L.DivIcon {
const color = getStatusColor(status)
return L.divIcon({
className: '', // Remove default leaflet-div-icon styling
html: `<div style="
width: 14px;
height: 14px;
border-radius: 50%;
background: ${color};
border: 2px solid white;
box-shadow: 0 1px 4px rgba(0,0,0,0.4);
"></div>`,
iconSize: [14, 14],
iconAnchor: [7, 7],
popupAnchor: [0, -10],
})
}
const statusLabels: Record<string, string> = {
online: 'Online',
offline: 'Offline',
unknown: 'Unknown',
}
export function DeviceMarker({ device, tenantId }: DeviceMarkerProps) {
if (device.latitude == null || device.longitude == null) return null
const icon = createMarkerIcon(device.status)
const statusColor = getStatusColor(device.status)
const statusLabel = statusLabels[device.status] ?? device.status
// In super_admin "all" mode, tenantId may be empty — fall back to device's own tenant_id
const resolvedTenantId = tenantId || device.tenant_id
return (
<Marker position={[device.latitude, device.longitude]} icon={icon}>
<Popup>
<div className="min-w-[200px] text-sm font-sans">
<div className="font-semibold text-base mb-1">{device.hostname}</div>
<div className="text-text-secondary space-y-0.5">
<div>IP: {device.ip_address}</div>
{device.model && <div>Model: {device.model}</div>}
<div>Uptime: {formatUptime(device.uptime_seconds)}</div>
<div className="flex items-center gap-1.5 mt-1">
Status:
<span
className="inline-block w-2 h-2 rounded-full"
style={{ background: statusColor }}
/>
<span>{statusLabel}</span>
</div>
</div>
<div className="flex gap-3 mt-2 pt-2 border-t border-border">
<Link
to="/tenants/$tenantId/devices/$deviceId"
params={{ tenantId: resolvedTenantId, deviceId: device.id }}
className="text-info hover:text-accent text-xs font-medium"
>
View Details &rarr;
</Link>
<Link
to="/config-editor"
className="text-info hover:text-accent text-xs font-medium"
>
Config Editor &rarr;
</Link>
</div>
</div>
</Popup>
</Marker>
)
}