feat: enhance pending device cards with service badges and recognized service title
- Show recognized service name (Home Assistant, Synology, Proxmox, etc.) as card title instead of IP when detected via service signatures - Keep IP displayed smaller below when title differs from IP - Add SSH / HTTP / HTTPS badges as colored pills - Show +N badge for additional services beyond the three common ones
This commit is contained in:
@@ -222,27 +222,38 @@ function PendingDevicesPanel({ onNodeApproved }: { onNodeApproved: (nodeId: stri
|
||||
{!loading && devices.length === 0 && (
|
||||
<p className="text-xs text-muted-foreground text-center py-4">No pending devices</p>
|
||||
)}
|
||||
{devices.map((d) => (
|
||||
<button
|
||||
key={d.id}
|
||||
onClick={() => setSelected(d)}
|
||||
className="w-full mb-1.5 p-2 rounded-md bg-[#21262d] text-xs text-left hover:bg-[#30363d] transition-colors border border-transparent hover:border-[#30363d]"
|
||||
>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-[#e3b341] shrink-0" />
|
||||
<span className="font-mono text-foreground truncate">{d.ip}</span>
|
||||
{d.services.length > 0 && (
|
||||
<span className="ml-auto text-[10px] text-muted-foreground shrink-0">
|
||||
{d.services.length} svc
|
||||
</span>
|
||||
{devices.map((d) => {
|
||||
const recognized = d.services.find((s) => s.category != null)
|
||||
const title = recognized?.service_name ?? d.hostname ?? d.ip
|
||||
const showIpBelow = title !== d.ip
|
||||
const hasSsh = d.services.some((s) => s.port === 22)
|
||||
const hasHttp = d.services.some((s) => s.port === 80)
|
||||
const hasHttps = d.services.some((s) => s.port === 443)
|
||||
const otherCount = d.services.filter((s) => s.port !== 22 && s.port !== 80 && s.port !== 443).length
|
||||
return (
|
||||
<button
|
||||
key={d.id}
|
||||
onClick={() => setSelected(d)}
|
||||
className="w-full mb-1.5 p-2 rounded-md bg-[#21262d] text-xs text-left hover:bg-[#30363d] transition-colors border border-transparent hover:border-[#30363d]"
|
||||
>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-[#e3b341] shrink-0" />
|
||||
<span className="text-foreground truncate font-medium">{title}</span>
|
||||
</div>
|
||||
{showIpBelow && (
|
||||
<div className="font-mono text-muted-foreground truncate pl-3 text-[10px] mt-0.5">{d.ip}</div>
|
||||
)}
|
||||
</div>
|
||||
{d.hostname && <div className="text-muted-foreground truncate pl-3 text-[10px] mt-0.5">{d.hostname}</div>}
|
||||
{d.suggested_type && (
|
||||
<div className="text-[#8b949e] truncate pl-3 text-[10px]">{d.suggested_type}</div>
|
||||
)}
|
||||
</button>
|
||||
))}
|
||||
{(hasSsh || hasHttp || hasHttps || otherCount > 0) && (
|
||||
<div className="flex items-center gap-1 pl-3 mt-1.5 flex-wrap">
|
||||
{hasSsh && <ServiceBadge label="SSH" color="#a855f7" />}
|
||||
{hasHttp && <ServiceBadge label="HTTP" color="#00d4ff" />}
|
||||
{hasHttps && <ServiceBadge label="HTTPS" color="#39d353" />}
|
||||
{otherCount > 0 && <ServiceBadge label={`+${otherCount}`} color="#8b949e" />}
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
<PendingDeviceModal
|
||||
@@ -386,6 +397,17 @@ function ScanHistoryPanel() {
|
||||
)
|
||||
}
|
||||
|
||||
function ServiceBadge({ label, color }: { label: string; color: string }) {
|
||||
return (
|
||||
<span
|
||||
className="px-1 py-0.5 rounded text-[9px] font-mono font-medium leading-none border"
|
||||
style={{ color, borderColor: `${color}40`, backgroundColor: `${color}15` }}
|
||||
>
|
||||
{label}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
interface ActionButtonProps {
|
||||
icon: React.ElementType
|
||||
label: string
|
||||
|
||||
Reference in New Issue
Block a user