revert: drop zigbee LQI edge coloring and path highlighting
Split out of this PR — will land separately with reworked zigbee edge capture/storage. Keeps only the floor plan + generic media upload work. - Remove getLqiColor + LQI-derived iot edge stroke (edges/index.tsx) - Remove zigbee path highlight effect (DetailPanel.tsx) - Remove highlightedPath/setHighlightedPath store state (canvasStore.ts) - Delete utils/zigbeePathfinding.ts - Revert associated test scaffolding and package-lock churn
This commit is contained in:
@@ -9,7 +9,7 @@ import {
|
||||
type EdgeProps,
|
||||
type Edge,
|
||||
} from '@xyflow/react'
|
||||
import type { EdgeData, EdgeType, NodeData, Waypoint } from '@/types'
|
||||
import type { EdgeData, EdgeType, Waypoint } from '@/types'
|
||||
import { useThemeStore } from '@/stores/themeStore'
|
||||
import { useCanvasStore } from '@/stores/canvasStore'
|
||||
import { THEMES } from '@/utils/themes'
|
||||
@@ -22,13 +22,6 @@ function getVlanColor(vlanId?: number): string {
|
||||
return VLAN_COLORS[vlanId % VLAN_COLORS.length]
|
||||
}
|
||||
|
||||
function getLqiColor(lqi: number): string {
|
||||
if (lqi >= 200) return '#00cc00'
|
||||
if (lqi >= 150) return '#88cc00'
|
||||
if (lqi >= 100) return '#ffaa00'
|
||||
return '#ff3300'
|
||||
}
|
||||
|
||||
// ── Waypoint drag handle ─────────────────────────────────────────────────────
|
||||
|
||||
interface WaypointHandleProps {
|
||||
@@ -296,8 +289,7 @@ export function HomelableEdge({ id, source, target, sourceHandleId, targetHandle
|
||||
const activeTheme = useThemeStore((s) => s.activeTheme)
|
||||
const theme = THEMES[activeTheme]
|
||||
const sourceType = useStore((s) => s.nodeLookup.get(source)?.type)
|
||||
const targetNode = useStore((s) => s.nodeLookup.get(target))
|
||||
const targetType = targetNode?.type
|
||||
const targetType = useStore((s) => s.nodeLookup.get(target)?.type)
|
||||
const isBidirectional = sourceType === 'proxmox' && targetType === 'proxmox'
|
||||
|
||||
const waypoints: Waypoint[] = Array.isArray(data?.waypoints) && data.waypoints.length > 0
|
||||
@@ -323,8 +315,6 @@ export function HomelableEdge({ id, source, target, sourceHandleId, targetHandle
|
||||
|
||||
const edgeType: EdgeType = data?.type ?? 'ethernet'
|
||||
const edgeColors = theme.colors.edgeColors
|
||||
const highlightedPath = useCanvasStore((s) => s.highlightedPath)
|
||||
const isHighlighted = highlightedPath.includes(id)
|
||||
|
||||
const BASE_STYLES: Record<EdgeType, React.CSSProperties> = {
|
||||
ethernet: { stroke: edgeColors.ethernet, strokeWidth: 2 },
|
||||
@@ -338,36 +328,15 @@ export function HomelableEdge({ id, source, target, sourceHandleId, targetHandle
|
||||
}
|
||||
|
||||
const customColor = data?.custom_color as string | undefined
|
||||
|
||||
// Derive LQI color from the target node's properties for iot edges
|
||||
const lqiColor = (edgeType === 'iot' && targetNode?.data?.properties)
|
||||
? (() => {
|
||||
const props = (targetNode.data as NodeData).properties ?? []
|
||||
const lqiProp = props.find(
|
||||
(p: { key: string; value: string }) => p.key === 'LQI'
|
||||
)
|
||||
if (lqiProp) {
|
||||
const lqi = parseInt(lqiProp.value, 10)
|
||||
if (!isNaN(lqi)) return getLqiColor(lqi)
|
||||
}
|
||||
return null
|
||||
})()
|
||||
: null
|
||||
|
||||
const pathHighlightColor = '#00d4ff'
|
||||
const strokeColor: string = isHighlighted
|
||||
? pathHighlightColor
|
||||
: selected
|
||||
const strokeColor: string = selected
|
||||
? theme.colors.edgeSelectedColor
|
||||
: lqiColor
|
||||
?? customColor
|
||||
: customColor
|
||||
?? (edgeType === 'vlan' ? getVlanColor(data?.vlan_id as number | undefined) : (BASE_STYLES[edgeType].stroke as string ?? edgeColors.ethernet))
|
||||
|
||||
const style: React.CSSProperties = {
|
||||
...BASE_STYLES[edgeType],
|
||||
...(edgeType === 'vlan' ? { stroke: getVlanColor(data?.vlan_id as number | undefined) } : {}),
|
||||
...(customColor ? { stroke: customColor } : {}),
|
||||
...(isHighlighted ? { stroke: pathHighlightColor, strokeWidth: 3, filter: `drop-shadow(0 0 6px ${pathHighlightColor}aa)` } : {}),
|
||||
...(selected ? { stroke: theme.colors.edgeSelectedColor, filter: `drop-shadow(0 0 4px ${theme.colors.edgeSelectedColor}88)` } : {}),
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createElement, useEffect, useRef, useState } from 'react'
|
||||
import { createElement, useRef, useState } from 'react'
|
||||
import { X, Edit, Trash2, ExternalLink, Plus, Pencil, Layers, Ungroup, Eye, EyeOff } from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
@@ -9,8 +9,6 @@ import { getServiceUrl } from '@/utils/serviceUrl'
|
||||
import { splitIps } from '@/utils/maskIp'
|
||||
import { PROPERTY_ICONS, PROPERTY_ICON_NAMES, resolvePropertyIcon } from '@/utils/propertyIcons'
|
||||
import { formatTimestamp } from '@/utils/timeFormat'
|
||||
import { findZigbeePath } from '@/utils/zigbeePathfinding'
|
||||
import { isZigbeeType } from '@/utils/zigbeeProperties'
|
||||
import type { Node } from '@xyflow/react'
|
||||
|
||||
interface DetailPanelProps {
|
||||
@@ -24,7 +22,7 @@ type PropForm = { key: string; value: string; icon: string | null; visible: bool
|
||||
const EMPTY_PROP: PropForm = { key: '', value: '', icon: null, visible: true }
|
||||
|
||||
export function DetailPanel({ onEdit }: DetailPanelProps) {
|
||||
const { nodes, edges, selectedNodeId, selectedNodeIds, setSelectedNode, deleteNode, updateNode, snapshotHistory, createGroup, ungroup, removeFromGroup, setNodeSize, setHighlightedPath } = useCanvasStore()
|
||||
const { nodes, selectedNodeId, selectedNodeIds, setSelectedNode, deleteNode, updateNode, snapshotHistory, createGroup, ungroup, removeFromGroup, setNodeSize } = useCanvasStore()
|
||||
const serviceStatuses = useCanvasStore((s) => s.serviceStatuses)
|
||||
|
||||
const [addingForNode, setAddingForNode] = useState<string | null>(null)
|
||||
@@ -43,25 +41,6 @@ export function DetailPanel({ onEdit }: DetailPanelProps) {
|
||||
// Multi-select panel
|
||||
const multiSelected = (selectedNodeIds ?? []).filter((id) => nodes.some((n) => n.id === id))
|
||||
|
||||
// Zigbee path highlighting: when a zigbee node is selected, compute
|
||||
// the optimal route to the coordinator and highlight those edges.
|
||||
useEffect(() => {
|
||||
const targetNode = nodes.find((n) => n.id === selectedNodeId)
|
||||
if (!targetNode || !isZigbeeType(targetNode.data.type)) {
|
||||
setHighlightedPath([])
|
||||
return
|
||||
}
|
||||
|
||||
const coordinator = nodes.find((n) => n.data.type === 'zigbee_coordinator')
|
||||
if (!coordinator) {
|
||||
setHighlightedPath([])
|
||||
return
|
||||
}
|
||||
|
||||
const path = findZigbeePath(targetNode.id, coordinator.id, nodes, edges)
|
||||
setHighlightedPath(path)
|
||||
}, [selectedNodeId, nodes, edges, setHighlightedPath])
|
||||
|
||||
if (multiSelected.length > 1) {
|
||||
return (
|
||||
<MultiSelectPanel
|
||||
|
||||
@@ -37,8 +37,6 @@ function setupStore(nodeData: Partial<NodeData> = {}, serviceStatuses: Record<st
|
||||
createGroup: vi.fn(),
|
||||
ungroup: vi.fn(),
|
||||
setNodeSize: vi.fn(),
|
||||
setHighlightedPath: vi.fn(),
|
||||
edges: [],
|
||||
serviceStatuses,
|
||||
}
|
||||
// Support both the bare destructure call and the selector-based call.
|
||||
@@ -59,8 +57,6 @@ describe('DetailPanel', () => {
|
||||
snapshotHistory: vi.fn(),
|
||||
createGroup: vi.fn(),
|
||||
ungroup: vi.fn(),
|
||||
setHighlightedPath: vi.fn(),
|
||||
edges: [],
|
||||
} as unknown as ReturnType<typeof canvasStore.useCanvasStore>)
|
||||
})
|
||||
|
||||
@@ -134,8 +130,6 @@ describe('DetailPanel', () => {
|
||||
snapshotHistory: vi.fn(),
|
||||
createGroup: vi.fn(),
|
||||
ungroup: vi.fn(),
|
||||
setHighlightedPath: vi.fn(),
|
||||
edges: [],
|
||||
} as unknown as ReturnType<typeof canvasStore.useCanvasStore>)
|
||||
|
||||
render(<DetailPanel onEdit={vi.fn()} />)
|
||||
@@ -163,8 +157,6 @@ describe('DetailPanel', () => {
|
||||
snapshotHistory: vi.fn(),
|
||||
createGroup: vi.fn(),
|
||||
ungroup: vi.fn(),
|
||||
setHighlightedPath: vi.fn(),
|
||||
edges: [],
|
||||
} as unknown as ReturnType<typeof canvasStore.useCanvasStore>)
|
||||
|
||||
render(<DetailPanel onEdit={vi.fn()} />)
|
||||
@@ -186,8 +178,6 @@ describe('DetailPanel', () => {
|
||||
snapshotHistory: vi.fn(),
|
||||
createGroup: vi.fn(),
|
||||
ungroup: vi.fn(),
|
||||
setHighlightedPath: vi.fn(),
|
||||
edges: [],
|
||||
} as unknown as ReturnType<typeof canvasStore.useCanvasStore>)
|
||||
|
||||
render(<DetailPanel onEdit={vi.fn()} />)
|
||||
@@ -209,8 +199,6 @@ describe('DetailPanel', () => {
|
||||
snapshotHistory: vi.fn(),
|
||||
createGroup: vi.fn(),
|
||||
ungroup: vi.fn(),
|
||||
setHighlightedPath: vi.fn(),
|
||||
edges: [],
|
||||
} as unknown as ReturnType<typeof canvasStore.useCanvasStore>)
|
||||
|
||||
render(<DetailPanel onEdit={vi.fn()} />)
|
||||
@@ -265,8 +253,6 @@ describe('DetailPanel', () => {
|
||||
deleteNode: vi.fn(),
|
||||
updateNode: vi.fn(),
|
||||
snapshotHistory: vi.fn(),
|
||||
setHighlightedPath: vi.fn(),
|
||||
edges: [],
|
||||
} as unknown as ReturnType<typeof canvasStore.useCanvasStore>)
|
||||
render(<DetailPanel onEdit={vi.fn()} />)
|
||||
fireEvent.click(screen.getByLabelText('Close panel'))
|
||||
@@ -291,8 +277,6 @@ describe('DetailPanel', () => {
|
||||
deleteNode,
|
||||
updateNode: vi.fn(),
|
||||
snapshotHistory,
|
||||
setHighlightedPath: vi.fn(),
|
||||
edges: [],
|
||||
} as unknown as ReturnType<typeof canvasStore.useCanvasStore>)
|
||||
vi.spyOn(window, 'confirm').mockReturnValue(true)
|
||||
render(<DetailPanel onEdit={vi.fn()} />)
|
||||
@@ -311,8 +295,6 @@ describe('DetailPanel', () => {
|
||||
deleteNode,
|
||||
updateNode: vi.fn(),
|
||||
snapshotHistory,
|
||||
setHighlightedPath: vi.fn(),
|
||||
edges: [],
|
||||
} as unknown as ReturnType<typeof canvasStore.useCanvasStore>)
|
||||
vi.spyOn(window, 'confirm').mockReturnValue(false)
|
||||
render(<DetailPanel onEdit={vi.fn()} />)
|
||||
@@ -344,8 +326,6 @@ describe('DetailPanel', () => {
|
||||
snapshotHistory: vi.fn(),
|
||||
createGroup: vi.fn(),
|
||||
ungroup: vi.fn(),
|
||||
setHighlightedPath: vi.fn(),
|
||||
edges: [],
|
||||
} as unknown as ReturnType<typeof canvasStore.useCanvasStore>)
|
||||
render(<DetailPanel onEdit={vi.fn()} />)
|
||||
// Two "Add" header buttons: first = properties, second = services
|
||||
@@ -371,8 +351,6 @@ describe('DetailPanel', () => {
|
||||
snapshotHistory: vi.fn(),
|
||||
createGroup: vi.fn(),
|
||||
ungroup: vi.fn(),
|
||||
setHighlightedPath: vi.fn(),
|
||||
edges: [],
|
||||
} as unknown as ReturnType<typeof canvasStore.useCanvasStore>)
|
||||
render(<DetailPanel onEdit={vi.fn()} />)
|
||||
const addHeaders = screen.getAllByText('Add')
|
||||
@@ -396,8 +374,6 @@ describe('DetailPanel', () => {
|
||||
deleteNode: vi.fn(),
|
||||
updateNode,
|
||||
snapshotHistory: vi.fn(),
|
||||
setHighlightedPath: vi.fn(),
|
||||
edges: [],
|
||||
} as unknown as ReturnType<typeof canvasStore.useCanvasStore>)
|
||||
render(<DetailPanel onEdit={vi.fn()} />)
|
||||
fireEvent.click(screen.getByTitle('Remove service'))
|
||||
@@ -413,8 +389,6 @@ describe('DetailPanel', () => {
|
||||
deleteNode: vi.fn(),
|
||||
updateNode: vi.fn(),
|
||||
snapshotHistory: vi.fn(),
|
||||
setHighlightedPath: vi.fn(),
|
||||
edges: [],
|
||||
} as unknown as ReturnType<typeof canvasStore.useCanvasStore>)
|
||||
expect(() => render(<DetailPanel onEdit={vi.fn()} />)).not.toThrow()
|
||||
})
|
||||
@@ -446,8 +420,6 @@ describe('DetailPanel', () => {
|
||||
deleteNode: vi.fn(),
|
||||
updateNode,
|
||||
snapshotHistory: vi.fn(),
|
||||
setHighlightedPath: vi.fn(),
|
||||
edges: [],
|
||||
} as unknown as ReturnType<typeof canvasStore.useCanvasStore>)
|
||||
|
||||
render(<DetailPanel onEdit={vi.fn()} />)
|
||||
@@ -473,8 +445,6 @@ describe('DetailPanel', () => {
|
||||
deleteNode: vi.fn(),
|
||||
updateNode,
|
||||
snapshotHistory: vi.fn(),
|
||||
setHighlightedPath: vi.fn(),
|
||||
edges: [],
|
||||
} as unknown as ReturnType<typeof canvasStore.useCanvasStore>)
|
||||
|
||||
render(<DetailPanel onEdit={vi.fn()} />)
|
||||
@@ -638,7 +608,6 @@ describe('DetailPanel', () => {
|
||||
function setupSized(node: Partial<Node<NodeData>>, setNodeSize = vi.fn()) {
|
||||
const state = {
|
||||
nodes: [{ ...makeNode({}), ...node }],
|
||||
edges: [],
|
||||
selectedNodeId: 'n1',
|
||||
selectedNodeIds: [],
|
||||
setSelectedNode: vi.fn(),
|
||||
@@ -648,7 +617,6 @@ describe('DetailPanel', () => {
|
||||
createGroup: vi.fn(),
|
||||
ungroup: vi.fn(),
|
||||
setNodeSize,
|
||||
setHighlightedPath: vi.fn(),
|
||||
serviceStatuses: {},
|
||||
}
|
||||
vi.mocked(canvasStore.useCanvasStore).mockImplementation(
|
||||
|
||||
@@ -34,7 +34,6 @@ function makeGroupNode(id = 'g1', label = 'My Group', showBorder = true) {
|
||||
|
||||
const mockStore = {
|
||||
nodes: [],
|
||||
edges: [],
|
||||
selectedNodeId: null,
|
||||
selectedNodeIds: [],
|
||||
setSelectedNode: vi.fn(),
|
||||
@@ -44,7 +43,6 @@ const mockStore = {
|
||||
createGroup: vi.fn(),
|
||||
ungroup: vi.fn(),
|
||||
removeFromGroup: vi.fn(),
|
||||
setHighlightedPath: vi.fn(),
|
||||
}
|
||||
|
||||
function setupStore(overrides = {}) {
|
||||
|
||||
Reference in New Issue
Block a user