feat: add home_node support to Citizens API and UI

- Add HomeNodeView model to city-service
- Update get_public_citizens and get_public_citizen_by_slug to JOIN node_cache
- Add HomeNode interface to frontend types
- Display node badge on citizen cards
- Show full home_node info on citizen profile page
This commit is contained in:
Apple
2025-11-28 04:34:25 -08:00
parent f52a9e6c5e
commit 35768f1180
5 changed files with 112 additions and 13 deletions

View File

@@ -151,10 +151,38 @@ export default function CitizenProfilePage() {
</p>
</div>
)}
{citizen.node_id && (
{citizen.home_node && (
<div className="bg-white/5 border border-white/10 rounded-xl p-4">
<p className="text-xs uppercase text-white/40">Node</p>
<p className="text-white mt-1 text-lg">{citizen.node_id}</p>
<p className="text-xs uppercase text-white/40">Home Node</p>
<div className="mt-2 space-y-1">
<p className="text-white text-lg">{citizen.home_node.name || citizen.node_id}</p>
{citizen.home_node.roles && citizen.home_node.roles.length > 0 && (
<div className="flex flex-wrap gap-1">
{citizen.home_node.roles.map((role) => (
<span
key={role}
className={`px-2 py-0.5 rounded text-xs ${
role === 'gpu' ? 'bg-amber-500/20 text-amber-300' :
role === 'core' ? 'bg-emerald-500/20 text-emerald-300' :
role === 'development' ? 'bg-purple-500/20 text-purple-300' :
'bg-white/10 text-white/60'
}`}
>
{role}
</span>
))}
</div>
)}
{citizen.home_node.environment && (
<span className={`inline-block px-2 py-0.5 rounded text-xs ${
citizen.home_node.environment === 'production'
? 'bg-emerald-500/20 text-emerald-300'
: 'bg-amber-500/20 text-amber-300'
}`}>
{citizen.home_node.environment}
</span>
)}
</div>
</div>
)}
</div>

View File

@@ -190,14 +190,25 @@ function CitizenCard({ citizen }: { citizen: PublicCitizenSummary }) {
)}
<div className="mt-4 pt-4 border-t border-white/10 flex items-center justify-between">
<span className={`flex items-center gap-1.5 text-xs ${statusColor}`}>
<span
className={`w-2 h-2 rounded-full ${
status === 'online' ? 'bg-emerald-500' : 'bg-white/30'
}`}
/>
{status}
</span>
<div className="flex items-center gap-3">
<span className={`flex items-center gap-1.5 text-xs ${statusColor}`}>
<span
className={`w-2 h-2 rounded-full ${
status === 'online' ? 'bg-emerald-500' : 'bg-white/30'
}`}
/>
{status}
</span>
{citizen.home_node?.name && (
<span className={`px-2 py-0.5 rounded text-[10px] font-medium ${
citizen.home_node.environment === 'production'
? 'bg-emerald-500/20 text-emerald-400'
: 'bg-amber-500/20 text-amber-400'
}`}>
{citizen.home_node.name.split(' ')[0]}
</span>
)}
</div>
<span className="text-cyan-400 text-sm group-hover:translate-x-1 transition-transform">
View Profile
</span>

View File

@@ -1,3 +1,11 @@
export interface HomeNode {
id?: string | null;
name?: string | null;
hostname?: string | null;
roles: string[];
environment?: string | null;
}
export interface PublicCitizenSummary {
slug: string;
display_name: string;
@@ -10,6 +18,7 @@ export interface PublicCitizenSummary {
public_skills: string[];
online_status?: "online" | "offline" | "unknown" | string;
status?: string | null;
home_node?: HomeNode | null;
}
export interface CityPresenceRoom {
@@ -44,6 +53,7 @@ export interface PublicCitizenProfile {
name: string;
district?: string | null;
} | null;
home_node?: HomeNode | null;
}
export interface CitizenInteractionInfo {