Merge remote-tracking branch 'origin/temp_screenshot_manual' into feat/experiment-logs

# Conflicts:
#	src/pages/MonitoringPage/MonitoringPage.tsx
This commit is contained in:
Twirre Meulenbelt
2026-01-27 17:59:37 +01:00
42 changed files with 2250 additions and 525 deletions

View File

@@ -93,11 +93,6 @@
color: white;
}
.restartPhase{
background-color: rgb(255, 123, 0);
color: white;
}
.restartExperiment{
background-color: red;
color: white;

View File

@@ -31,11 +31,6 @@ import {
RobotConnected
} from './MonitoringPageComponents';
import ExperimentLogs from "./components/ExperimentLogs.tsx";
import Replay from "../../components/Icons/Replay.tsx";
import Pause from "../../components/Icons/Pause.tsx";
import Play from "../../components/Icons/Play.tsx";
import Next from "../../components/Icons/Next.tsx";
import Redo from "../../components/Icons/Redo.tsx";
// ----------------------------------------------------------------------
// 1. State management
@@ -107,7 +102,6 @@ function useExperimentLogic() {
}, [getPhaseIds, getGoalsInPhase, phaseIds, phaseIndex, phaseNames]);
const handleStatusUpdate = useCallback((data: unknown) => {
const payload = data as CondNormsStateUpdate;
if (payload.type !== 'cond_norms_state_update') return;
@@ -147,7 +141,7 @@ function useExperimentLogic() {
}
}, [setProgramState]);
const handleControlAction = async (action: "pause" | "play" | "nextPhase" | "resetPhase") => {
const handleControlAction = async (action: "pause" | "play" | "nextPhase") => {
try {
setLoading(true);
switch (action) {
@@ -162,7 +156,6 @@ function useExperimentLogic() {
case "nextPhase":
await nextPhase();
break;
// Case for resetPhase if implemented in API
}
} catch (err) {
console.error(err);
@@ -230,7 +223,7 @@ function ControlPanel({
}: {
loading: boolean,
isPlaying: boolean,
onAction: (a: "pause" | "play" | "nextPhase" | "resetPhase") => void,
onAction: (a: "pause" | "play" | "nextPhase") => void,
onReset: () => void
}) {
return (
@@ -241,31 +234,25 @@ function ControlPanel({
className={!isPlaying ? styles.pausePlayActive : styles.pausePlayInactive}
onClick={() => onAction("pause")}
disabled={loading}
><Pause /></button>
></button>
<button
className={isPlaying ? styles.pausePlayActive : styles.pausePlayInactive}
onClick={() => onAction("play")}
disabled={loading}
><Play /></button>
></button>
<button
className={styles.next}
onClick={() => onAction("nextPhase")}
disabled={loading}
><Next /></button>
<button
className={styles.restartPhase}
onClick={() => onAction("resetPhase")}
disabled={loading}
><Redo /></button>
></button>
<button
className={styles.restartExperiment}
onClick={onReset}
disabled={loading}
><Replay /></button>
></button>
</div>
</div>
);
@@ -285,14 +272,17 @@ function PhaseDashboard({
setActiveIds: React.Dispatch<React.SetStateAction<Record<string, boolean>>>,
goalIndex: number
}) {
const getGoals = useProgramStore((s) => s.getGoalsInPhase);
const getGoalsWithDepth = useProgramStore((s) => s.getGoalsWithDepth);
const getTriggers = useProgramStore((s) => s.getTriggersInPhase);
const getNorms = useProgramStore((s) => s.getNormsInPhase);
// Prepare data view models
const goals = (getGoals(phaseId) as GoalNode[]).map(g => ({
const goals = getGoalsWithDepth(phaseId).map((g) => ({
...g,
achieved: activeIds[g.id] ?? false,
id: g.id as string,
name: g.name as string,
achieved: activeIds[g.id as string] ?? false,
level: g.level, // Pass this new property to the UI
}));
const triggers = (getTriggers(phaseId) as TriggerNode[]).map(t => ({

View File

@@ -32,16 +32,6 @@ export async function nextPhase(): Promise<void> {
}
/**
* Sends an API call to the CB for going to reset the currect phase
* In case we can't go to the next phase, the function will throw an error.
*/
export async function resetPhase(): Promise<void> {
const type = "reset_phase"
const context = ""
sendAPICall(type, context)
}
/**
* Sends an API call to the CB for going to pause experiment
*/

View File

@@ -91,13 +91,14 @@ export const DirectSpeechInput: React.FC = () => {
};
// --- interface for goals/triggers/norms/conditional norms ---
type StatusItem = {
export type StatusItem = {
id?: string | number;
achieved?: boolean;
description?: string;
label?: string;
norm?: string;
name?: string;
level?: number;
};
interface StatusListProps {
@@ -129,7 +130,7 @@ export const StatusList: React.FC<StatusListProps> = ({
const isCurrentGoal = type === 'goal' && idx === currentGoalIndex;
const canOverride = (showIndicator && !isActive) || (type === 'cond_norm' && isActive);
const indentation = (item.level || 0) * 20;
const handleOverrideClick = () => {
if (!canOverride) return;
@@ -147,7 +148,10 @@ export const StatusList: React.FC<StatusListProps> = ({
};
return (
<li key={item.id ?? idx} className={styles.statusItem}>
<li key={item.id ?? idx}
className={styles.statusItem}
style={{ paddingLeft: `${indentation}px` }}
>
{showIndicator && (
<span
className={`${styles.statusIndicator} ${isActive ? styles.active : styles.inactive} ${canOverride ? styles.clickable : ''}`}