import React from 'react'; import { Server, Monitor, Shield, Database, Cpu, Globe, Users, HardDrive, Eye, type LucideIcon, } from '../../icons'; import type { MazeNode } from './types'; import { DEFAULT_SERVICES } from './data'; const ARCHETYPE_ICONS: Record = { 'linux-server': Server, 'windows-workstation': Monitor, 'domain-controller': Shield, 'database-server': Database, 'iot-device': Cpu, 'web-application': Globe, 'deaddeck': HardDrive, 'attacker-pool': Eye, 'directory-services': Users, }; interface Props { node: MazeNode; absX: number; absY: number; selected: boolean; dragging?: boolean; deployed?: boolean; selectedServiceSlug?: string | null; onSelect?: (id: string) => void; onSelectService?: (nodeId: string, slug: string) => void; onMouseDown?: (id: string) => (e: React.MouseEvent) => void; onPortMouseDown?: (id: string) => (e: React.MouseEvent) => void; onContextMenu?: (id: string) => (e: React.MouseEvent) => void; } const NodeCard: React.FC = ({ node, absX, absY, selected, dragging, deployed, selectedServiceSlug, onSelect, onSelectService, onMouseDown, onPortMouseDown, onContextMenu }) => { const isDmzGateway = !!(node as { decky_config?: { forwards_l3?: boolean } }).decky_config?.forwards_l3; const classes = [ 'maze-node', node.kind === 'observed' ? 'observed' : '', node.status === 'hot' ? 'hot' : '', selected ? 'selected' : '', dragging ? 'dragging' : '', deployed ? 'deployed' : '', deployed && isDmzGateway ? 'dmz-gateway' : '', ].filter(Boolean).join(' '); const handleDown = (e: React.MouseEvent) => { onSelect?.(node.id); onMouseDown?.(node.id)(e); }; return (
{(() => { const Icon = ARCHETYPE_ICONS[node.archetype] ?? Server; return ; })()} {node.name}
{node.archetype.toUpperCase()}
{node.services.length > 0 && (
{node.services.map((s) => { const meta = DEFAULT_SERVICES.find((x) => x.slug === s); const isHigh = meta?.risk === 'high' || node.status === 'hot'; const isSel = selectedServiceSlug === s; return ( { if (!onSelectService) return; e.stopPropagation(); onSelectService(node.id, s); }} > {s} ); })}
)} {node.kind === 'decky' && <> } {node.kind === 'observed' && ( )}
); }; export default React.memo(NodeCard);