fix(web): show node last_seen instead of adopted_at on profile

The User Profile page rendered each adopted node's relative time from
adopted_at (the adoption date) rather than the node's most recent
activity, so it always showed the time since adoption (e.g. "35 days
ago") even when the node had advertised today.

Expose last_seen on AdoptedNodeRead and render it on the profile page,
falling back to "-" when null (matching the nodes/node-detail pages).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Louis King
2026-06-11 16:48:32 +01:00
parent 6c9a07e4c8
commit 620747baa3
4 changed files with 8 additions and 3 deletions
@@ -46,6 +46,7 @@ def _build_adopted_nodes(profile: UserProfile) -> list[AdoptedNodeRead]:
name=assoc.node.name,
adv_type=assoc.node.adv_type,
adopted_at=assoc.adopted_at,
last_seen=assoc.node.last_seen,
)
)
return adopted_nodes
@@ -75,6 +75,9 @@ class AdoptedNodeRead(BaseModel):
name: Optional[str] = Field(default=None, description="Node display name")
adv_type: Optional[str] = Field(default=None, description="Advertisement type")
adopted_at: datetime = Field(..., description="When the node was adopted")
last_seen: Optional[datetime] = Field(
default=None, description="Timestamp of the node's most recent activity"
)
class Config:
from_attributes = True
@@ -7,15 +7,15 @@ import {
function renderAdoptedNode(node) {
const displayName = node.name || node.public_key.slice(0, 12) + '...';
const relTime = formatRelativeTime(node.adopted_at);
const fullTime = formatDateTime(node.adopted_at);
const relTime = node.last_seen ? formatRelativeTime(node.last_seen) : '-';
const fullTime = node.last_seen ? formatDateTime(node.last_seen) : '-';
return html`<a href="/nodes/${node.public_key}" class="flex items-center justify-between gap-3 p-3 bg-base-200 rounded-lg hover:bg-base-300 transition-colors">
<div class="flex-1 min-w-0">
<div class="font-medium text-sm truncate">${displayName}</div>
<div class="font-mono text-xs opacity-60 truncate">${node.public_key}</div>
</div>
<time class="text-xs opacity-60 whitespace-nowrap shrink-0" datetime=${node.adopted_at} title=${fullTime} data-relative-time>${relTime}</time>
<time class="text-xs opacity-60 whitespace-nowrap shrink-0" datetime=${node.last_seen || nothing} title=${fullTime} data-relative-time>${relTime}</time>
</a>`;
}
+1
View File
@@ -206,6 +206,7 @@ class TestGetProfile:
assert len(data["nodes"]) == 1
assert data["nodes"][0]["public_key"] == "abc123def456abc123def456abc123de"
assert "adopted_at" in data["nodes"][0]
assert data["nodes"][0]["last_seen"] is not None
def test_get_profile_returns_roles(self, client_no_auth, sample_user_profile):
"""Test that profile includes roles."""