feat(fleet): make device hostname a clickable link in fleet table

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jason Staack
2026-03-18 11:12:38 -05:00
parent c3bbdb852d
commit e4b9eeae1d

View File

@@ -16,6 +16,7 @@ import {
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
import { formatUptime, formatDateTime } from '@/lib/utils' import { formatUptime, formatDateTime } from '@/lib/utils'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
import { DeviceLink } from '@/components/ui/device-link'
import { TableSkeleton } from '@/components/ui/page-skeleton' import { TableSkeleton } from '@/components/ui/page-skeleton'
import { EmptyState } from '@/components/ui/empty-state' import { EmptyState } from '@/components/ui/empty-state'
@@ -82,17 +83,16 @@ function SortHeader({ column, label, currentSort, currentDir, onSort, className
) )
} }
function DeviceCard({ device, onClick }: { device: DeviceResponse; onClick: () => void }) { function DeviceCard({ device, tenantId }: { device: DeviceResponse; tenantId: string }) {
return ( return (
<button <div
onClick={onClick}
className="w-full text-left rounded-lg border border-border bg-surface p-3 hover:bg-elevated/50 transition-colors min-h-[44px]" className="w-full text-left rounded-lg border border-border bg-surface p-3 hover:bg-elevated/50 transition-colors min-h-[44px]"
data-testid={`device-card-${device.hostname}`} data-testid={`device-card-${device.hostname}`}
> >
<div className="flex items-start justify-between gap-2"> <div className="flex items-start justify-between gap-2">
<div className="flex items-center gap-2 min-w-0"> <div className="flex items-center gap-2 min-w-0">
<StatusDot status={device.status} /> <StatusDot status={device.status} />
<span className="font-medium text-sm text-text-primary truncate">{device.hostname}</span> <DeviceLink tenantId={tenantId} deviceId={device.id} className="font-medium text-sm text-text-primary truncate">{device.hostname}</DeviceLink>
</div> </div>
<span className="text-xs text-text-muted shrink-0">{formatUptime(device.uptime_seconds)}</span> <span className="text-xs text-text-muted shrink-0">{formatUptime(device.uptime_seconds)}</span>
</div> </div>
@@ -108,7 +108,7 @@ function DeviceCard({ device, onClick }: { device: DeviceResponse; onClick: () =
))} ))}
</div> </div>
)} )}
</button> </div>
) )
} }
@@ -213,7 +213,7 @@ export function FleetTable({
<td className="px-2 py-1.5 text-center"> <td className="px-2 py-1.5 text-center">
<StatusDot status={device.status} /> <StatusDot status={device.status} />
</td> </td>
<td className="px-2 py-1.5 font-medium text-text-primary">{device.hostname}</td> <td className="px-2 py-1.5 font-medium text-text-primary"><DeviceLink tenantId={tenantId} deviceId={device.id}>{device.hostname}</DeviceLink></td>
<td className="px-2 py-1.5 font-mono text-xs text-text-secondary"> <td className="px-2 py-1.5 font-mono text-xs text-text-secondary">
{device.ip_address} {device.ip_address}
</td> </td>
@@ -287,7 +287,7 @@ export function FleetTable({
<DeviceCard <DeviceCard
key={device.id} key={device.id}
device={device} device={device}
onClick={() => handleDeviceClick(device)} tenantId={tenantId}
/> />
)) ))
)} )}
@@ -324,12 +324,9 @@ export function FleetTable({
data-index={virtualRow.index} data-index={virtualRow.index}
ref={virtualizer.measureElement} ref={virtualizer.measureElement}
className={cn( className={cn(
'border-b border-border/50 hover:bg-elevated/50 cursor-pointer transition-colors', 'border-b border-border/50 hover:bg-elevated/50 transition-colors',
selectedIndex === virtualRow.index && 'bg-elevated/50', selectedIndex === virtualRow.index && 'bg-elevated/50',
)} )}
onClick={() => handleDeviceClick(device)}
tabIndex={0}
onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleDeviceClick(device) } }}
style={{ style={{
position: 'absolute', position: 'absolute',
top: 0, top: 0,
@@ -385,12 +382,9 @@ export function FleetTable({
key={device.id} key={device.id}
data-testid={`device-row-${device.hostname}`} data-testid={`device-row-${device.hostname}`}
className={cn( className={cn(
'border-b border-border/50 hover:bg-elevated/50 cursor-pointer transition-colors', 'border-b border-border/50 hover:bg-elevated/50 transition-colors',
selectedIndex === idx && 'bg-elevated/50', selectedIndex === idx && 'bg-elevated/50',
)} )}
onClick={() => handleDeviceClick(device)}
tabIndex={0}
onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleDeviceClick(device) } }}
> >
{renderDeviceRow(device)} {renderDeviceRow(device)}
</tr> </tr>