All files / src/pages/VisProgPage VisProg.tsx

0% Statements 0/47
0% Branches 0/12
0% Functions 0/19
0% Lines 0/40

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221                                                                                                                                                                                                                                                                                                                                                                                                                                                         
import {
  Background,
  Controls,
  Panel,
  ReactFlow,
  ReactFlowProvider,
  MarkerType,
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
import {type CSSProperties, useEffect, useState} from "react";
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 {PhaseNode} from "./visualProgrammingUI/nodes/PhaseNode.tsx";
import useFlowStore from './visualProgrammingUI/VisProgStores.tsx';
import type {FlowState} from './visualProgrammingUI/VisProgTypes.tsx';
import styles from './VisProg.module.css'
import { NodeReduces, NodeTypes } from './visualProgrammingUI/NodeRegistry.ts';
import SaveLoadPanel from './visualProgrammingUI/components/SaveLoadPanel.tsx';
import MonitoringPage from '../MonitoringPage/MonitoringPage.tsx';
 
// --| config starting params for flow |--
 
 
/**
 * defines how the default edge looks inside the editor
 */
const DEFAULT_EDGE_OPTIONS = {
  type: 'default',
  markerEnd: {
    type: MarkerType.ArrowClosed,
    color: '#505050',
  },
};
 
 
/**
 * defines what functions in the FlowState store map to which names,
 * @param state
 */
const selector = (state: FlowState) => ({
  nodes: state.nodes,
  edges: state.edges,
  onNodesChange: state.onNodesChange,
  onEdgesDelete: state.onEdgesDelete,
  onEdgesChange: state.onEdgesChange,
  onConnect: state.onConnect,
  onReconnectStart: state.onReconnectStart,
  onReconnectEnd: state.onReconnectEnd,
  onReconnect: state.onReconnect,
  undo: state.undo,
  redo: state.redo,
  beginBatchAction: state.beginBatchAction,
  endBatchAction: state.endBatchAction,
  scrollable: state.scrollable
});
 
// --| define ReactFlow editor |--
 
/**
 * Defines the ReactFlow visual programming editor component
 * any implementations of editor logic should be encapsulated where possible
 * so the Component definition stays as readable as possible
 * @constructor
 */
const VisProgUI = () => {
  const {
    nodes, edges,
    onNodesChange,
    onEdgesDelete,
    onEdgesChange,
    onConnect,
    onReconnect,
    onReconnectStart,
    onReconnectEnd,
    undo,
    redo,
    beginBatchAction,
    endBatchAction,
    scrollable
  } = useFlowStore(useShallow(selector)); // instructs the editor to use the corresponding functions from the FlowStore
  const [zoom, setZoom] = useState(1);
  // adds ctrl+z and ctrl+y support to respectively undo and redo actions
  useEffect(() => {
    const handler = (e: KeyboardEvent) => {
      if (e.ctrlKey && e.key === 'z') undo();
      if (e.ctrlKey && e.key === 'y') redo();
    };
    window.addEventListener('keydown', handler);
    return () => window.removeEventListener('keydown', handler);
  });
 
  return (
    <div className={`${styles.innerEditorContainer} round-lg border-lg`} style={({'--flow-zoom': zoom} as CSSProperties)}>
      <ReactFlow
        nodes={nodes}
        edges={edges}
        defaultEdgeOptions={DEFAULT_EDGE_OPTIONS}
        nodeTypes={NodeTypes}
        onNodesChange={onNodesChange}
        onEdgesDelete={onEdgesDelete}
        onEdgesChange={onEdgesChange}
        onReconnect={onReconnect}
        onReconnectStart={onReconnectStart}
        onReconnectEnd={onReconnectEnd}
        onConnect={onConnect}
        onNodeDragStart={beginBatchAction}
        onNodeDragStop={endBatchAction}
        preventScrolling={scrollable}
        onMove={(_, viewport) => setZoom(viewport.zoom)}
        snapToGrid
        fitView
        proOptions={{hideAttribution: true}}
      >
        <Panel position="top-center" className={styles.dndPanel}>
          <DndToolbar/> {/* contains the drag and drop panel for nodes */}
          </Panel>
          <Panel position = "bottom-left" className={styles.saveLoadPanel}>
            <SaveLoadPanel></SaveLoadPanel>
          </Panel>
        <Panel position="bottom-center">
          <button onClick={() => undo()}>undo</button>
          <button onClick={() => redo()}>Redo</button>
        </Panel>
        <Controls/>
        <Background/>
      </ReactFlow>
    </div>
  );
};
 
/**
 * Places the VisProgUI component inside a ReactFlowProvider
 *
 * Wrapping the editor component inside a ReactFlowProvider
 * allows us to access and interact with the components inside the editor, outside the editor definition,
 * thus facilitating the addition of node specific functions inside their node definitions
 */
function VisualProgrammingUI() {
  return (
    <ReactFlowProvider>
      <VisProgUI/>
    </ReactFlowProvider>
  );
}
 
// currently outputs the prepared program to the console
export function runProgramm() {
  const phases = graphReducer();
  const program = {phases}
  console.log(JSON.stringify(program, null, 2));
  fetch(
    "http://localhost:8000/program",
    {
      method: "POST",
      headers: {"Content-Type": "application/json"},
      body: JSON.stringify(program),
    }
  ).then((res) => {
    if (!res.ok) throw new Error("Failed communicating with the backend.")
    console.log("Successfully sent the program to the backend.");
 
    // store reduced program in global program store for further use in the UI
    // when the program was sent to the backend successfully:
    useProgramStore.getState().setProgramState(structuredClone(program));
  }).catch(() => console.log("Failed to send program to the backend."));
  console.log(program);
}
 
/**
 * Reduces the graph into its phases' information and recursively calls their reducing function
 */ 
export function graphReducer() {
  const { nodes } = useFlowStore.getState();
  return orderPhaseNodeArray(nodes.filter((n) => n.type == 'phase') as PhaseNode [])
    .map((n) => {
      const reducer = NodeReduces['phase'];
      return reducer(n, nodes)
    }); 
}
 
 
 
/**
 * houses the entire page, so also UI elements
 * that are not a part of the Visual Programming UI
 * @constructor
 */
function VisProgPage() {
  const [showSimpleProgram, setShowSimpleProgram] = useState(false);
  const setProgramState = useProgramStore((state) => state.setProgramState);
 
  const runProgram = () => {
    const phases = graphReducer();       // reduce graph
    setProgramState({ phases });          // <-- save to store
    setShowSimpleProgram(true);          // show SimpleProgram
    runProgramm();                        // send to backend if needed
  };
 
  if (showSimpleProgram) {
    return (
    <div>
      <button className={styles.backButton} onClick={() => setShowSimpleProgram(false)}>
        Back to Editor ◀ 
      </button>
      <MonitoringPage/>
    </div>
    );
  }
 
  return (
    <>
      <VisualProgrammingUI/>
      <button onClick={runProgram}>run program</button>
    </>
  )
}
 
export default VisProgPage