-
Is robot currently connected?
-
-
Robot is currently: {connected == null ? "checking..." : (connected ? "connected! 🟢" : "not connected... 🔴")}
-
- {connected == null ? "If checking continues, make sure CB is properly loaded with robot at least once." : ""}
-
-
-
- );
-}
diff --git a/src/pages/Home/Home.module.css b/src/pages/Home/Home.module.css
index 9ec90e5..73f7217 100644
--- a/src/pages/Home/Home.module.css
+++ b/src/pages/Home/Home.module.css
@@ -26,4 +26,52 @@ University within the Software Project course.
display: flex;
flex-direction: column;
gap: 1em;
+}
+
+.links {
+ display: flex;
+ flex-direction: row; /* Horizontal layout looks more like a dashboard */
+ gap: 1.5rem;
+ justify-content: center;
+ margin-top: 2rem;
+}
+
+.navCard {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 1rem 2rem;
+ min-width: 180px;
+ background-color: #ffffff;
+ color: #333;
+ text-decoration: none;
+ font-weight: 600;
+ border-radius: 12px;
+ border: 1px solid #e0e0e0;
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
+ transition: all 0.2s ease-in-out;
+}
+
+/* Hover effects */
+.navCard:hover {
+ transform: translateY(-4px);
+ box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1);
+ border-color: #ffcd00; /* UU Yellow accent */
+ color: #000;
+}
+
+/* Specific styling for the logo container */
+.logoPepperScaling {
+ display: flex;
+ justify-content: center;
+ transition: transform 0.3s ease;
+}
+
+.logoPepperScaling:hover {
+ transform: scale(1.05);
+}
+
+.logopepper {
+ height: 120px;
+ width: auto;
}
\ No newline at end of file
diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx
index b2ccb5c..07a2517 100644
--- a/src/pages/Home/Home.tsx
+++ b/src/pages/Home/Home.tsx
@@ -16,15 +16,20 @@ import styles from './Home.module.css'
function Home() {
return (
-
)
diff --git a/src/pages/Manuals/Manuals.module.css b/src/pages/Manuals/Manuals.module.css
new file mode 100644
index 0000000..ebaadf7
--- /dev/null
+++ b/src/pages/Manuals/Manuals.module.css
@@ -0,0 +1,81 @@
+/* This program has been developed by students from the bachelor Computer Science at Utrecht
+University within the Software Project course.
+© Copyright Utrecht University (Department of Information and Computing Sciences)
+*/
+
+.manualContainer {
+ width: 100%;
+ max-width: 800px;
+ margin: 4rem auto;
+ padding: 2rem;
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ text-align: center;
+}
+
+.manualHeader h1 {
+ font-size: 2.2rem;
+ margin-bottom: 0.5rem;
+}
+
+.buttonStack {
+ display: flex;
+ flex-direction: column; /* Stacks the manual sections vertically */
+ gap: 3rem;
+ margin-top: 3rem;
+ align-items: center;
+}
+
+.manualEntry {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 0.5rem;
+}
+
+.manualEntry h3 {
+ margin: 0;
+ font-size: 1.4rem;
+}
+
+.manualEntry p {
+ color: #666;
+ margin-bottom: 0.5rem;
+}
+
+.downloadBtn {
+ display: inline-block;
+ background-color: #ffffff; /* White background as requested */
+ color: #000;
+ padding: 1rem 2rem;
+ border-radius: 50px;
+ text-decoration: none;
+ font-weight: 700;
+ font-size: 1.1rem;
+ width: 280px; /* Fixed width for uniform appearance */
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
+ transition: transform 0.2s ease, background-color 0.2s ease, color 0.2s ease;
+}
+
+.downloadBtn:hover {
+ transform: translateY(-3px);
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
+ background-color: #247284; /* Teal hover as requested */
+ color: #ffffff; /* Text turns white on teal for better contrast */
+}
+
+.dateBadge {
+ display: inline-block;
+ margin-top: 1rem;
+ padding: 0.4rem 1rem;
+ background-color: #f0f0f0;
+ border-radius: 4px;
+ font-size: 0.85rem;
+ color: #888;
+}
+
+.divider {
+ margin-top: 4rem;
+ border: 0;
+ border-top: 1px solid #eee;
+ width: 60%;
+}
\ No newline at end of file
diff --git a/src/pages/Manuals/Manuals.tsx b/src/pages/Manuals/Manuals.tsx
new file mode 100644
index 0000000..2c88590
--- /dev/null
+++ b/src/pages/Manuals/Manuals.tsx
@@ -0,0 +1,39 @@
+// This program has been developed by students from the bachelor Computer Science at Utrecht
+// University within the Software Project course.
+// © Copyright Utrecht University (Department of Information and Computing Sciences)
+import styles from './Manuals.module.css';
+
+export default function Manuals() {
+ const userManualPath = "/UserManual.pdf";
+ const developerManualPath = "/DeveloperManual.pdf";
+
+ return (
+
+
+ Documentation & Manuals
+
+ Last Updated: January 2026
+
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/pages/Robot/Robot.tsx b/src/pages/Robot/Robot.tsx
deleted file mode 100644
index 80f4109..0000000
--- a/src/pages/Robot/Robot.tsx
+++ /dev/null
@@ -1,134 +0,0 @@
-// This program has been developed by students from the bachelor Computer Science at Utrecht
-// University within the Software Project course.
-// © Copyright Utrecht University (Department of Information and Computing Sciences)
-import { useState, useEffect, useRef } from 'react'
-import { API_BASE_URL } from '../../config/api.ts';
-
-/**
- * Displays a live robot interaction panel with user input, conversation history,
- * and real-time updates from the robot backend via Server-Sent Events (SSE).
- *
- * @returns A React element rendering the interactive robot UI.
- */
-export default function Robot() {
- /** The text message currently entered by the user. */
- const [message, setMessage] = useState('');
-
- /** Whether the robot’s microphone or listening mode is currently active. */
- const [listening, setListening] = useState(false);
- /** The ongoing conversation history as a sequence of user/assistant messages. */
- const [conversation, setConversation] = useState<
- {"role": "user" | "assistant", "content": string}[]>([])
- /** Reference to the scrollable conversation container for auto-scrolling. */
- const conversationRef = useRef
(null);
- /**
- * Index used to force refresh the SSE connection or clear conversation.
- * Incrementing this value triggers a reset of the live data stream.
- */
- const [conversationIndex, setConversationIndex] = useState(0);
-
- /**
- * Sends a message to the robot backend.
- *
- * Makes a POST request to `/message` with the user’s text.
- * The backend may respond with confirmation or error information.
- */
- const sendMessage = async () => {
- try {
- const response = await fetch(`${API_BASE_URL}/message`, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({ message }),
- });
- const data = await response.json();
- console.log(data);
- } catch (error) {
- console.error("Error sending message: ", error);
- }
- };
-
- /**
- * Establishes a persistent Server-Sent Events (SSE) connection
- * to receive real-time updates from the robot backend.
- *
- * Handles three event types:
- * - `voice_active`: whether the robot is currently listening.
- * - `speech`: recognized user speech input.
- * - `llm_response`: the robot’s language model-generated reply.
- *
- * The connection resets whenever `conversationIndex` changes.
- */
- useEffect(() => {
- const eventSource = new EventSource(`${API_BASE_URL}/sse`);
-
- eventSource.onmessage = (event) => {
- try {
- const data = JSON.parse(event.data);
- if ("voice_active" in data) setListening(data.voice_active);
- if ("speech" in data) setConversation(conversation => [...conversation, {"role": "user", "content": data.speech}]);
- if ("llm_response" in data) setConversation(conversation => [...conversation, {"role": "assistant", "content": data.llm_response}]);
- } catch {
- console.log("Unparsable SSE message:", event.data);
- }
- };
-
- return () => {
- eventSource.close();
- };
- }, [conversationIndex]);
-
- /**
- * Automatically scrolls the conversation view to the bottom
- * whenever a new message is added.
- */
- useEffect(() => {
- if (!conversationRef || !conversationRef.current) return;
- conversationRef.current.scrollTop = conversationRef.current.scrollHeight;
- }, [conversation]);
-
- return (
- <>
- Robot interaction
- Force robot speech
-
- setMessage(e.target.value)}
- onKeyDown={(e) => e.key === "Enter" && sendMessage().then(() => setMessage(""))}
- placeholder="Enter a message"
- />
-
-
-
-
Conversation
-
Listening {listening ? "🟢" : "🔴"}
-
- {conversation.map((item, i) => (
-
{item["content"]}
- ))}
-
-
-
-
-
-
- >
- );
-}
diff --git a/test/pages/connectedRobots/ConnectedRobots.test.tsx b/test/pages/connectedRobots/ConnectedRobots.test.tsx
deleted file mode 100644
index e5b1fc3..0000000
--- a/test/pages/connectedRobots/ConnectedRobots.test.tsx
+++ /dev/null
@@ -1,107 +0,0 @@
-// This program has been developed by students from the bachelor Computer Science at Utrecht
-// University within the Software Project course.
-// © Copyright Utrecht University (Department of Information and Computing Sciences)
-import { render, screen, act, cleanup, waitFor } from '@testing-library/react';
-import ConnectedRobots from '../../../src/pages/ConnectedRobots/ConnectedRobots';
-
-// Mock event source
-const mockInstances: MockEventSource[] = [];
-class MockEventSource {
- url: string;
- onmessage: ((event: MessageEvent) => void) | null = null;
- closed = false;
-
- constructor(url: string) {
- this.url = url;
- mockInstances.push(this);
- }
-
- sendMessage(data: string) {
- // Trigger whatever the component listens to
- this.onmessage?.({ data } as MessageEvent);
- }
-
- close() {
- this.closed = true;
- }
-}
-
-// mock event source generation with fake function that returns our fake mock source
-beforeAll(() => {
- // Cast globalThis to a type exposing EventSource and assign a mocked constructor.
- (globalThis as unknown as { EventSource?: typeof EventSource }).EventSource =
- jest.fn((url: string) => new MockEventSource(url)) as unknown as typeof EventSource;
-});
-
-// clean after tests
-afterEach(() => {
- cleanup();
- jest.restoreAllMocks();
- mockInstances.length = 0;
-});
-
-describe('ConnectedRobots', () => {
- test('renders initial state correctly', () => {
- render();
-
- // Check initial texts (before connection)
- expect(screen.getByText('Is robot currently connected?')).toBeInTheDocument();
- expect(screen.getByText(/Robot is currently:\s*checking/i)).toBeInTheDocument();
- expect(
- screen.getByText(/If checking continues, make sure CB is properly loaded/i)
- ).toBeInTheDocument();
- });
-
- test('updates to connected when message data is true', async () => {
- render();
- const eventSource = mockInstances[0];
- expect(eventSource).toBeDefined();
-
- // Check state after getting 'true' message
- await act(async () => {
- eventSource.sendMessage('true');
- });
-
- await waitFor(() => {
- expect(screen.getByText(/connected! 🟢/i)).toBeInTheDocument();
- });
- });
-
- test('updates to not connected when message data is false', async () => {
- render();
- const eventSource = mockInstances[0];
-
- // Check statew after getting 'false' message
- await act(async () => {
- eventSource.sendMessage('false');
- });
-
- await waitFor(() => {
- expect(screen.getByText(/not connected.*🔴/i)).toBeInTheDocument();
- });
- });
-
- test('handles invalid JSON gracefully', async () => {
- const logSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
- render();
- const eventSource = mockInstances[0];
-
- await act(async () => {
- eventSource.sendMessage('not-json');
- });
-
- expect(logSpy).toHaveBeenCalledWith(
- 'Ping message not in correct format:',
- 'not-json'
- );
- });
-
- test('closes EventSource on unmount', () => {
- render();
- const eventSource = mockInstances[0];
- const closeSpy = jest.spyOn(eventSource, 'close');
- cleanup();
- expect(closeSpy).toHaveBeenCalled();
- expect(eventSource.closed).toBe(true);
- });
-});
\ No newline at end of file
diff --git a/test/pages/robot/Robot.test.tsx b/test/pages/robot/Robot.test.tsx
deleted file mode 100644
index d2bd07a..0000000
--- a/test/pages/robot/Robot.test.tsx
+++ /dev/null
@@ -1,171 +0,0 @@
-// This program has been developed by students from the bachelor Computer Science at Utrecht
-// University within the Software Project course.
-// © Copyright Utrecht University (Department of Information and Computing Sciences)
-import { render, screen, act, cleanup, fireEvent } from '@testing-library/react';
-import Robot from '../../../src/pages/Robot/Robot';
-import { API_BASE_URL } from '../../../src/config/api.ts';
-
-// Mock EventSource
-const mockInstances: MockEventSource[] = [];
-class MockEventSource {
- url: string;
- onmessage: ((event: MessageEvent) => void) | null = null;
- closed = false;
-
- constructor(url: string) {
- this.url = url;
- mockInstances.push(this);
- }
-
- sendMessage(data: string) {
- this.onmessage?.({ data } as MessageEvent);
- }
-
- close() {
- this.closed = true;
- }
-}
-
-// Mock global EventSource
-beforeAll(() => {
- (globalThis as any).EventSource = jest.fn((url: string) => new MockEventSource(url));
-});
-
-// Mock fetch
-beforeEach(() => {
- globalThis.fetch = jest.fn(() =>
- Promise.resolve({
- json: () => Promise.resolve({ reply: 'ok' }),
- })
- ) as jest.Mock;
-});
-
-// Cleanup
-afterEach(() => {
- cleanup();
- jest.restoreAllMocks();
- mockInstances.length = 0;
-});
-
-describe('Robot', () => {
- test('renders initial state', () => {
- render();
- expect(screen.getByText('Robot interaction')).toBeInTheDocument();
- expect(screen.getByText('Force robot speech')).toBeInTheDocument();
- expect(screen.getByText('Listening 🔴')).toBeInTheDocument();
- expect(screen.getByPlaceholderText('Enter a message')).toBeInTheDocument();
- });
-
- test('sends message via button', async () => {
- render();
- const input = screen.getByPlaceholderText('Enter a message');
- const button = screen.getByText('Speak');
-
- fireEvent.change(input, { target: { value: 'Hello' } });
- await act(async () => fireEvent.click(button));
-
- expect(globalThis.fetch).toHaveBeenCalledWith(
- `${API_BASE_URL}/message`,
- expect.objectContaining({
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ message: 'Hello' }),
- })
- );
- });
-
- test('sends message via Enter key', async () => {
- render();
- const input = screen.getByPlaceholderText('Enter a message');
- fireEvent.change(input, { target: { value: 'Hi Enter' } });
-
- await act(async () =>
- fireEvent.keyDown(input, { key: 'Enter', code: 'Enter', charCode: 13 })
- );
-
- expect(globalThis.fetch).toHaveBeenCalledWith(
- `${API_BASE_URL}/message`,
- expect.objectContaining({
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ message: 'Hi Enter' }),
- })
- );
- expect((input as HTMLInputElement).value).toBe('');
- });
-
- test('handles fetch errors', async () => {
- globalThis.fetch = jest.fn(() => Promise.reject('Network error')) as jest.Mock;
- const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
-
- render();
- const input = screen.getByPlaceholderText('Enter a message');
- const button = screen.getByText('Speak');
- fireEvent.change(input, { target: { value: 'Error test' } });
-
- await act(async () => fireEvent.click(button));
-
- expect(consoleSpy).toHaveBeenCalledWith(
- 'Error sending message: ',
- 'Network error'
- );
- });
-
- test('updates conversation on SSE', async () => {
- render();
- const eventSource = mockInstances[0];
-
- await act(async () => {
- eventSource.sendMessage(JSON.stringify({ voice_active: true }));
- eventSource.sendMessage(JSON.stringify({ speech: 'User says hi' }));
- eventSource.sendMessage(JSON.stringify({ llm_response: 'Assistant replies' }));
- });
-
- expect(screen.getByText('Listening 🟢')).toBeInTheDocument();
- expect(screen.getByText('User says hi')).toBeInTheDocument();
- expect(screen.getByText('Assistant replies')).toBeInTheDocument();
- });
-
- test('handles invalid SSE JSON', async () => {
- const logSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
- render();
- const eventSource = mockInstances[0];
-
- await act(async () => eventSource.sendMessage('bad-json'));
-
- expect(logSpy).toHaveBeenCalledWith('Unparsable SSE message:', 'bad-json');
- });
-
- test('resets conversation with Reset button', async () => {
- render();
- const eventSource = mockInstances[0];
-
- await act(async () =>
- eventSource.sendMessage(JSON.stringify({ speech: 'Hello' }))
- );
- expect(screen.getByText('Hello')).toBeInTheDocument();
-
- fireEvent.click(screen.getByText('Reset'));
- expect(screen.queryByText('Hello')).not.toBeInTheDocument();
- });
-
- test('toggles conversationIndex with Stop/Start button', () => {
- render();
- const stopButton = screen.getByText('Stop');
- fireEvent.click(stopButton);
- expect(screen.getByText('Start')).toBeInTheDocument();
-
- fireEvent.click(screen.getByText('Start'));
- expect(screen.getByText('Stop')).toBeInTheDocument();
- });
-
- test('closes EventSource on unmount', () => {
- const { unmount } = render();
- const eventSource = mockInstances[0];
- const closeSpy = jest.spyOn(eventSource, 'close');
-
- unmount();
- expect(closeSpy).toHaveBeenCalled();
- expect(eventSource.closed).toBe(true);
- });
-});
--
2.49.1
From f626a6571adebfb93e6717986093d30ee85b6cb0 Mon Sep 17 00:00:00 2001
From: Pim Hutting
Date: Fri, 30 Jan 2026 20:58:03 +0100
Subject: [PATCH 2/2] chore: add pdfs
---
public/DeveloperManual.pdf | Bin 0 -> 1590269 bytes
public/UserManual.pdf | Bin 0 -> 1445225 bytes
2 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 public/DeveloperManual.pdf
create mode 100644 public/UserManual.pdf
diff --git a/public/DeveloperManual.pdf b/public/DeveloperManual.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..0e357c5b520051b79b16ee8b9db577ac697f3fc3
GIT binary patch
literal 1590269
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 iqve}Hd}4n
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 |