build: merge

ref: N25B-402
This commit is contained in:
JobvAlewijk
2026-01-02 19:56:01 +01:00
8 changed files with 258 additions and 1 deletions

View File

@@ -9,6 +9,7 @@ import {
import '@xyflow/react/dist/style.css';
import {useEffect, useState} from "react";
import {useShallow} from 'zustand/react/shallow';
import useProgramStore from "../../utils/programStore.ts";
import {DndToolbar} from './visualProgrammingUI/components/DragDropSidebar.tsx';
import useFlowStore from './visualProgrammingUI/VisProgStores.tsx';
import type {FlowState} from './visualProgrammingUI/VisProgTypes.tsx';
@@ -153,6 +154,10 @@ function runProgramm() {
).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."));
}

View File

@@ -45,7 +45,7 @@ const initialNodes : Node[] = [
createNode('start', 'start', {x: 100, y: 100}, {label: "Start"}, false),
createNode('end', 'end', {x: 500, y: 100}, {label: "End"}, false),
createNode('phase-1', 'phase', {x:200, y:100}, {label: "Phase 1", children : []}),
createNode('norms-1', 'norm', {x:-200, y:100}, {label: "Initial Norms", normList: ["Be a robot", "get good"]}),
createNode('norms-1', 'norm', {x:-200, y:100}, {label: "Initial Norms", normList: ["Be a robot", "get good"], critical:false}),
];
// * Initial edges * /

View File

@@ -8,4 +8,5 @@ export const NormNodeDefaults: NormNodeData = {
droppable: true,
norm: "",
hasReduce: true,
critical: false,
};

View File

@@ -21,6 +21,7 @@ export type NormNodeData = {
droppable: boolean;
norm: string;
hasReduce: boolean;
critical: boolean;
};
export type NormNode = Node<NormNodeData>
@@ -35,11 +36,16 @@ export default function NormNode(props: NodeProps<NormNode>) {
const {updateNodeData} = useFlowStore();
const text_input_id = `norm_${props.id}_text_input`;
const checkbox_id = `goal_${props.id}_checkbox`;
const setValue = (value: string) => {
updateNodeData(props.id, {norm: value});
}
const setCritical = (value: boolean) => {
updateNodeData(props.id, {...data, critical: value});
}
return <>
<Toolbar nodeId={props.id} allowDelete={true}/>
<div className={`${styles.defaultNode} ${styles.nodeNorm}`}>
@@ -52,6 +58,15 @@ export default function NormNode(props: NodeProps<NormNode>) {
placeholder={"Pepper should ..."}
/>
</div>
<div className={"flex-row gap-md align-center"}>
<label htmlFor={checkbox_id}>Critical:</label>
<input
id={checkbox_id}
type={"checkbox"}
checked={data.critical || false}
onChange={(e) => setCritical(e.target.checked)}
/>
</div>
<Handle type="source" position={Position.Right} id="norms"/>
</div>
</>;
@@ -69,6 +84,7 @@ export function NormReduce(node: Node, _nodes: Node[]) {
id: node.id,
label: data.label,
norm: data.norm,
critical: data.critical,
}
}

81
src/utils/programStore.ts Normal file
View File

@@ -0,0 +1,81 @@
import {create} from "zustand";
// the type of a reduced program
export type ReducedProgram = { phases: Record<string, unknown>[] };
/**
* the type definition of the programStore
*/
export type ProgramState = {
// Basic store functionality:
currentProgram: ReducedProgram;
setProgramState: (state: ReducedProgram) => void;
getProgramState: () => ReducedProgram;
// Utility functions:
// to avoid having to manually go through the entire state for every instance where data is required
getPhaseIds: () => string[];
getNormsInPhase: (currentPhaseId: string) => Record<string, unknown>[];
getGoalsInPhase: (currentPhaseId: string) => Record<string, unknown>[];
getTriggersInPhase: (currentPhaseId: string) => Record<string, unknown>[];
// if more specific utility functions are needed they can be added here:
}
/**
* the ProgramStore can be used to access all information of the most recently sent program,
* it contains basic functions to set and get the current program.
* And it contains some utility functions that allow you to easily gain access
* to the norms, triggers and goals of a specific phase.
*/
const useProgramStore = create<ProgramState>((set, get) => ({
currentProgram: { phases: [] as Record<string, unknown>[]},
/**
* sets the current program by cloning the provided program using a structuredClone
*/
setProgramState: (program: ReducedProgram) => set({currentProgram: structuredClone(program)}),
/**
* gets the current program
*/
getProgramState: () => get().currentProgram,
// utility functions:
/**
* gets the ids of all phases in the program
*/
getPhaseIds: () => get().currentProgram.phases.map(entry => entry["id"] as string),
/**
* gets the norms for the provided phase
*/
getNormsInPhase: (currentPhaseId) => {
const program = get().currentProgram;
const phase = program.phases.find(val => val["id"] === currentPhaseId);
if (phase) {
return phase["norms"] as Record<string, unknown>[];
}
throw new Error(`phase with id:"${currentPhaseId}" not found`)
},
/**
* gets the goals for the provided phase
*/
getGoalsInPhase: (currentPhaseId) => {
const program = get().currentProgram;
const phase = program.phases.find(val => val["id"] === currentPhaseId);
if (phase) {
return phase["goals"] as Record<string, unknown>[];
}
throw new Error(`phase with id:"${currentPhaseId}" not found`)
},
/**
* gets the triggers for the provided phase
*/
getTriggersInPhase: (currentPhaseId) => {
const program = get().currentProgram;
const phase = program.phases.find(val => val["id"] === currentPhaseId);
if (phase) {
return phase["triggers"] as Record<string, unknown>[];
}
throw new Error(`phase with id:"${currentPhaseId}" not found`)
}
}));
export default useProgramStore;