ci: add GitHub Pages deployment workflow for docs site Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
94 lines
2.9 KiB
TypeScript
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 →
|
|
</Link>
|
|
<Link
|
|
to="/config-editor"
|
|
className="text-info hover:text-accent text-xs font-medium"
|
|
>
|
|
Config Editor →
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
</Popup>
|
|
</Marker>
|
|
)
|
|
}
|