import { useEffect, useState } from 'react'; import { Badge } from './ui/badge'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from './ui/card'; import StatBar from './StatBar'; import { nationColor } from '@shared/protocol'; import { RELATION_COLORS } from '@/lib/colors'; import { keyToName } from '@/lib/regions'; import type { Nation, RelationStatus } from '@shared/types'; /** Human labels for a diplomatic status (the raw value is kept in data-attrs). */ const RELATION_LABEL: Record = { ally: 'Ally', neutral: 'Neutral', rival: 'Rival', hostile: 'Hostile', war: 'At war', }; interface Props { nation: Nation; nations: Record; /** True when this is the nation the player is leading. */ isPlayer: boolean; /** * Render without the outer card chrome (the surrounding floating window * provides the border, backdrop, and title bar). Same content, same testids. */ inWindow?: boolean; } /** * A single nation's dossier: identity + color chip, the four 0โ€“100 stat bars * (colored by value: low = red โ†’ high = green, animated on change), territory as * color-coded region chips, and diplomacy as a list with a colored indicator per * relation (ally = green, rival = amber, war = red, neutral = slate). Used for * the player (highlighted) or any inspected nation. */ export default function NationPanel({ nation, nations, isPlayer, inWindow = false }: Props) { const color = nationColor(nation.id); const relations = Object.entries(nation.relations).sort(([a], [b]) => a.localeCompare(b)); const regions = nation.territory; // Human region names for the territory chips (region key -> name), loaded once // (cached in @/lib/regions). A key with no known name falls back to the raw key. const [keyNames, setKeyNames] = useState>({}); useEffect(() => { let cancelled = false; keyToName() .then((m) => { if (!cancelled) setKeyNames(m); }) .catch(() => { /* names are cosmetic โ€” a failed fetch just leaves the raw keys */ }); return () => { cancelled = true; }; }, []); const identity = (
{nation.name} {nation.type} ยท {regions.length} region{regions.length === 1 ? '' : 's'}
{isPlayer && ( Your nation )}
); const sections = (

Status

Territory

{regions.length > 0 ? (
{regions.map((key) => { const label = keyNames[key] ?? key; return ( {label} ); })}
) : (

No territory.

)}

Diplomacy

{relations.length > 0 ? (
    {relations.map(([id, status]) => { const other = nations[id]; const c = RELATION_COLORS[status] ?? '#94a3b8'; return (
  • {other?.name ?? id} {RELATION_LABEL[status] ?? status}
  • ); })}
) : (

No diplomatic relations yet.

)}
); if (inWindow) { return (
{identity} {sections}
); } return ( {identity} {sections} ); }