From 022a6708eadcdb50746d6f345d9b9adcf5a61e5f Mon Sep 17 00:00:00 2001 From: JGerla Date: Thu, 15 Jan 2026 16:03:33 +0100 Subject: [PATCH] feat: added a Warnings sidebar warnings are now displayed in the sidebar ref: N25B-450 --- package-lock.json | 16 ++- package.json | 1 + src/pages/VisProgPage/VisProg.tsx | 7 +- .../visualProgrammingUI/VisProgStores.tsx | 2 +- .../visualProgrammingUI/VisProgTypes.tsx | 2 +- .../{ => components}/EditorWarnings.tsx | 18 +++- .../components/WarningSidebar.module.css | 73 ++++++++++++++ .../components/WarningSidebar.tsx | 99 +++++++++++++++++++ .../visualProgrammingUI/nodes/StartNode.tsx | 2 +- 9 files changed, 208 insertions(+), 12 deletions(-) rename src/pages/VisProgPage/visualProgrammingUI/{ => components}/EditorWarnings.tsx (92%) create mode 100644 src/pages/VisProgPage/visualProgrammingUI/components/WarningSidebar.module.css create mode 100644 src/pages/VisProgPage/visualProgrammingUI/components/WarningSidebar.tsx diff --git a/package-lock.json b/package-lock.json index a52343e..cb39172 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@neodrag/react": "^2.3.1", "@xyflow/react": "^12.8.6", + "clsx": "^2.1.1", "react": "^19.1.1", "react-dom": "^19.1.1", "react-router": "^7.9.3", @@ -3971,6 +3972,15 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -6945,9 +6955,9 @@ } }, "node_modules/react-router": { - "version": "7.9.3", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.3.tgz", - "integrity": "sha512-4o2iWCFIwhI/eYAIL43+cjORXYn/aRQPgtFRRZb3VzoyQ5Uej0Bmqj7437L97N9NJW4wnicSwLOLS+yCXfAPgg==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.12.0.tgz", + "integrity": "sha512-kTPDYPFzDVGIIGNLS5VJykK0HfHLY5MF3b+xj0/tTyNYL1gF1qs7u67Z9jEhQk2sQ98SUaHxlG31g1JtF7IfVw==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", diff --git a/package.json b/package.json index 45f66df..ea362b7 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "dependencies": { "@neodrag/react": "^2.3.1", "@xyflow/react": "^12.8.6", + "clsx": "^2.1.1", "react": "^19.1.1", "react-dom": "^19.1.1", "react-router": "^7.9.3", diff --git a/src/pages/VisProgPage/VisProg.tsx b/src/pages/VisProgPage/VisProg.tsx index ef759c1..032dfb3 100644 --- a/src/pages/VisProgPage/VisProg.tsx +++ b/src/pages/VisProgPage/VisProg.tsx @@ -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 {WarningsSidebar} from "./visualProgrammingUI/components/WarningSidebar.tsx"; import type {PhaseNode} from "./visualProgrammingUI/nodes/PhaseNode.tsx"; import useFlowStore from './visualProgrammingUI/VisProgStores.tsx'; import type {FlowState} from './visualProgrammingUI/VisProgTypes.tsx'; @@ -93,7 +94,7 @@ const VisProgUI = () => { return ( -
+
{ snapToGrid fitView proOptions={{hideAttribution: true}} + style={{flexGrow: 3}} > {/* contains the drag and drop panel for nodes */} @@ -128,6 +130,7 @@ const VisProgUI = () => { +
); }; @@ -190,10 +193,8 @@ function graphReducer() { */ function VisProgPage() { const [programValidity, setProgramValidity] = useState(true); - const {isProgramValid, severityIndex} = useFlowStore(); - useEffect(() => { setProgramValidity(isProgramValid); // the following eslint disable is required as it wants us to use all possible dependencies for the useEffect statement, diff --git a/src/pages/VisProgPage/visualProgrammingUI/VisProgStores.tsx b/src/pages/VisProgPage/visualProgrammingUI/VisProgStores.tsx index 570d0df..8305bb2 100644 --- a/src/pages/VisProgPage/visualProgrammingUI/VisProgStores.tsx +++ b/src/pages/VisProgPage/visualProgrammingUI/VisProgStores.tsx @@ -9,7 +9,7 @@ import { type XYPosition, } from '@xyflow/react'; import '@xyflow/react/dist/style.css'; -import { editorWarningRegistry } from "./EditorWarnings.tsx"; +import { editorWarningRegistry } from "./components/EditorWarnings.tsx"; import type { FlowState } from './VisProgTypes'; import { NodeDefaults, diff --git a/src/pages/VisProgPage/visualProgrammingUI/VisProgTypes.tsx b/src/pages/VisProgPage/visualProgrammingUI/VisProgTypes.tsx index bf1bffd..afb1024 100644 --- a/src/pages/VisProgPage/visualProgrammingUI/VisProgTypes.tsx +++ b/src/pages/VisProgPage/visualProgrammingUI/VisProgTypes.tsx @@ -9,7 +9,7 @@ import type { OnEdgesDelete, OnNodesDelete } from '@xyflow/react'; -import type {EditorWarningRegistry} from "./EditorWarnings.tsx"; +import type {EditorWarningRegistry} from "./components/EditorWarnings.tsx"; import type {HandleRule} from "./HandleRuleLogic.ts"; import type { NodeTypes } from './NodeRegistry'; import type {FlowSnapshot} from "./EditorUndoRedo.ts"; diff --git a/src/pages/VisProgPage/visualProgrammingUI/EditorWarnings.tsx b/src/pages/VisProgPage/visualProgrammingUI/components/EditorWarnings.tsx similarity index 92% rename from src/pages/VisProgPage/visualProgrammingUI/EditorWarnings.tsx rename to src/pages/VisProgPage/visualProgrammingUI/components/EditorWarnings.tsx index 436fc21..a0c90d6 100644 --- a/src/pages/VisProgPage/visualProgrammingUI/EditorWarnings.tsx +++ b/src/pages/VisProgPage/visualProgrammingUI/components/EditorWarnings.tsx @@ -5,12 +5,11 @@ * - if there is no completely connected chain of startNode-[PhaseNodes]-EndNode * then hide any startNode, phaseNode, or endNode specific warnings */ +import useFlowStore from "../VisProgStores.tsx"; +import type {FlowState} from "../VisProgTypes.tsx"; // --| Type definitions |-- -import type {FlowState} from "./VisProgTypes.tsx"; - - export type WarningId = NodeId | "GLOBAL_WARNINGS"; export type NodeId = string; @@ -202,3 +201,16 @@ export function editorWarningRegistry(get: ZustandGet, set: ZustandSet) : Editor }, }} + +// returns a summary of the warningRegistry +export function warningSummary() { + const {severityIndex, isProgramValid} = useFlowStore.getState(); + return { + info: severityIndex.get('INFO')!.size, + warning: severityIndex.get('WARNING')!.size, + error: severityIndex.get('ERROR')!.size, + isValid: isProgramValid(), + }; +} + + diff --git a/src/pages/VisProgPage/visualProgrammingUI/components/WarningSidebar.module.css b/src/pages/VisProgPage/visualProgrammingUI/components/WarningSidebar.module.css new file mode 100644 index 0000000..182d99e --- /dev/null +++ b/src/pages/VisProgPage/visualProgrammingUI/components/WarningSidebar.module.css @@ -0,0 +1,73 @@ +.warnings-sidebar { + width: 320px; + height: 100%; + background: canvas; + border-left: 2px solid black; + display: flex; + flex-direction: column; +} + +.warnings-header { + padding: 12px; + border-bottom: 1px solid #2a2a2e; +} + +.severity-tabs { + display: flex; + gap: 4px; +} + +.severity-tab { + flex: 1; + padding: 4px; + background: ButtonFace; + color: GrayText; + border: none; + cursor: pointer; +} + +.count { + padding: 4px; + color: GrayText; + border: none; + cursor: pointer; +} + +.severity-tab.active { + color: ButtonText; + border: 2px solid currentColor; +} + +.warnings-list { + flex: 1; + overflow-y: auto; +} + +.warnings-empty { + margin: auto; +} + +.warning-item { + display: flex; + margin: 5px; + gap: 8px; + padding: 8px 12px; + border-radius: 5px; +} + +.warning-item--error { + border: 2px solid red; +} + +.warning-item--warning { + border-left: 3px solid orange; +} + +.warning-item--info { + border-left: 3px solid steelblue; +} + +.warning-item .meta { + font-size: 11px; + opacity: 0.6; +} \ No newline at end of file diff --git a/src/pages/VisProgPage/visualProgrammingUI/components/WarningSidebar.tsx b/src/pages/VisProgPage/visualProgrammingUI/components/WarningSidebar.tsx new file mode 100644 index 0000000..b91a17a --- /dev/null +++ b/src/pages/VisProgPage/visualProgrammingUI/components/WarningSidebar.tsx @@ -0,0 +1,99 @@ +import clsx from "clsx"; +import {useEffect, useState} from "react"; +import useFlowStore from "../VisProgStores.tsx"; +import { + warningSummary, + type WarningSeverity, type EditorWarning +} from "./EditorWarnings.tsx"; +import styles from "./WarningSidebar.module.css"; + +export function WarningsSidebar() { + const warnings = useFlowStore.getState().getWarnings(); + + const [severityFilter, setSeverityFilter] = useState('ALL'); + + useEffect(() => {}, [warnings]); + const filtered = severityFilter === 'ALL' + ? warnings + : warnings.filter(w => w.severity === severityFilter); + + return ( + + ); +} + +function WarningsHeader({ + severityFilter, + onChange, +}: { + severityFilter: WarningSeverity | 'ALL'; + onChange: (severity: WarningSeverity | 'ALL') => void; +}) { + const summary = warningSummary(); + + return ( +
+

Warnings

+ +
+ {(['ALL', 'ERROR', 'WARNING', 'INFO'] as const).map(severity => ( + + ))} +
+
+ ); +} + + + +function WarningsList(props: { warnings: EditorWarning[] }) { + if (props.warnings.length === 0) { + return ( +
+ No warnings! +
+ ) + } + return ( +
+ {props.warnings.map((warning) => ( + + ))} +
+ ); +} + +function WarningListItem(props: { warning: EditorWarning }) { + return ( +
+
+ {props.warning.description} +
+ +
+ {props.warning.scope.id} + {props.warning.scope.handleId && ( + @{props.warning.scope.handleId} + )} +
+
+ ); +} \ No newline at end of file diff --git a/src/pages/VisProgPage/visualProgrammingUI/nodes/StartNode.tsx b/src/pages/VisProgPage/visualProgrammingUI/nodes/StartNode.tsx index d4bf0f6..3d6c2b2 100644 --- a/src/pages/VisProgPage/visualProgrammingUI/nodes/StartNode.tsx +++ b/src/pages/VisProgPage/visualProgrammingUI/nodes/StartNode.tsx @@ -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 "../EditorWarnings.tsx"; +import type {EditorWarning} from "../components/EditorWarnings.tsx"; import {allowOnlyConnectionsFromHandle} from "../HandleRules.ts"; import useFlowStore from "../VisProgStores.tsx";