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>
This commit is contained in:
Jason Staack
2026-03-08 17:46:37 -05:00
commit b840047e19
511 changed files with 106948 additions and 0 deletions

View File

@@ -0,0 +1,93 @@
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>
)
}

View File

@@ -0,0 +1,132 @@
import { useEffect, useMemo } from 'react'
import { MapContainer, TileLayer, useMap } from 'react-leaflet'
import MarkerClusterGroup from 'react-leaflet-cluster'
import L from 'leaflet'
import type { FleetDevice } from '@/lib/api'
import { DeviceMarker } from './DeviceMarker'
import 'leaflet/dist/leaflet.css'
interface FleetMapProps {
devices: FleetDevice[]
tenantId: string
}
/** Default world view when no devices have coordinates. */
const DEFAULT_CENTER: [number, number] = [20, 0]
const DEFAULT_ZOOM = 2
/**
* Inner component that auto-fits the map to device bounds
* whenever the device list changes.
*/
function AutoFitBounds({ devices }: { devices: FleetDevice[] }) {
const map = useMap()
useEffect(() => {
if (devices.length === 0) {
map.setView(DEFAULT_CENTER, DEFAULT_ZOOM)
return
}
const bounds = L.latLngBounds(
devices.map((d) => [d.latitude!, d.longitude!] as [number, number]),
)
map.fitBounds(bounds, { padding: [40, 40], maxZoom: 15 })
}, [devices, map])
return null
}
/**
* Custom cluster icon factory.
* - All online: green cluster
* - Any offline: red cluster
* - Mixed: yellow/orange cluster
*/
function createClusterIcon(cluster: L.MarkerCluster): L.DivIcon {
const childMarkers = cluster.getAllChildMarkers()
const count = childMarkers.length
// Determine aggregate status by inspecting marker HTML color
let hasOffline = false
let hasOnline = false
for (const marker of childMarkers) {
const icon = marker.getIcon() as L.DivIcon
const html = (icon.options.html as string) ?? ''
if (html.includes('#ef4444')) {
hasOffline = true
} else if (html.includes('#22c55e')) {
hasOnline = true
}
}
let bgColor: string
if (hasOffline && hasOnline) {
bgColor = '#f59e0b' // amber-500 — mixed
} else if (hasOffline) {
bgColor = '#ef4444' // red-500 — all offline
} else {
bgColor = '#22c55e' // green-500 — all online
}
const size = count < 10 ? 34 : count < 100 ? 40 : 48
return L.divIcon({
html: `<div style="
width: ${size}px;
height: ${size}px;
border-radius: 50%;
background: ${bgColor};
border: 3px solid white;
box-shadow: 0 2px 6px rgba(0,0,0,0.35);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: 700;
font-size: ${count < 100 ? 13 : 11}px;
font-family: system-ui, sans-serif;
">${count}</div>`,
className: '',
iconSize: L.point(size, size),
iconAnchor: L.point(size / 2, size / 2),
})
}
export function FleetMap({ devices, tenantId }: FleetMapProps) {
// Filter to only devices that have coordinates
const mappedDevices = useMemo(
() => devices.filter((d) => d.latitude != null && d.longitude != null),
[devices],
)
return (
<MapContainer
center={DEFAULT_CENTER}
zoom={DEFAULT_ZOOM}
className="h-full w-full"
scrollWheelZoom
style={{ background: 'hsl(var(--background))' }}
>
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<AutoFitBounds devices={mappedDevices} />
<MarkerClusterGroup
chunkedLoading
iconCreateFunction={createClusterIcon}
maxClusterRadius={50}
spiderfyOnMaxZoom
showCoverageOnHover={false}
>
{mappedDevices.map((device) => (
<DeviceMarker key={device.id} device={device} tenantId={tenantId} />
))}
</MarkerClusterGroup>
</MapContainer>
)
}

View File

