feat: Added global warning for incomplete program chain

ref: N25B-450
This commit is contained in:
JGerla
2026-01-20 11:53:51 +01:00
parent 23a02b2b4a
commit 5d55ebaaa2
4 changed files with 47 additions and 6 deletions

View File

@@ -4,7 +4,7 @@ import {
Panel,
ReactFlow,
ReactFlowProvider,
MarkerType,
MarkerType, getOutgoers
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
import {type CSSProperties, useEffect, useState} from "react";
@@ -12,6 +12,7 @@ import {useShallow} from 'zustand/react/shallow';
import orderPhaseNodeArray from "../../utils/orderPhaseNodes.ts";
import useProgramStore from "../../utils/programStore.ts";
import {DndToolbar} from './visualProgrammingUI/components/DragDropSidebar.tsx';
import {type EditorWarning, globalWarning} from "./visualProgrammingUI/components/EditorWarnings.tsx";
import {WarningsSidebar} from "./visualProgrammingUI/components/WarningSidebar.tsx";
import type {PhaseNode} from "./visualProgrammingUI/nodes/PhaseNode.tsx";
import useFlowStore from './visualProgrammingUI/VisProgStores.tsx';
@@ -90,6 +91,26 @@ const VisProgUI = () => {
window.addEventListener('keydown', handler);
return () => window.removeEventListener('keydown', handler);
});
const {unregisterWarning, registerWarning} = useFlowStore();
useEffect(() => {
if (checkPhaseChain()) {
unregisterWarning(globalWarning,'INCOMPLETE_PROGRAM');
} else {
// create global warning for incomplete program chain
const incompleteProgramWarning : EditorWarning = {
scope: {
id: globalWarning,
handleId: undefined
},
type: 'INCOMPLETE_PROGRAM',
severity: "ERROR",
description: "there is no complete phase chain from the startNode to the EndNode"
}
registerWarning(incompleteProgramWarning);
}
},[edges, registerWarning, unregisterWarning])
@@ -184,6 +205,24 @@ function graphReducer() {
});
}
const checkPhaseChain = (): boolean => {
const {nodes, edges} = useFlowStore.getState();
function checkForCompleteChain(currentNodeId: string): boolean {
const outgoingPhases = getOutgoers({id: currentNodeId}, nodes, edges)
.filter(node => ["end", "phase"].includes(node.type!));
if (outgoingPhases.length === 0) return false;
if (outgoingPhases.some(node => node.type === "end" )) return true;
const next = outgoingPhases.map(node => checkForCompleteChain(node.id))
.find(result => result);
console.log(next);
return !!next;
}
return checkForCompleteChain('start');
};
/**

View File

@@ -9,7 +9,7 @@ import {
type XYPosition,
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
import { editorWarningRegistry } from "./components/EditorWarnings.tsx";
import {editorWarningRegistry} from "./components/EditorWarnings.tsx";
import type { FlowState } from './VisProgTypes';
import {
NodeDefaults,
@@ -50,7 +50,7 @@ function createNode(id: string, type: string, position: XYPosition, data: Record
const endNode = createNode('end', 'end', {x: 590, y: 100}, {label: "End"}, false)
const initialPhaseNode = createNode(crypto.randomUUID(), 'phase', {x:235, y:100}, {label: "Phase 1", children : [], isFirstPhase: false, nextPhaseId: null})
const initialNodes : Node[] = [startNode, endNode, initialPhaseNode,];
const initialNodes : Node[] = [startNode, endNode, initialPhaseNode];
// Initial edges, leave empty as setting initial edges...
// ...breaks logic that is dependent on connection events
@@ -312,4 +312,7 @@ const useFlowStore = create<FlowState>(UndoRedo((set, get) => ({
}))
);
export default useFlowStore;

View File

@@ -18,6 +18,7 @@ export type WarningType =
| 'MISSING_INPUT'
| 'MISSING_OUTPUT'
| 'PLAN_IS_UNDEFINED'
| 'INCOMPLETE_PROGRAM'
| string
export type WarningSeverity =

View File

@@ -7,7 +7,7 @@ import {useEffect} from "react";
import { Toolbar } from '../components/NodeComponents';
import styles from '../../VisProg.module.css';
import {SingleConnectionHandle} from "../components/RuleBasedHandle.tsx";
import type {EditorWarning} from "../components/EditorWarnings.tsx";
import {type EditorWarning} from "../components/EditorWarnings.tsx";
import {allowOnlyConnectionsFromHandle} from "../HandleRules.ts";
import useFlowStore from "../VisProgStores.tsx";
@@ -48,8 +48,6 @@ export default function StartNode(props: NodeProps<StartNode>) {
if (connections.length === 0) { registerWarning(noConnectionWarning); }
else { unregisterWarning(props.id, `${noConnectionWarning.type}:source`); }
}, [connections.length, props.id, registerWarning, unregisterWarning]);
return (
<>