diff --git a/MVP/package-lock.json b/MVP/package-lock.json
index d1a6d9f..7bacc2b 100644
--- a/MVP/package-lock.json
+++ b/MVP/package-lock.json
@@ -12,7 +12,8 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-icons": "^5.2.1",
- "react-mic": "^12.4.6"
+ "react-mic": "^12.4.6",
+ "react-router-dom": "^6.26.2"
},
"devDependencies": {
"@eslint/js": "^9.8.0",
@@ -2507,6 +2508,15 @@
"node": ">= 8"
}
},
+ "node_modules/@remix-run/router": {
+ "version": "1.19.2",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.2.tgz",
+ "integrity": "sha512-baiMx18+IMuD1yyvOGaHM9QrVUPGGG0jC+z+IPHnRJWUAUvaKuWKyE8gjDj2rzv3sz9zOGoRSPgeBVHRhZnBlA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
"node_modules/@rollup/plugin-node-resolve": {
"version": "15.2.3",
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz",
@@ -6035,6 +6045,38 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-router": {
+ "version": "6.26.2",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.2.tgz",
+ "integrity": "sha512-tvN1iuT03kHgOFnLPfLJ8V95eijteveqdOSk+srqfePtQvqCExB8eHOYnlilbOcyJyKnYkr1vJvf7YqotAJu1A==",
+ "license": "MIT",
+ "dependencies": {
+ "@remix-run/router": "1.19.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8"
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "6.26.2",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.2.tgz",
+ "integrity": "sha512-z7YkaEW0Dy35T3/QKPYB1LjMK2R1fxnHO8kWpUMTBdfVzZrWOiY9a7CtN8HqdWtDUWd5FY6Dl8HFsqVwH4uOtQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@remix-run/router": "1.19.2",
+ "react-router": "6.26.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8",
+ "react-dom": ">=16.8"
+ }
+ },
"node_modules/reflect.getprototypeof": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz",
diff --git a/MVP/package.json b/MVP/package.json
index d09ce3f..cca698a 100644
--- a/MVP/package.json
+++ b/MVP/package.json
@@ -14,7 +14,8 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-icons": "^5.2.1",
- "react-mic": "^12.4.6"
+ "react-mic": "^12.4.6",
+ "react-router-dom": "^6.26.2"
},
"devDependencies": {
"@eslint/js": "^9.8.0",
diff --git a/MVP/src/App.jsx b/MVP/src/App.jsx
index 6460729..d952022 100644
--- a/MVP/src/App.jsx
+++ b/MVP/src/App.jsx
@@ -1,146 +1,24 @@
-import { useState, useRef } from 'react';
-import './App.css';
-import VoiceRecorder from './VoiceRecorder';
-import './assets/mvp.css';
-import { FaPaperclip } from 'react-icons/fa';
+import { HashRouter as Router, Routes, Route } from 'react-router-dom';
+import Home from './components/Home';
+import Report from './components/Report';
+import Header from './components/Header';
+import Footer from './components/Footer';
+import { StudentsProvider } from './contexts/Students';
function App() {
- const [isRecording, setIsRecording] = useState(false);
- const [audioUrl, setAudioUrl] = useState(null);
- const [transcription, setTranscription] = useState('');
- const [aiResponse, setAiResponse] = useState('');
- const [isUploading, setIsUploading] = useState(false); // Track uploading state
-
- const fileInputRef = useRef(null);
-
- const handleStopRecording = async (blob) => {
- const formData = new FormData();
- formData.append('file', blob.blob, 'recording.mp3');
-
- setIsUploading(true); // Set uploading state to true
- callAjax(formData, (response) => {
- setIsUploading(false); // Reset uploading state
- if (response && response.transcription) {
- setTranscription(response.transcription);
- setAudioUrl(URL.createObjectURL(blob.blob));
- } else {
- console.error("Failed to get a transcription from the server.");
- }
- });
- };
-
- const handleFileChange = async (event) => {
- const file = event.target.files[0];
- if (file) {
- console.log("Selected file: ", file);
-
- const formData = new FormData();
- formData.append('file', file, file.name); // Ensure filename is set
-
- setIsUploading(true); // Set uploading state to true
-
- callAjax(formData, (response) => {
- setIsUploading(false); // Reset uploading state
- if (response && response.transcription) {
- setTranscription(response.transcription);
- setAudioUrl(URL.createObjectURL(file)); // URL for the uploaded file
- } else {
- console.error("Failed to get a transcription from the server.");
- }
- });
- }
- };
-
-
- const callAjax = (formData, callback) => {
- window.clicnical_coach_jsmo_module.transcribeAudio(formData, (res) => {
- if (res && res.transcription) {
- if (callback) callback(res);
- } else {
- console.log("Unexpected response format:", res);
- }
- }, (err) => {
- console.log("transcribeAudio error:", err);
- if (callback) callback();
- });
- };
-
- const handleSubmitToAI = () => {
- const chatmlPayload = [
- { role: "system", content: "You are a helpful assistant." },
- { role: "user", content: transcription }
- ];
-
- callAI(chatmlPayload, (aiContent) => {
- if (aiContent) {
- setAiResponse(aiContent);
- } else {
- console.error("Failed to get a response from the AI.");
- }
- });
- };
-
- const callAI = (chatmlPayload, callback) => {
- window.clicnical_coach_jsmo_module.callAI(chatmlPayload, (res) => {
- if (res) {
- if (callback) callback(res);
- } else {
- console.log("Unexpected AI response format:", res);
- }
- }, (err) => {
- console.log("callAI error:", err);
- if (callback) callback();
- });
- };
-
return (
-
-
Clinical Coach - MVP
-
-
-
-
-
+
+
+
+
+
+ } />
+ } />
+
+
- {isUploading && (
- Uploading...
- )}
- {audioUrl && (
-
- )}
- {transcription && (
-
-
- "{transcription}"
-
-
-
- )}
- {aiResponse && (
-
- )}
-
-
+
+
);
}
diff --git a/MVP/src/assets/mvp.css b/MVP/src/assets/mvp.css
index b5e62ff..668e7d1 100644
--- a/MVP/src/assets/mvp.css
+++ b/MVP/src/assets/mvp.css
@@ -1,3 +1,4 @@
+/* General styling for body and container */
body, html {
height: 100%;
margin: 0;
@@ -5,68 +6,269 @@ body, html {
justify-content: center;
align-items: center;
background-color: #f5f5f5;
+ overflow: hidden; /* Prevent body from scrolling */
+}
+
+#clinicalcoach_ui_container {
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+ max-height: 100vh; /* Limit to full viewport height */
+ width: 100vw;
+ max-width: 600px;
+ margin: 0 auto;
}
#clinicalcoachmvp_container {
border: 1px solid #ccc;
border-radius: 8px;
- padding: 20px;
background-color: white;
box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1);
- max-width: 600px;
width: 100%;
text-align: center;
+ flex-grow: 1; /* Allows it to grow vertically */
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ overflow: hidden; /* Ensure the footer stays in place */
}
-h1 {
- font-size: 2rem;
- margin-bottom: 20px;
- color: #333;
+/* Header and Footer Styling */
+header {
+ background-color: transparent;
+ padding: 1rem;
+ text-align: center;
+ border: none;
+ flex-shrink: 0; /* Prevents shrinking */
}
-.card {
+/* Footer Styling */
+footer {
+ position: relative;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ background-color: #979DC0; /* Your desired footer color */
+ color: white;
+ padding: 1rem;
display: flex;
justify-content: center;
align-items: center;
+ border-top: 1px solid #ccc;
+ width: 100%;
+ max-width: 600px;
+ margin: 0 auto;
+ z-index: 1;
+ flex-shrink: 0; /* Prevents shrinking */
+ overflow: visible;
+}
+
+/* Left concave curve */
+footer::before {
+ content: "";
+ position: absolute;
+ top: -30px;
+ left: 0;
+ width: 40px;
+ height: 20px;
+ background-color: transparent;
+ box-shadow: none;
+ border-right: 50px solid transparent;
+ border-top: 50px solid transparent;
+ border-bottom: 0px solid transparent;
+ border-lefT: 90px solid #979dc0;
+}
+
+/* Right concave curve */
+footer::after {
+ content: "";
+ position: absolute;
+ top: -30px;
+ right: 0;
+ width: 40px;
+ height: 20px;
+ box-shadow: none;
+ border-left: 50px solid transparent;
+ border-top: 50px solid transparent;
+ border-bottom: 0px solid transparent;
+ border-right: 90px solid #979dc0;
+}
+
+/* Adjust the content area */
+.scrollable-content {
+ overflow-y: auto; /* Enable scrolling if content overflows */
+ flex-grow: 1;
+ max-height: calc(100vh - 120px); /* Adjust to leave space for header and footer */
+ padding: 20px;
+}
+
+/* Footer Content - Align Record Button and Dropdown */
+.footer-content {
+ display: flex;
flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 10px; /* Space between heading and buttons */
+ margin-top: 0;
+ text-align: center;
}
-button {
+/* Create a horizontal row for the button and dropdown */
+.footer-controls {
+ display: flex;
+ flex-direction: row; /* Ensure button and dropdown are side by side */
+ gap: 20px; /* Add space between button and dropdown */
+ align-items: center;
+ justify-content: center;
+}
+
+/* Footer Heading Styling */
+.footer-content h3 {
+ font-size: 1.2rem; /* Smaller size */
+ font-weight: normal; /* Normal weight */
+ color: #333; /* Darker color */
+ margin-bottom: 10px; /* Slight spacing below heading */
+}
+
+/* Dropdown styling */
+.student-dropdown {
+ padding: 10px;
+ font-size: 1rem;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+}
+
+/* Circular button styling */
+.circle-recording-btn {
background-color: #28a745;
color: white;
- padding: 10px 20px;
+ width: 100px;
+ height: 100px;
border: none;
- border-radius: 4px;
- font-size: 1rem;
+ border-radius: 50%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ font-size: 1.5rem;
cursor: pointer;
transition: background-color 0.3s ease;
+ position: relative;
+ box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1);
}
-button:hover {
- background-color: #218838;
+/* Pulsating animation for recording state */
+.circle-recording-btn.recording {
+ background-color: red;
+ animation: pulse 1s infinite;
}
-.controls {
+/* Animation for recording button */
+@keyframes pulse {
+ 0% {
+ transform: scale(1);
+ box-shadow: 0 0 5px rgba(255, 0, 0, 0.8);
+ }
+ 50% {
+ transform: scale(1.05);
+ box-shadow: 0 0 15px rgba(255, 0, 0, 1);
+ }
+ 100% {
+ transform: scale(1);
+ box-shadow: 0 0 5px rgba(255, 0, 0, 0.8);
+ }
+}
+
+.react_mic_container {
+ visibility: hidden;
+ position:absolute;
+ z-index:-999;
+}
+.home-content {
+ padding: 20px;
+}
+
+.session-date {
+ background-color: #f0f0f0;
+ padding: 10px;
+ margin-bottom: 20px;
+}
+
+.session-date h2 {
+ font-size: 1rem;
+ color: #666;
+ margin: 0;
+ text-align: left;
+}
+
+.student-card {
+ background-color: #f5f5f5;
+ border: 1px solid #ccc;
+ border-radius: 10px;
+ padding: 15px;
display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ justify-content: space-between;
+ margin-bottom: 20px;
+}
+
+.student-info {
+ display: flex;
+ flex-direction: column;
align-items: center;
- justify-content: center;
+ margin-right: 20px;
}
-.controls button {
- margin:0;
- padding:0;
- vertical-align: middle;
+.student-icon {
+ color: #666;
}
-.controls .attachment-button {
- background-color: transparent;
- margin:0 10px 10px;
- cursor: pointer;
+.student-name {
+ margin-top: 10px;
font-size: 1.5rem;
- color: #555;
+ text-align: center;
+}
+
+.session-details {
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+ align-items: flex-start;
+}
+
+.session-status {
+ background-color: #979DC0;
+ padding: 10px 20px;
+ border-radius: 5px;
+ color: white;
+ text-align: center;
+ width: 100%;
+ max-width: 300px;
+}
+
+.session-summary {
+ margin-top: 10px;
+ text-align: left;
+ width: 100%;
+}
+
+button {
+ margin-top: 15px;
+ padding: 10px 20px;
+ background-color: #28a745;
+ color: white;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ transition: background-color 0.3s ease;
+}
+button:hover {
+ background-color: #218838;
}
-.controls .attachment-button:hover{
- background-color:initial;
- border: initial;
+
+.ai-response {
+ margin-top: 20px;
+ font-style: italic;
}
+
diff --git a/MVP/src/components/Footer.jsx b/MVP/src/components/Footer.jsx
new file mode 100644
index 0000000..a4f17f4
--- /dev/null
+++ b/MVP/src/components/Footer.jsx
@@ -0,0 +1,89 @@
+import React, { useState, useEffect, useRef } from 'react';
+import VoiceRecorder from './VoiceRecorder';
+import { useStudents } from '../contexts/Students';
+
+function Footer() {
+ const [isRecording, setIsRecording] = useState(false);
+ const [isUploading, setIsUploading] = useState(false);
+ const { students, selectedStudent, selectStudent, updateTranscription } = useStudents();
+
+ const clinicianId = 1234; // Placeholder for clinician ID
+ const selectedStudentRef = useRef(null); // Create a ref to store selectedStudent
+
+ // Keep the ref updated with the latest selectedStudent value
+ useEffect(() => {
+ selectedStudentRef.current = selectedStudent;
+ }, [selectedStudent]);
+
+ const handleStudentChange = (event) => {
+ selectStudent(Number(event.target.value));
+ };
+
+ const handleStopRecording = async (blob) => {
+ const currentSelectedStudent = selectedStudentRef.current; // Access the current ref value
+ if (!currentSelectedStudent) {
+ alert("Please select a student before recording.");
+ return;
+ }
+
+ const formData = new FormData();
+ formData.append('file', blob.blob, 'recording.mp3');
+ formData.append('studentId', currentSelectedStudent.id); // Use the ref value
+ formData.append('clinicianId', clinicianId);
+
+ setIsUploading(true);
+ callAjax(formData, (response) => {
+ setIsUploading(false);
+
+ //TODO just for show for now
+ if (response && response.transcription) {
+ // Update transcription in the context
+ updateTranscription(currentSelectedStudent.id, response.transcription);
+ }
+ });
+ };
+
+ const callAjax = (formData, callback) => {
+ window.clicnical_coach_jsmo_module.transcribeAudio(formData, (res) => {
+ if (res) {
+ if (callback) callback(res);
+ } else {
+ console.log("Unexpected response format:", res);
+ }
+ }, (err) => {
+ console.log("transcribeAudio error:", err);
+ if (callback) callback();
+ });
+ };
+
+ return (
+
+ );
+}
+
+export default Footer;
diff --git a/MVP/src/components/Header.jsx b/MVP/src/components/Header.jsx
new file mode 100644
index 0000000..e39ec35
--- /dev/null
+++ b/MVP/src/components/Header.jsx
@@ -0,0 +1,27 @@
+import React from 'react';
+import { useLocation, Link } from 'react-router-dom';
+
+function Header() {
+ const location = useLocation(); // Get current route information
+ const { pathname } = location; // Get pathname directly
+
+ const isHomeView = pathname === '/'; // Simplified check for home view
+ const isReportView = pathname.startsWith('/report'); // Simplified check for report view
+
+ return (
+
+ {isHomeView && (
+ Clinical Coach
+ )}
+ {isReportView && (
+
+ )}
+
+ );
+}
+
+export default Header;
diff --git a/MVP/src/components/Home.jsx b/MVP/src/components/Home.jsx
new file mode 100644
index 0000000..f3721d3
--- /dev/null
+++ b/MVP/src/components/Home.jsx
@@ -0,0 +1,84 @@
+import React from 'react';
+import { useStudents } from '../contexts/Students';
+import { FaUserCircle } from 'react-icons/fa';
+
+function Home() {
+ const { selectedStudent, aiResponse, updateAIResponse } = useStudents();
+
+ const handleSubmitToAI = () => {
+ if (!selectedStudent || !selectedStudent.transcription) {
+ console.error("No transcription to submit.");
+ return;
+ }
+
+ const chatmlPayload = [
+ { role: "system", content: "You are a helpful assistant." },
+ { role: "user", content: selectedStudent.transcription }
+ ];
+
+ console.log("chatmlPayload", chatmlPayload);
+
+ callAI(chatmlPayload, (aiContent) => {
+ console.log("aiContent callback", aiContent);
+ if (aiContent) {
+ updateAIResponse(aiContent); // Store AI response in context
+ } else {
+ console.error("Failed to get a response from the AI.");
+ }
+ });
+ };
+
+ const callAI = (chatmlPayload, callback) => {
+ window.clicnical_coach_jsmo_module.callAI(chatmlPayload, (res) => {
+ if (res) {
+ if (callback) callback(res);
+ } else {
+ console.log("Unexpected AI response format:", res);
+ }
+ }, (err) => {
+ console.log("callAI error:", err);
+ if (callback) callback();
+ });
+ };
+
+ return (
+
+
+
Today, September 10, 2024
+
+
+ {selectedStudent ? (
+
+
+
+
{selectedStudent.name}
+
+
+
+
+
Session Completed
+
+
+ {selectedStudent.transcription && (
+
+
{selectedStudent.transcription}
+
+ )}
+
+
+
+
+ ) : (
+
Please select a student and start recording a session.
+ )}
+
+ {aiResponse && (
+
+ "{aiResponse}"
+
+ )}
+
+ );
+}
+
+export default Home;
diff --git a/MVP/src/components/Report.jsx b/MVP/src/components/Report.jsx
new file mode 100644
index 0000000..d137182
--- /dev/null
+++ b/MVP/src/components/Report.jsx
@@ -0,0 +1,20 @@
+import React from 'react';
+import { useStudents } from '../contexts/Students';
+
+function Report() {
+ const { selectedStudent } = useStudents();
+
+ if (!selectedStudent) {
+ return
No student selected. Please go back and select a student.
;
+ }
+
+ return (
+
+
Report for {selectedStudent.name}
+ {/* Display the student's report here */}
+
Report content goes here...
+
+ );
+}
+
+export default Report;
diff --git a/MVP/src/VoiceRecorder.jsx b/MVP/src/components/VoiceRecorder.jsx
similarity index 83%
rename from MVP/src/VoiceRecorder.jsx
rename to MVP/src/components/VoiceRecorder.jsx
index 20b6a71..31d2470 100644
--- a/MVP/src/VoiceRecorder.jsx
+++ b/MVP/src/components/VoiceRecorder.jsx
@@ -1,5 +1,6 @@
import React, { useState, useEffect, useRef } from 'react';
import { ReactMic } from 'react-mic';
+import { FaMicrophone, FaStop } from 'react-icons/fa';
const VoiceRecorder = ({ setIsRecording, handleStopRecording }) => {
const [record, setRecord] = useState(false);
@@ -92,8 +93,6 @@ const VoiceRecorder = ({ setIsRecording, handleStopRecording }) => {
const rms = Math.sqrt(sum / bufferLength);
const db = 20 * Math.log10(rms);
- // console.log(`Current dB level: ${db.toFixed(2)} (Threshold: ${silenceThreshold})`);
-
if (db < silenceThreshold) {
if (silenceStartRef.current === null) {
silenceStartRef.current = Date.now();
@@ -102,7 +101,7 @@ const VoiceRecorder = ({ setIsRecording, handleStopRecording }) => {
} else {
const silenceDurationElapsed = Date.now() - silenceStartRef.current;
silenceDurationRef.current += 100;
- console.log(`Silence duration elapsed: ${silenceDurationRef.current}ms`);
+ // console.log(`Silence duration elapsed: ${silenceDurationRef.current}ms`);
if (silenceDurationRef.current >= silenceDurationMs) {
console.log('Silence threshold reached, stopping recording');
stopRecording(); // Stop the recording when silence is detected
@@ -136,40 +135,36 @@ const VoiceRecorder = ({ setIsRecording, handleStopRecording }) => {
}, []);
return (
-
+
-
+
- {audioUrl && (
-
- )}
+ {/*{audioUrl && (*/}
+ {/*
*/}
+ {/*)}*/}
);
};
export default VoiceRecorder;
+
+
+
diff --git a/MVP/src/components/fileUpload.jsx b/MVP/src/components/fileUpload.jsx
new file mode 100644
index 0000000..4acb858
--- /dev/null
+++ b/MVP/src/components/fileUpload.jsx
@@ -0,0 +1,65 @@
+import React, { useRef, useState } from 'react';
+import { FaPaperclip } from 'react-icons/fa';
+
+function Footer() {
+ const [isUploading, setIsUploading] = useState(false);
+ const [transcription, setTranscription] = useState('');
+ const fileInputRef = useRef(null);
+
+ const handleFileChange = async (event) => {
+ const file = event.target.files[0];
+ if (file) {
+ const formData = new FormData();
+ formData.append('file', file, file.name);
+
+ setIsUploading(true);
+
+ callAjax(formData, (response) => {
+ setIsUploading(false);
+ if (response && response.transcription) {
+ setTranscription(response.transcription);
+ } else {
+ console.error("Failed to get a transcription from the server.");
+ }
+ });
+ }
+ };
+
+ const callAjax = (formData, callback) => {
+ window.clicnical_coach_jsmo_module.transcribeAudio(formData, (res) => {
+ if (res && res.transcription) {
+ if (callback) callback(res);
+ } else {
+ console.log("Unexpected response format:", res);
+ }
+ }, (err) => {
+ console.log("transcribeAudio error:", err);
+ if (callback) callback();
+ });
+ };
+
+ return (
+
+ );
+}
+
+export default Footer;
diff --git a/MVP/src/contexts/Students.jsx b/MVP/src/contexts/Students.jsx
new file mode 100644
index 0000000..22f59ec
--- /dev/null
+++ b/MVP/src/contexts/Students.jsx
@@ -0,0 +1,59 @@
+import React, { createContext, useState, useContext, useEffect } from 'react';
+
+// Create the context
+const StudentsContext = createContext();
+
+// Custom hook to use the StudentsContext
+export const useStudents = () => useContext(StudentsContext);
+
+// Provider component to wrap the app
+export const StudentsProvider = ({ children }) => {
+ const [students, setStudents] = useState([
+ { id: 1, name: "John Wick" },
+ { id: 2, name: "Selina Kyle" },
+ { id: 3, name: "Brock Purdy" }
+ ]);
+
+ const [selectedStudent, setSelectedStudent] = useState(null);
+ const [aiResponse, setAiResponse] = useState('');
+
+ // Function to select a student
+ const selectStudent = (studentId) => {
+ const student = students.find((s) => s.id === studentId);
+ setSelectedStudent(student);
+ };
+
+ const updateTranscription = (studentId, transcription) => {
+ console.log("updateTranscription", studentId, transcription);
+ setStudents((prevStudents) =>
+ prevStudents.map((student) =>
+ student.id === studentId ? { ...student, transcription } : student
+ )
+ );
+ };
+
+ const updateAIResponse = (response) => {
+ setAiResponse(response);
+ };
+
+ // Sync selectedStudent with updated transcription
+ useEffect(() => {
+ if (selectedStudent) {
+ const updatedStudent = students.find((s) => s.id === selectedStudent.id);
+ setSelectedStudent(updatedStudent); // Re-sync selectedStudent with the updated students array
+ }
+ }, [students]);
+
+ return (
+
+ {children}
+
+ );
+};
diff --git a/MVP/src/main.jsx b/MVP/src/main.jsx
index 1bb10e9..770b489 100644
--- a/MVP/src/main.jsx
+++ b/MVP/src/main.jsx
@@ -2,6 +2,7 @@ import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.jsx'
import './index.css'
+import './assets/mvp.css'
createRoot(document.getElementById('clinicalcoach_ui_container')).render(