diff --git a/src/meshcore_hub/api/routes/user_profiles.py b/src/meshcore_hub/api/routes/user_profiles.py index 1de01ab..057c4ea 100644 --- a/src/meshcore_hub/api/routes/user_profiles.py +++ b/src/meshcore_hub/api/routes/user_profiles.py @@ -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 diff --git a/src/meshcore_hub/common/schemas/user_profiles.py b/src/meshcore_hub/common/schemas/user_profiles.py index 242ee19..832720a 100644 --- a/src/meshcore_hub/common/schemas/user_profiles.py +++ b/src/meshcore_hub/common/schemas/user_profiles.py @@ -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 diff --git a/src/meshcore_hub/web/static/js/spa/pages/profile.js b/src/meshcore_hub/web/static/js/spa/pages/profile.js index aa76970..f74f9b3 100644 --- a/src/meshcore_hub/web/static/js/spa/pages/profile.js +++ b/src/meshcore_hub/web/static/js/spa/pages/profile.js @@ -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`
${displayName}
${node.public_key}
- +
`; } diff --git a/tests/test_api/test_user_profiles.py b/tests/test_api/test_user_profiles.py index da0cb69..7cb4c90 100644 --- a/tests/test_api/test_user_profiles.py +++ b/tests/test_api/test_user_profiles.py @@ -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."""