diff --git a/README.md b/README.md index e215bc4..ae40525 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,191 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). + + + + + + + +[![Contributors][contributors-shield]][contributors-url] +[![Stargazers][stars-shield]][stars-url] +[![Issues][issues-shield]][issues-url] +[![Apache License][license-shield]][license-url] + + + + +
+
+ + Logo + + +

PatSafe

+ +

+ Bridging the gap between doctors and patients for seamless post-discharge care. +
+
+ Visit + · + Report Bug + · + Request Feature +

+
+ + + + +## About the Project + +PatSafe is a web-based application that connects discharged patients with their doctors for post-discharge care. It features a doctor’s dashboard where they can update patient information, provide medication instructions, and track patient progress. Patients can report their symptoms, recovery status, and medication adherence, which is then updated in real time for their doctor to review. Additionally, PatSafe includes an AI-powered chatbot that allows patients to ask medical questions and report symptoms, helping doctors get insights into the patient’s condition remotely. The chatbot also aids in tracking medication adherence and offering information for diagnoses and prescriptions. + +### How does it work? + +PatSafe was built using **Next.js** and **React.js** for the frontend, while the backend is powered by **MongoDB** and **Clerk** for authentication. The platform utilizes APIs for real-time updates and features such as medication tracking and symptom reporting. We integrated a chatbot powered by **Hugging Face models** for natural language processing, allowing users to interact conversationally and gather helpful medical information. + + + + +### Built With + +* [![Next.js][Next.js]][Next-url] +* [![React][React.js]][React-url] +* [![Tailwind][Tailwind]][Tailwind-url] +* [![Clerk][Clerk]][Clerk-url] +* [![MongoDB][MongoDB]][MongoDB-url] +* [![HuggingFace][HuggingFace]][HuggingFace-url] +* [![OpenAI][OpenAI]][OpenAI] + + +

(back to top)

+ + + + + ## Getting Started -First, run the development server: +Here are the steps to run the project locally if you want to develop your own project. -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev -``` +### Prerequisites -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +* pnpm + ```sh + pnpm self-update + ``` -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. +### Installation -## Learn More +1. Fork the repository and set it up as a project on Vercel or another hosting platform -To learn more about Next.js, take a look at the following resources: +2. Install packages + ```sh + pnpm install + ``` + +3. You can run the website locally with + ```sh + npm run dev + ``` + or, if hosting on Vercel, with + ```sh + vercel dev + ``` -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! -## Deploy on Vercel -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. + + + + +## License + +Distributed under the Apache 2.0 License. See `LICENSE.txt` for more information. + + +* [Best README Template](https://github.com/othneildrew/Best-README-Template) + +

(back to top)

