fix: cluster edges render on left/right handles from approve flow
Cluster edges created via the pending -> approve path rendered on the top
handle instead of left/right, because the edge and its endpoints lost their
handle information on the way to the canvas.
- Approve resolver (scan.py) now returns each edge's type + source/target
handle. Handle IDs are the bare slot-0 side names ('right'/'left'), the
canonical stored form React Flow resolves to the correct side; a '-t' target
id fails to resolve and falls back to the top handle.
- Frontend injectAutoEdges no longer hardcodes iot/bottom/top-t. It injects
each edge with its real type + handles and bumps the referenced nodes'
left/right handle counts (which default to 0) so the cluster endpoints exist.
Logic extracted to a pure, tested util (applyAutoEdges).
- clusterEdges direct-import path uses the bare 'left' target to match.
Tests: new autoEdges unit tests; updated backend handle assertions.
ha-relevant: maybe
This commit is contained in:
@@ -12,6 +12,7 @@ import { resolveNodeColors } from '@/utils/nodeColors'
|
||||
import { toast } from 'sonner'
|
||||
import { PendingDeviceModal, type PendingDevice } from '@/components/modals/PendingDeviceModal'
|
||||
import type { NodeType, ServiceInfo } from '@/types'
|
||||
import { applyAutoEdges, type AutoEdge } from '@/utils/autoEdges'
|
||||
import { buildZigbeeProperties, isZigbeeType } from '@/utils/zigbeeProperties'
|
||||
import { buildZwaveProperties, isZwaveType } from '@/utils/zwaveProperties'
|
||||
import { buildMacProperty } from '@/utils/macProperty'
|
||||
@@ -100,21 +101,10 @@ function deviceLabel(d: PendingDevice): string {
|
||||
return d.friendly_name ?? d.hostname ?? specialServiceName(d) ?? d.ip ?? d.ieee_address ?? 'device'
|
||||
}
|
||||
|
||||
function injectAutoEdges(edges: { id: string; source: string; target: string }[] | undefined) {
|
||||
function injectAutoEdges(edges: AutoEdge[] | undefined) {
|
||||
if (!edges || edges.length === 0) return
|
||||
useCanvasStore.setState((state) => ({
|
||||
edges: [
|
||||
...state.edges,
|
||||
...edges.map((e) => ({
|
||||
id: e.id,
|
||||
source: e.source,
|
||||
target: e.target,
|
||||
sourceHandle: 'bottom',
|
||||
targetHandle: 'top-t',
|
||||
type: 'iot',
|
||||
data: { type: 'iot' as const },
|
||||
})),
|
||||
],
|
||||
...applyAutoEdges(state.nodes, state.edges, edges),
|
||||
hasUnsavedChanges: true,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -9,9 +9,12 @@
|
||||
*/
|
||||
import type { ProxmoxNode } from './types'
|
||||
|
||||
/** Source/target handle IDs for a cluster link (see handleUtils.handleId). */
|
||||
/** Source/target handle IDs for a cluster link (see handleUtils.handleId).
|
||||
* Both are the bare slot-0 side names — the canonical stored form. React Flow
|
||||
* resolves a bare id to that side; a '-t' target id fails to resolve and falls
|
||||
* back to the top handle (which is why the target must be 'left', not 'left-t'). */
|
||||
export const CLUSTER_SOURCE_HANDLE = 'right'
|
||||
export const CLUSTER_TARGET_HANDLE = 'left-t'
|
||||
export const CLUSTER_TARGET_HANDLE = 'left'
|
||||
|
||||
export interface ClusterEdgeSpec {
|
||||
source: string
|
||||
|
||||
Reference in New Issue
Block a user