Compare commits

2 Commits

Author SHA1 Message Date
Björn Otgaar
cf64794f36 refactor: change the code for better readability 2026-01-12 12:21:17 +01:00
Björn Otgaar
a0a4687aeb chore: add support for dark mode in monitoring page 2026-01-10 12:14:37 +01:00
5 changed files with 178 additions and 101 deletions

View File

@@ -8,6 +8,9 @@
background-color: #242424;
--accent-color: #008080;
--panel-shadow:
0 1px 2px white,
0 8px 24px rgba(190, 186, 186, 0.253);
font-synthesis: none;
text-rendering: optimizeLegibility;
@@ -15,6 +18,14 @@
-moz-osx-font-smoothing: grayscale;
}
@media (prefers-color-scheme: dark) {
:root {
--panel-shadow:
0 1px 2px rgba(221, 221, 221, 0.178),
0 8px 24px rgba(27, 27, 27, 0.507);
}
}
html, body, #root {
margin: 0;
padding: 0;

View File

@@ -8,7 +8,8 @@
"footer footer";
gap: 1rem;
padding: 1rem;
background-color: #f5f5f5;
background-color: var(--bg-main);
color: var(--text-main);
font-family: Arial, sans-serif;
}
@@ -16,11 +17,14 @@
.experimentOverview {
grid-area: header;
display: flex;
color: color;
justify-content: space-between;
align-items: flex-start;
background: #fff;
background: var(--bg-surface);
color: var(--text-main);
box-shadow: var(--shadow);
padding: 1rem;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
box-shadow: var(--panel-shadow);
position: static; /* ensures it scrolls away */
}
@@ -35,16 +39,16 @@
margin: 0 3px;
text-align: center;
line-height: 25px;
background: #ccc;
background: gray;
}
.completed {
background-color: #5cb85c;
background-color: green;
color: white;
}
.current {
background-color: #f0ad4e;
background-color: rgb(255, 123, 0);
color: white;
}
@@ -60,22 +64,27 @@
.pausePlayInactive{
background-color: gray;
color: white;
}
.pausePlayActive{
background-color: green;
color: white;
}
.next {
background-color: lightgray;
background-color: #6c757d;
color: white;
}
.restartPhase{
background-color: rgb(255, 123, 0);
color: white;
}
.restartExperiment{
background-color: red;
color: white;
}
/* MAIN GRID */
@@ -85,18 +94,20 @@
grid-template-columns: repeat(2, 1fr);
grid-template-rows: repeat(2, auto);
gap: 1rem;
background: #fff;
background: var(--bg-surface);
color: var(--text-main);
padding: 1rem;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
box-shadow: var(--panel-shadow);
}
.phaseBox {
background: #f9f9f9;
border: 1px solid #ddd;
background: var(--bg-surface);
border: 1px solid var(--border-color);
box-shadow: var(--panel-shadow);
padding: 1rem;
display: flex;
flex-direction: column;
box-shadow: inset 0 0 3px rgba(0, 0, 0, 0.05);
}
.phaseOverviewText {
@@ -114,7 +125,7 @@
.phaseBox h3 {
margin-top: 0;
border-bottom: 1px solid #ddd;
border-bottom: 1px solid var(--border-color);
padding-bottom: 0.4rem;
}
@@ -125,15 +136,27 @@
/* LOGS */
.logs {
grid-area: logs;
background: #fff;
background: var(--bg-surface);
color: var(--text-main);
box-shadow: var(--panel-shadow);
padding: 1rem;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
}
.logs textarea {
width: 100%;
height: 200px;
height: 83%;
margin-top: 0.5rem;
background-color: Canvas;
color: CanvasText;
border: 1px solid var(--border-color);
}
.logs button {
background: var(--bg-surface);
box-shadow: var(--panel-shadow);
margin-top: 0.5rem;
margin-left: 0.5rem;
}
/* FOOTER */
@@ -142,11 +165,20 @@
display: flex;
justify-content: space-between;
gap: 1rem;
background: #fff;
background: var(--bg-surface);
color: var(--text-main);
box-shadow: var(--panel-shadow);
padding: 1rem;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
}
.controlsSection button {
background: var(--bg-surface);
box-shadow: var(--panel-shadow);
margin-top: 0.5rem;
margin-left: 0.5rem;
}
.gestures,
.speech,
.directSpeech {
@@ -161,15 +193,19 @@
.speechInput input {
flex: 1;
padding: 0.5rem;
border: 1px solid #ccc;
background-color: Canvas;
color: CanvasText;
border: 1px solid var(--border-color);
}
.speechInput button {
background: #007bff;
color: white;
border: none;
padding: 0.5rem 1rem;
cursor: pointer;
background-color: Canvas;
color: CanvasText;
border: 1px solid var(--border-color);
}
/* RESPONSIVE */

View File

@@ -9,6 +9,107 @@ type Trigger = { id?: string | number; label?: string ; achieved?: boolean };
type Norm = { id?: string | number; norm?: string };
/**
* Helper function for handling the experiment controls buttons.
* @param buttonType - Type of button that was pressed.
* @param setLoading - Reference to the function that sets the current loading value.
* @param _context - (Optional) context parameter that could get used in an API call.
* @param _endpoint - (Optional) context parameter that could get used in an API call.
*/
const handleButton = async (buttonType: string, setLoading: React.Dispatch<React.SetStateAction<boolean>>, _context?: string, _endpoint?: string,) => {
try {
setLoading(true);
switch (buttonType) {
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);
}
}
/**
* Defines the pages experiment controls section
* @returns an JSX React element
*/
const experimentControls = () => {
// Define use states for playing and loading
const [loading, setLoading] = React.useState(false);
const [isPlaying, setIsPlaying] = React.useState(false);
return (
<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", setLoading);}
}
disabled={loading}
></button>
{/*Play button*/}
<button
className={`${isPlaying ? styles.pausePlayActive : styles.pausePlayInactive}`}
onClick={() => {
setIsPlaying(true);
handleButton("play", setLoading);}
}
disabled={loading}
></button>
{/*Next button*/}
<button
className={styles.next}
onClick={() => handleButton("nextPhase", setLoading)}
disabled={loading}
>
</button>
{/*Restart Phase button*/}
<button
className={styles.restartPhase}
onClick={() => handleButton("resetPhase", setLoading)}
disabled={loading}
>
</button>
{/*Restart Experiment button*/}
<button
className={styles.restartExperiment}
onClick={() => handleButton("resetExperiment", setLoading)}
disabled={loading}
>
</button>
</div>
</div>
)
}
const MonitoringPage: React.FC = () => {
const getPhaseIds = useProgramStore((s) => s.getPhaseIds);
const getNormsInPhase = useProgramStore((s) => s.getNormsInPhase);
@@ -16,8 +117,7 @@ const MonitoringPage: React.FC = () => {
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);
@@ -32,35 +132,7 @@ const MonitoringPage: React.FC = () => {
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 */}
@@ -77,57 +149,8 @@ const MonitoringPage: React.FC = () => {
</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>
{/*Experiment Controls*/}
{experimentControls()}
<div className={styles.connectionStatus}>
{RobotConnected()}

View File

@@ -126,3 +126,10 @@
outline: red solid 2pt;
filter: drop-shadow(0 0 0.25rem red);
}
.backButton {
background: var(--bg-surface);
box-shadow: var(--panel-shadow);
margin-top: 0.5rem;
margin-left: 0.5rem;
}

View File

@@ -193,7 +193,7 @@ function VisProgPage() {
if (showSimpleProgram) {
return (
<div>
<button onClick={() => setShowSimpleProgram(false)}>
<button className={styles.backButton} onClick={() => setShowSimpleProgram(false)}>
Back to Editor
</button>
<MonitoringPage/>