+ + + + + + + +[contributors-shield]: https://img.shields.io/github/contributors/GamerBoss101/HoyaHax2025.svg?style=for-the-badge +[contributors-url]: https://github.com/GamerBoss101/HoyaHax2025/graphs/contributors +[forks-shield]: https://img.shields.io/github/forks/GamerBoss101/HoyaHax2025.svg?style=for-the-badge +[forks-url]: https://github.com/GamerBoss101/HoyaHax2025/network/members +[stars-shield]: https://img.shields.io/github/stars/GamerBoss101/HoyaHax2025.svg?style=for-the-badge +[stars-url]: https://github.com/GamerBoss101/HoyaHax2025/stargazers +[issues-shield]: https://img.shields.io/github/issues/GamerBoss101/HoyaHax2025.svg?style=for-the-badge +[issues-url]: https://github.com/GamerBoss101/HoyaHax2025/issues +[license-shield]: https://img.shields.io/github/license/GamerBoss101/HoyaHax2025.svg?style=for-the-badge +[license-url]: https://github.com/GamerBoss101/HoyaHax2025/blob/master/LICENSE.txt +[linkedin-shield]: https://img.shields.io/badge/LinkedIn-0A66C2.svg?style=for-the-badge&logo=linkedin&logoColor=white +[linkedin-url-joseph]: https://linkedin.com/in/joseph-j-helfenbein +[product-screenshot]: images/screenshot.png +[Next.js]: https://img.shields.io/badge/next.js-000000?style=for-the-badge&logo=nextdotjs&logoColor=white +[Next-url]: https://nextjs.org/ +[React.js]: https://img.shields.io/badge/React.js-20232A?style=for-the-badge&logo=react&logoColor=61DAFB +[React-url]: https://reactjs.org/ +[Vue.js]: https://img.shields.io/badge/Vue.js-35495E?style=for-the-badge&logo=vuedotjs&logoColor=4FC08D +[Vue-url]: https://vuejs.org/ +[Angular.io]: https://img.shields.io/badge/Angular-DD0031?style=for-the-badge&logo=angular&logoColor=white +[Angular-url]: https://angular.io/ +[Svelte.dev]: https://img.shields.io/badge/Svelte-4A4A55?style=for-the-badge&logo=svelte&logoColor=FF3E00 +[Svelte-url]: https://svelte.dev/ +[Laravel.com]: https://img.shields.io/badge/Laravel-FF2D20?style=for-the-badge&logo=laravel&logoColor=white +[Laravel-url]: https://laravel.com +[Bootstrap.com]: https://img.shields.io/badge/Bootstrap-563D7C?style=for-the-badge&logo=bootstrap&logoColor=white +[Bootstrap-url]: https://getbootstrap.com +[JQuery.com]: https://img.shields.io/badge/jQuery-0769AD?style=for-the-badge&logo=jquery&logoColor=white +[JQuery-url]: https://jquery.com +[Expo]: https://img.shields.io/badge/expo-000000?style=for-the-badge&logo=expo&logoColor=white +[Expo-url]: https://expo.dev/ +[Flask]: https://img.shields.io/badge/flask-4590A1?logo=flask&style=for-the-badge&logoColor=white +[Flask-url]: https://flask.palletsprojects.com/en/3.0.x/ +[JavaScript]: https://img.shields.io/badge/javascript-yellow?logo=javascript&style=for-the-badge&logoColor=white +[JavaScript-url]: https://developer.oracle.com/languages/javascript.html +[ThreeJS]: https://img.shields.io/badge/three.js-black?logo=three.js&style=for-the-badge&logoColor=white +[ThreeJS-url]: https://threejs.org/ +[TypeScript]: https://img.shields.io/badge/typescript-3178C6?logo=typescript&style=for-the-badge&logoColor=white +[TypeScript-url]: https://www.typescriptlang.org/ +[Python]: https://img.shields.io/badge/python-3776AB?style=for-the-badge&logo=python&logoColor=white +[Python-url]: https://www.python.org/ +[Amazon-RDS]: https://img.shields.io/badge/amazon%20rds-527FFF?style=for-the-badge&logo=amazon%20rds&logoColor=white +[Amazon-RDS-url]: https://aws.amazon.com/rds/ +[Cloudflare]: https://img.shields.io/badge/cloudflare%20workers-F38020?style=for-the-badge&logo=cloudflare%20workers&logoColor=white +[Cloudflare-url]: https://workers.cloudflare.com/ +[Vercel]: https://img.shields.io/badge/vercel-000000?logo=vercel&style=for-the-badge&logoColor=white +[Vercel-url]: https://www.vercel.com/ +[Supabase]: https://img.shields.io/badge/supabase-3FCF8E?logo=supabase&style=for-the-badge&logoColor=white +[Supabase-url]: https://supabase.com/ +[Clerk]: https://img.shields.io/badge/clerk-6C47FF?logo=clerk&style=for-the-badge&logoColor=white +[Clerk-url]: https://clerk.com/ +[Tailwind]: https://img.shields.io/badge/tailwind%20css-06B6D4?logo=tailwindcss&style=for-the-badge&logoColor=white +[Tailwind-url]: https://tailwindcss.com/ +[MongoDB]: https://img.shields.io/badge/mongodb-47A248?logo=mongodb&style=for-the-badge&logoColor=white +[MongoDB-url]: https://www.mongodb.com/ +[HuggingFace]: https://img.shields.io/badge/huggingface-FFD21E?logo=huggingface&style=for-the-badge&logoColor=white +[HuggingFace-url]: https://huggingface.co/ +[OpenAI]: https://img.shields.io/badge/openai%20api-412991?logo=openai&style=for-the-badge&logoColor=white +[OpenAI-url]: [https://huggingface.co/](https://openai.com/) diff --git a/src/app/(panels)/suite/doctor/account/page.jsx b/src/app/(panels)/suite/doctor/account/page.jsx index 01cb049..ff54128 100644 --- a/src/app/(panels)/suite/doctor/account/page.jsx +++ b/src/app/(panels)/suite/doctor/account/page.jsx @@ -7,17 +7,9 @@ import { Button } from '@/components/ui/button'; import { Label } from '@/components/ui/label'; import { Card, CardHeader, CardContent } from '@/components/ui/card'; -import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible" -import { ChevronDown } from "lucide-react" - -import { PersonForm } from './PatientForm'; - -import { useRouter } from 'next/navigation'; - const AccountPage = () => { const { user } = useUser(); const [userData, setUserData] = useState(null); - const [patients, setPatients] = useState([]); useEffect(() => { if (user) { @@ -72,33 +64,6 @@ const AccountPage = () => { - - {userData.role === 'caregiver' && ( -
-