@@ -0,0 +1,161 @@
import { useMemo, useState } from 'react'
import { useQuery } from '@tanstack/react-query'
import { MapPin } from 'lucide-react'
import { metricsApi, tenantsApi } from '@/lib/api'
import { useAuth, isSuperAdmin } from '@/lib/auth'
import { Skeleton } from '@/components/ui/skeleton'
import { FleetMap } from './FleetMap'
export function MapPage() {
const { user } = useAuth()
const superAdmin = isSuperAdmin(user)
const [selectedTenant, setSelectedTenant] = useState<string>('all')
// Fetch devices -- super_admin gets cross-tenant, others get their own tenant
const {
data: devices,
isLoading: devicesLoading,
error: devicesError,
} = useQuery({
queryKey: ['fleet-map', superAdmin ? 'all' : user?.tenant_id],
queryFn: () =>
superAdmin
? metricsApi.fleetSummaryAll()
: metricsApi.fleetSummary(user!.tenant_id!),
enabled: !!user && (superAdmin || !!user.tenant_id),
})
// Fetch tenant list for super_admin filter dropdown
const { data: tenants } = useQuery({
queryKey: ['tenants'],
queryFn: tenantsApi.list,
enabled: superAdmin,
})
// Filter devices by selected tenant
const filteredDevices = useMemo(() => {
if (!devices) return []
if (selectedTenant === 'all') return devices
return devices.filter((d) => d.tenant_id === selectedTenant)
}, [devices, selectedTenant])
// Count mapped vs total
const totalDevices = filteredDevices.length
const mappedDevices = filteredDevices.filter(
(d) => d.latitude != null && d.longitude != null,
).length
// Determine effective tenantId for links in markers
const effectiveTenantId = useMemo(() => {
if (!superAdmin) return user?.tenant_id ?? ''
if (selectedTenant !== 'all') return selectedTenant
// For "all" view as super_admin, we pass the device's own tenant_id from the FleetDevice record
// The FleetMap component handles this per-device
return ''
}, [superAdmin, selectedTenant, user])
if (devicesLoading) {
return <Skeleton className="h-[calc(100vh-8rem)] w-full rounded-lg" />
}
if (devicesError) {
return (
<div className="flex items-center justify-center h-full">
<div className="text-center">
<MapPin className="h-10 w-10 text-text-muted mx-auto mb-3" />
<p className="text-text-secondary text-sm">Failed to load fleet data</p>
<p className="text-text-muted text-xs mt-1">
{devicesError instanceof Error ? devicesError.message : 'Unknown error'}
</p>
</div>
</div>
)
}
return (
<div className="flex flex-col h-full">
{/* Toolbar */}
<div className="flex items-center justify-between px-4 py-2 border-b border-border bg-sidebar shrink-0">
<div className="flex items-center gap-3">
<MapPin className="h-4 w-4 text-text-secondary" />
<h1 className="text-sm font-medium text-text-primary">Fleet Map</h1>
<span className="text-xs text-text-muted">
{mappedDevices} of {totalDevices} device{totalDevices !== 1 ? 's' : ''} mapped
</span>
</div>
{superAdmin && tenants && tenants.length > 0 && (
<select
value={selectedTenant}
onChange={(e) => setSelectedTenant(e.target.value)}
className="text-xs bg-elevated/50 border border-border text-text-primary rounded px-2 py-1 focus:outline-none focus:ring-1 focus:ring-border-bright"
>
<option value="all">All Organizations</option>
{tenants.map((t) => (
<option key={t.id} value={t.id}>
{t.name}
</option>
))}
</select>
)}
</div>
{/* Map */}
<div className="flex-1 relative">
{totalDevices === 0 ? (
<div className="flex items-center justify-center h-full">
<div className="text-center">
<MapPin className="h-10 w-10 text-text-muted mx-auto mb-3" />
<p className="text-text-secondary text-sm">No devices found</p>
<p className="text-text-muted text-xs mt-1">
Add devices with coordinates to see them on the map
</p>
</div>
</div>
) : mappedDevices === 0 ? (
<div className="flex items-center justify-center h-full">
<div className="text-center">
<MapPin className="h-10 w-10 text-text-muted mx-auto mb-3" />
<p className="text-text-secondary text-sm">No devices have coordinates</p>
<p className="text-text-muted text-xs mt-1">
Edit devices and add latitude/longitude to place them on the map
</p>
</div>
</div>
) : (
<FleetMapWithTenantRouting
devices={filteredDevices}
effectiveTenantId={effectiveTenantId}
superAdmin={superAdmin}
/>
)}
</div>
</div>
)
}
/**
* Wrapper that handles tenant routing for markers.
* In super_admin "all" mode, each device marker uses its own tenant_id.
* Otherwise, the effective tenant is used for all markers.
*/
function FleetMapWithTenantRouting({
devices,
effectiveTenantId,
superAdmin,
}: {
devices: Array<{ latitude: number | null; longitude: number | null; tenant_id: string } & Record<string, unknown>>
effectiveTenantId: string
superAdmin: boolean
}) {
// For super_admin "all" view we need per-device tenant routing
// FleetMap + DeviceMarker handle this by using device.tenant_id when tenantId is empty
const tenantId = superAdmin && !effectiveTenantId ? '' : effectiveTenantId
return (
<FleetMap
devices={devices as unknown as import('@/lib/api').FleetDevice[]}
tenantId={tenantId}
/>
)
}