import { useState } from 'react' import { type NodeProps, type Node, NodeResizer, Handle, Position } from '@xyflow/react' import { Layers, Pencil, Check, X, ChevronDown } from 'lucide-react' import { useCanvasStore } from '@/stores/canvasStore' import { useThemeStore } from '@/stores/themeStore' import { THEMES } from '@/utils/themes' import { STATUS_COLORS, type NodeData } from '@/types' export function GroupNode({ id, data, selected }: NodeProps>) { const { nodes, updateNode, snapshotHistory, toggleNodeCollapsed } = useCanvasStore() const isCollapsed = data.collapsed ?? false const activeTheme = useThemeStore((s) => s.activeTheme) const theme = THEMES[activeTheme] const showBorder = data.custom_colors?.show_border !== false const isVisible = showBorder || selected const [editing, setEditing] = useState(false) const [labelDraft, setLabelDraft] = useState(data.label) const children = nodes.filter((n) => n.parentId === id) const onlineCount = children.filter((n) => n.data.status === 'online').length const offlineCount = children.filter((n) => n.data.status === 'offline').length const unknownCount = children.length - onlineCount - offlineCount const handleRename = () => { if (labelDraft.trim()) { snapshotHistory() updateNode(id, { label: labelDraft.trim() }) } setEditing(false) } const borderColor = selected ? '#00d4ff' : '#30363d' const borderStyle = selected ? 'solid' : 'dashed' return (
{/* 4 snap-point handles — one per side. Source + invisible target overlay for each. */} {([ ['group-top', Position.Top], ['group-right', Position.Right], ['group-bottom', Position.Bottom], ['group-left', Position.Left], ] as const).map(([hid, pos]) => ( ))} {/* Header */} {isVisible && (
{editing ? ( setLabelDraft(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') handleRename() if (e.key === 'Escape') { setLabelDraft(data.label); setEditing(false) } }} style={{ flex: 1, background: 'transparent', border: 'none', outline: 'none', color: '#e6edf3', fontSize: 11, fontWeight: 600, }} /> ) : ( {data.label} )} {editing ? ( <> ) : ( )} {/* Collapse / expand toggle */} {children.length > 0 && ( )} {/* Status summary */} {children.length > 0 && (
{onlineCount > 0 && ● {onlineCount}} {offlineCount > 0 && ● {offlineCount}} {unknownCount > 0 && ● {unknownCount}}
)}
)}
) }