Patients

- -
- )} diff --git a/src/app/(panels)/suite/doctor/dashboard/AppList.jsx b/src/app/(panels)/suite/doctor/dashboard/AppList.jsx new file mode 100644 index 0000000..fb50e13 --- /dev/null +++ b/src/app/(panels)/suite/doctor/dashboard/AppList.jsx @@ -0,0 +1,33 @@ +import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" +import { Card, CardContent } from "@/components/ui/card" + +export function AppointmentList({ appointments }) { + return ( + + + + A list of your upcoming appointments. + + + ID + Date + Status + + + + {appointments.map((appointment) => { + return ( + + {appointment.id} + {new Date(appointment.date).toLocaleString()} + {appointment.status} + + ) + })} + +
+
+
+ ) +} + diff --git a/src/app/(panels)/suite/doctor/dashboard/page.jsx b/src/app/(panels)/suite/doctor/dashboard/page.jsx index eb7ef22..445de82 100644 --- a/src/app/(panels)/suite/doctor/dashboard/page.jsx +++ b/src/app/(panels)/suite/doctor/dashboard/page.jsx @@ -1,32 +1,35 @@ "use client" import { PatientTable } from "./PatientTable" +import { AppointmentList } from "./AppList" import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; import axios from "axios"; import { useUser } from '@clerk/nextjs'; +import React from "react"; + export default function Dashboard() { const router = useRouter(); - const { user } = useUser(); - const [userData, setUserData] = useState(null); + const { user } = useUser(); + const [userData, setUserData] = useState(null); - useEffect(() => { - if (user) { - axios.get(`/api/user?userId=${user.id}`).then(response => { - setUserData(response.data); - }); - } - }, [user]); + useEffect(() => { + if (user) { + axios.get(`/api/user?userId=${user.id}`).then(response => { + setUserData(response.data); + }); + } + }, [user]); - if (userData) { + if (userData) { if (userData.role != "caregiver") { router.push("/suite/patient/dashboard"); } - } + } const patients = [ { id: 1, name: "John Doe", age: 30, lastVisit: "2024-10-01" }, @@ -34,11 +37,21 @@ export default function Dashboard() { { id: 3, name: "Sam Johnson", age: 40, lastVisit: "2024-10-05" }, ]; + + const appointments = [ + { id: 1, patientId: 1, date: "2025-01-27T09:00:00", status: "Scheduled" }, + { id: 2, patientId: 2, date: "2025-01-27T10:30:00", status: "Scheduled" }, + { id: 3, patientId: 3, date: "2025-01-27T14:00:00", status: "Scheduled" }, + { id: 4, patientId: 4, date: "2025-01-28T11:00:00", status: "Scheduled" }, + { id: 5, patientId: 5, date: "2025-01-28T15:30:00", status: "Scheduled" }, + ] + return (

Dashboard

-
+
+
) diff --git a/src/app/(panels)/suite/doctor/account/PatientForm.tsx b/src/app/(panels)/suite/doctor/patient/PatientForm.tsx similarity index 100% rename from src/app/(panels)/suite/doctor/account/PatientForm.tsx rename to src/app/(panels)/suite/doctor/patient/PatientForm.tsx diff --git a/src/app/(panels)/suite/doctor/patient/page.jsx b/src/app/(panels)/suite/doctor/patient/page.jsx new file mode 100644 index 0000000..f3f2144 --- /dev/null +++ b/src/app/(panels)/suite/doctor/patient/page.jsx @@ -0,0 +1,76 @@ + +"use client"; + +import { useEffect, useState } from "react"; + +import { useRouter } from "next/navigation"; + +import axios from "axios"; +import { useUser } from "@clerk/nextjs"; +import { CardContent } from "@/components/ui/card"; + +import { Button } from "@/components/ui/button"; +import { ChevronDown } from "lucide-react"; +import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; +import PersonForm from "@/components/PersonForm"; +import { Card } from "@/components/ui/card"; + + +export default function Dashboard() { + + const router = useRouter(); + const { user } = useUser(); + const [userData, setUserData] = useState(null); + const [patients, setPatients] = useState([]); + + useEffect(() => { + if (user) { + axios.get(`/api/user?userId=${user.id}`).then(response => { + setUserData(response.data); + }); + } + }, [user]); + + if (userData) { + if (userData.role != "caregiver") { + router.push("/suite/patient/dashboard"); + } + } + + return ( +
+

Patients

+
+ + + {userData.role === 'caregiver' && ( +
+
    + {patients.map(patient => ( + +
    +
    +

    {patient.name}

    +

    {patient.role}

    +
    + + + +
    + + + +
    + ))} +
+
+ )} +
+
+
+
+ ) +} \ No newline at end of file diff --git a/src/app/(web)/transcribe/page.tsx b/src/app/(web)/transcribe/page.tsx index 7358505..d460e86 100644 --- a/src/app/(web)/transcribe/page.tsx +++ b/src/app/(web)/transcribe/page.tsx @@ -1,156 +1,174 @@ "use client"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; //import Hero1 from '@/components/Hero1' //IMPORT THE HERO1 FUNCTION TO MAKE THE TRANSCRIBE PAGE LOOK BETTER import React, { useState, useRef } from "react"; // import axios from "axios"; +import { AlertCircle } from "lucide-react" + +import { + Alert, + AlertDescription, + AlertTitle, +} from "@/components/ui/alert" +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; + const AudioTranscriber: React.FC = () => { - const [file, setFile] = useState(null); - const [transcription, setTranscription] = useState(null); - const [loading, setLoading] = useState(false); - const [recording, setRecording] = useState(false); - const [error, setError] = useState(null); - const mediaRecorderRef = useRef(null); - const audioChunksRef = useRef([]); + const [file, setFile] = useState(null); + const [transcription, setTranscription] = useState(null); + const [loading, setLoading] = useState(false); + const [recording, setRecording] = useState(false); + const [error, setError] = useState(null); + const mediaRecorderRef = useRef(null); + const audioChunksRef = useRef([]); - // Handle file selection - const handleFileChange = (event: React.ChangeEvent) => { - if (event.target.files && event.target.files.length > 0) { - setFile(event.target.files[0]); - console.log("File selected:", event.target.files[0].name); - } - }; + // Handle file selection + const handleFileChange = (event: React.ChangeEvent) => { + if (event.target.files && event.target.files.length > 0) { + setFile(event.target.files[0]); + console.log("File selected:", event.target.files[0].name); + } + }; - // Handle file transcription - const handleTranscription = async (audioFile: File) => { - if (!audioFile) { - alert("No audio file to transcribe!"); - return; - } + // Handle file transcription + const handleTranscription = async (audioFile: File) => { + if (!audioFile) { + alert("No audio file to transcribe!"); + return; + } - console.log("Starting transcription for:", audioFile.name); + console.log("Starting transcription for:", audioFile.name); - const formData = new FormData(); - formData.append("file", audioFile); - console.log(audioFile); - setLoading(true); - setError(null); // Clear previous errors - try { - let response = await fetch("/api/transcribe", { - method: "POST", - body: formData, - headers: { - "Content-Type": "multipart/form-data", - } - }) + const formData = new FormData(); + formData.append("file", audioFile); + setLoading(true); + setError(null); // Clear previous errors + try { + let response = await fetch("/api/transcribe", { + method: "POST", + body: formData, + headers: { + "Content-Type": "multipart/form-data", + } + }) - response = await response.json(); - console.log("Transcription response:", response); + console.log("! response:", response); + response = await response.json(); + console.log("@ response:", response); - // if (response.data && response.data.transcription) { - // setTranscription(response.data.transcription); - // } else { - // setError("Unexpected response format. Check backend API."); - // console.error("Invalid response format:", response.data); - // } - } catch (error) { - console.error("Error transcribing audio:", error); - setError("Failed to transcribe audio. Please try again."); - } finally { - setLoading(false); - } - }; + // if (response.data && response.data.transcription) { + // setTranscription(response.data.transcription); + // } else { + // setError("Unexpected response format. Check backend API."); + // console.error("Invalid response format:", response.data); + // } + } catch (error) { + console.error("Error transcribing audio:", error); + setError("Failed to transcribe audio. Please try again."); + } finally { + setLoading(false); + } + }; - // Start recording audio - const startRecording = async () => { - try { - const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); - console.log("Microphone access granted."); + // Start recording audio + const startRecording = async () => { + try { + const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); + console.log("Microphone access granted."); - mediaRecorderRef.current = new MediaRecorder(stream); - audioChunksRef.current = []; // Reset audio chunks + mediaRecorderRef.current = new MediaRecorder(stream); + audioChunksRef.current = []; // Reset audio chunks - mediaRecorderRef.current.ondataavailable = (event) => { - if (event.data.size > 0) { - console.log("Audio chunk received:", event.data); - audioChunksRef.current.push(event.data); - } - }; + mediaRecorderRef.current.ondataavailable = (event) => { + if (event.data.size > 0) { + console.log("Audio chunk received:", event.data); + audioChunksRef.current.push(event.data); + } + }; - mediaRecorderRef.current.onstop = async () => { - const audioBlob = new Blob(audioChunksRef.current, { type: "audio/mp3" }); - const audioFile = new File([audioBlob], "recording.mp3", { type: "audio/mp3" }); + mediaRecorderRef.current.onstop = async () => { + const audioBlob = new Blob(audioChunksRef.current, { type: "audio/mp3" }); + const audioFile = new File([audioBlob], "recording.mp3", { type: "audio/mp3" }); - console.log("Recording stopped. Blob created:", audioBlob); + console.log("Recording stopped. Blob created:", audioBlob); - setFile(audioFile); // Save the recorded file - setTranscription("Processing transcription for recorded audio..."); - await handleTranscription(audioFile); // Automatically transcribe - }; + setFile(audioFile); // Save the recorded file + setTranscription("Processing transcription for recorded audio..."); + await handleTranscription(audioFile); // Automatically transcribe + }; - mediaRecorderRef.current.start(); - console.log("Recording started."); - setRecording(true); - } catch (error) { - console.error("Error starting recording:", error); - setError("Failed to start recording. Please check microphone permissions."); - } - }; + mediaRecorderRef.current.start(); + console.log("Recording started."); + setRecording(true); + } catch (error) { + console.error("Error starting recording:", error); + setError("Failed to start recording. Please check microphone permissions."); + } + }; - // Stop recording audio - const stopRecording = () => { - if (mediaRecorderRef.current) { - console.log("Stopping recording..."); - mediaRecorderRef.current.stop(); - setRecording(false); - } - }; + // Stop recording audio + const stopRecording = () => { + if (mediaRecorderRef.current) { + console.log("Stopping recording..."); + mediaRecorderRef.current.stop(); + setRecording(false); + } + }; - return ( -
-

-
- Audio Transcription -
-

-
-

Upload or Record Audio

- - - -
+ return ( +
+

+
+ Audio Transcription +
+

+
+
+

Upload or Record Audio

-
-

Record Audio

- {!recording ? ( - - ) : ( - - )} -
+ + +
-
-

Transcription:

- {loading ? ( -

Processing transcription...

- ) : transcription ? ( -

{transcription}

- ) : ( -

No transcription available yet.

- )} -
+
+

Record Audio

+ {!recording ? ( + + ) : ( + + )} +
- {error && ( -
- Error: {error} -
- )} -
- ); + + + Transcription Result + + + + {loading + ? "Processing transcription..." + : transcription + ? "Your transcription is ready" + : "No transcription available yet"} + + + + + {error && ( + + + Error + + Error: {error} + + + )} + + ); }; export default AudioTranscriber; @@ -182,179 +200,179 @@ const AudioTranscriber: React.FC = () => { const audioChunksRef = useRef([]) const handleFileChange = (event: React.ChangeEvent) => { - if (event.target.files && event.target.files.length > 0) { - setFile(event.target.files[0]) - console.log("File selected:", event.target.files[0].name) - } + if (event.target.files && event.target.files.length > 0) { + setFile(event.target.files[0]) + console.log("File selected:", event.target.files[0].name) + } } const handleTranscription = async (audioFile: File) => { - if (!audioFile) { - setError("No audio file to transcribe!") - return - } + if (!audioFile) { + setError("No audio file to transcribe!") + return + } - console.log("Starting transcription for:", audioFile.name) + console.log("Starting transcription for:", audioFile.name) - const formData = new FormData() - formData.append("file", audioFile) + const formData = new FormData() + formData.append("file", audioFile) - setLoading(true) - setError(null) - try { - const response = await axios.post("http://localhost:8000/transcribe", formData, { - headers: { - "Content-Type": "multipart/form-data", - }, - }) + setLoading(true) + setError(null) + try { + const response = await axios.post("http://localhost:8000/transcribe", formData, { + headers: { + "Content-Type": "multipart/form-data", + }, + }) - console.log("Transcription response:", response.data) + console.log("Transcription response:", response.data) - if (response.data && response.data.transcription) { - setTranscription(response.data.transcription) - } else { - setError("Unexpected response format. Check backend API.") - console.error("Invalid response format:", response.data) - } - } catch (error) { - console.error("Error transcribing audio:", error) - setError("Failed to transcribe audio. Please try again.") - } finally { - setLoading(false) - } + if (response.data && response.data.transcription) { + setTranscription(response.data.transcription) + } else { + setError("Unexpected response format. Check backend API.") + console.error("Invalid response format:", response.data) + } + } catch (error) { + console.error("Error transcribing audio:", error) + setError("Failed to transcribe audio. Please try again.") + } finally { + setLoading(false) + } } const startRecording = async () => { - try { - const stream = await navigator.mediaDevices.getUserMedia({ audio: true }) - console.log("Microphone access granted.") + try { + const stream = await navigator.mediaDevices.getUserMedia({ audio: true }) + console.log("Microphone access granted.") - mediaRecorderRef.current = new MediaRecorder(stream) - audioChunksRef.current = [] + mediaRecorderRef.current = new MediaRecorder(stream) + audioChunksRef.current = [] - mediaRecorderRef.current.ondataavailable = (event) => { - if (event.data.size > 0) { - console.log("Audio chunk received:", event.data) - audioChunksRef.current.push(event.data) - } - } + mediaRecorderRef.current.ondataavailable = (event) => { + if (event.data.size > 0) { + console.log("Audio chunk received:", event.data) + audioChunksRef.current.push(event.data) + } + } - mediaRecorderRef.current.onstop = async () => { - const audioBlob = new Blob(audioChunksRef.current, { type: "audio/mp3" }) - const audioFile = new File([audioBlob], "recording.mp3", { type: "audio/mp3" }) + mediaRecorderRef.current.onstop = async () => { + const audioBlob = new Blob(audioChunksRef.current, { type: "audio/mp3" }) + const audioFile = new File([audioBlob], "recording.mp3", { type: "audio/mp3" }) - console.log("Recording stopped. Blob created:", audioBlob) + console.log("Recording stopped. Blob created:", audioBlob) - setFile(audioFile) - setTranscription("Processing transcription for recorded audio...") - await handleTranscription(audioFile) - } + setFile(audioFile) + setTranscription("Processing transcription for recorded audio...") + await handleTranscription(audioFile) + } - mediaRecorderRef.current.start() - console.log("Recording started.") - setRecording(true) - } catch (error) { - console.error("Error starting recording:", error) - setError("Failed to start recording. Please check microphone permissions.") - } + mediaRecorderRef.current.start() + console.log("Recording started.") + setRecording(true) + } catch (error) { + console.error("Error starting recording:", error) + setError("Failed to start recording. Please check microphone permissions.") + } } const stopRecording = () => { - if (mediaRecorderRef.current) { - console.log("Stopping recording...") - mediaRecorderRef.current.stop() - setRecording(false) - } + if (mediaRecorderRef.current) { + console.log("Stopping recording...") + mediaRecorderRef.current.stop() + setRecording(false) + } } return ( -
-

Audio Transcriber

+
+

Audio Transcriber

- - - Transcribe Audio - Upload an audio file or record directly to transcribe - - - - - Upload Audio - Record Audio - - -
- - - -
-
- -
- - {!recording ? ( - - ) : ( - - )} -
-
-
-
-
+ + + Transcribe Audio + Upload an audio file or record directly to transcribe + + + + + Upload Audio + Record Audio + + +
+ + + +
+
+ +
+ + {!recording ? ( + + ) : ( + + )} +
+
+
+
+
- - - Transcription Result - - {loading - ? "Processing transcription..." - : transcription - ? "Your transcription is ready" - : "No transcription available yet"} - - - -