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,7 +222,15 @@ function PendingDevicesPanel({ onNodeApproved }: { onNodeApproved: (nodeId: stri
|
|||||||
{!loading && devices.length === 0 && (
|
{!loading && devices.length === 0 && (
|
||||||
<p className="text-xs text-muted-foreground text-center py-4">No pending devices</p>
|
<p className="text-xs text-muted-foreground text-center py-4">No pending devices</p>
|
||||||
)}
|
)}
|
||||||
{devices.map((d) => (
|
{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
|
<button
|
||||||
key={d.id}
|
key={d.id}
|
||||||
onClick={() => setSelected(d)}
|
onClick={() => setSelected(d)}
|
||||||
@@ -230,19 +238,22 @@ function PendingDevicesPanel({ onNodeApproved }: { onNodeApproved: (nodeId: stri
|
|||||||
>
|
>
|
||||||
<div className="flex items-center gap-1.5">
|
<div className="flex items-center gap-1.5">
|
||||||
<span className="w-1.5 h-1.5 rounded-full bg-[#e3b341] shrink-0" />
|
<span className="w-1.5 h-1.5 rounded-full bg-[#e3b341] shrink-0" />
|
||||||
<span className="font-mono text-foreground truncate">{d.ip}</span>
|
<span className="text-foreground truncate font-medium">{title}</span>
|
||||||
{d.services.length > 0 && (
|
</div>
|
||||||
<span className="ml-auto text-[10px] text-muted-foreground shrink-0">
|
{showIpBelow && (
|
||||||
{d.services.length} svc
|
<div className="font-mono text-muted-foreground truncate pl-3 text-[10px] mt-0.5">{d.ip}</div>
|
||||||
</span>
|
)}
|
||||||
)}
|
{(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>
|
</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>
|
</button>
|
||||||
))}
|
)
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<PendingDeviceModal
|
<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 {
|
interface ActionButtonProps {
|
||||||
icon: React.ElementType
|
icon: React.ElementType
|
||||||
label: string
|
label: string
|
||||||
|
|||||||
Reference in New Issue
Block a user