176 lines
5.5 KiB
TypeScript
176 lines
5.5 KiB
TypeScript
import React from 'react';
|
|
import styles from './MonitoringPage.module.css';
|
|
import useProgramStore from "../../utils/programStore.ts";
|
|
import { GestureControls, SpeechPresets, DirectSpeechInput, StatusList, RobotConnected } from './Components';
|
|
import { nextPhase, pauseExperiment, playExperiment, resetExperiment, resetPhase } from ".//MonitoringPageAPI.ts"
|
|
|
|
type Goal = { id?: string | number; description?: string; achieved?: boolean };
|
|
type Trigger = { id?: string | number; label?: string ; achieved?: boolean };
|
|
type Norm = { id?: string | number; norm?: string };
|
|
|
|
|
|
const MonitoringPage: React.FC = () => {
|
|
const getPhaseIds = useProgramStore((s) => s.getPhaseIds);
|
|
const getNormsInPhase = useProgramStore((s) => s.getNormsInPhase);
|
|
const getGoalsInPhase = useProgramStore((s) => s.getGoalsInPhase);
|
|
const getTriggersInPhase = useProgramStore((s) => s.getTriggersInPhase);
|
|
|
|
// Can be used to block actions until feedback from CB.
|
|
const [loading, setLoading] = React.useState(false);
|
|
const [isPlaying, setIsPlaying] = React.useState(false);
|
|
|
|
const phaseIds = getPhaseIds();
|
|
const [phaseIndex, _] = React.useState(0);
|
|
|
|
if (phaseIds.length === 0) {
|
|
return <p className={styles.empty}>No program loaded.</p>;
|
|
}
|
|
|
|
const phaseId = phaseIds[phaseIndex];
|
|
|
|
const goals = getGoalsInPhase(phaseId) as Goal[];
|
|
const triggers = getTriggersInPhase(phaseId) as Trigger[];
|
|
const norms = getNormsInPhase(phaseId) as Norm[];
|
|
|
|
// Handle logic of 'next' button.
|
|
|
|
const handleButton = async (button: string, _context?: string, _endpoint?: string) => {
|
|
try {
|
|
setLoading(true);
|
|
switch (button) {
|
|
case "pause":
|
|
await pauseExperiment();
|
|
break;
|
|
case "play":
|
|
await playExperiment();
|
|
break;
|
|
case "nextPhase":
|
|
await nextPhase();
|
|
break;
|
|
case "resetPhase":
|
|
await resetPhase();
|
|
break;
|
|
case "resetExperiment":
|
|
await resetExperiment();
|
|
break;
|
|
default:
|
|
}
|
|
} catch (err) {
|
|
console.error(err);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
return (
|
|
<div className={styles.dashboardContainer}>
|
|
{/* HEADER */}
|
|
<header className={styles.experimentOverview}>
|
|
<div className={styles.phaseName}>
|
|
<h2>Experiment Overview</h2>
|
|
<p><strong>Phase name:</strong> Rhyming fish</p>
|
|
<div className={styles.phaseProgress}>
|
|
<span className={`${styles.phase} ${styles.completed}`}>1</span>
|
|
<span className={`${styles.phase} ${styles.completed}`}>2</span>
|
|
<span className={`${styles.phase} ${styles.current}`}>3</span>
|
|
<span className={styles.phase}>4</span>
|
|
<span className={styles.phase}>5</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className={styles.experimentControls}>
|
|
<h3>Experiment Controls</h3>
|
|
<div className={styles.controlsButtons}>
|
|
{/*Pause button*/}
|
|
<button
|
|
className={`${!isPlaying ? styles.pausePlayActive : styles.pausePlayInactive}`}
|
|
onClick={() => {
|
|
setIsPlaying(false);
|
|
handleButton("pause");}
|
|
}
|
|
disabled={loading}
|
|
>❚❚</button>
|
|
|
|
{/*Play button*/}
|
|
<button
|
|
className={`${isPlaying ? styles.pausePlayActive : styles.pausePlayInactive}`}
|
|
onClick={() => {
|
|
setIsPlaying(true);
|
|
handleButton("play");}
|
|
}
|
|
disabled={loading}
|
|
>▶</button>
|
|
|
|
{/*Next button*/}
|
|
<button
|
|
className={styles.next}
|
|
onClick={() => handleButton("nextPhase")}
|
|
disabled={loading}
|
|
>
|
|
⏭
|
|
</button>
|
|
|
|
{/*Restart Phase button*/}
|
|
<button
|
|
className={styles.restartPhase}
|
|
onClick={() => handleButton("resetPhase")}
|
|
disabled={loading}
|
|
>
|
|
↩
|
|
</button>
|
|
|
|
{/*Restart Experiment button*/}
|
|
<button
|
|
className={styles.restartExperiment}
|
|
onClick={() => handleButton("resetExperiment")}
|
|
disabled={loading}
|
|
>
|
|
⟲
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className={styles.connectionStatus}>
|
|
{RobotConnected()}
|
|
</div>
|
|
</header>
|
|
|
|
{/* MAIN GRID */}
|
|
|
|
<main className={styles.phaseOverview}>
|
|
<section className={styles.phaseOverviewText}>
|
|
<h3>Phase Overview</h3>
|
|
</section>
|
|
|
|
<StatusList title="Goals" items={goals} type="goal" />
|
|
<StatusList title="Triggers" items={triggers} type="trigger" />
|
|
<StatusList title="Norms" items={norms} type="norm" />
|
|
<StatusList
|
|
title="Conditional Norms"
|
|
items={[{ id: 'cn_1', norm: '“RP is sad” - Be nice', achieved: false }]}
|
|
type="cond_norm"
|
|
/>
|
|
</main>
|
|
|
|
{/* LOGS */}
|
|
<aside className={styles.logs}>
|
|
<h3>Logs</h3>
|
|
<div className={styles.logHeader}>
|
|
<span>Global:</span>
|
|
<button>ALL</button>
|
|
<button>Add</button>
|
|
<button className={styles.live}>Live</button>
|
|
</div>
|
|
<textarea defaultValue="Example Log: much log"></textarea>
|
|
</aside>
|
|
|
|
{/* FOOTER */}
|
|
<footer className={styles.controlsSection}>
|
|
<GestureControls />
|
|
<SpeechPresets />
|
|
<DirectSpeechInput />
|
|
</footer>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default MonitoringPage; |