Merge remote-tracking branch 'refs/remotes/origin/main'

This commit is contained in:
Joseph J Helfenbein
2025-01-26 06:34:04 -05:00
8 changed files with 605 additions and 345 deletions

199
README.md
View File

@@ -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).
<!-- Improved compatibility of back to top link: See: https://github.com/othneildrew/Best-README-Template/pull/73 -->
<a id="readme-top"></a>
<!--
*** Thanks for checking out the Best-README-Template. If you have a suggestion
*** that would make this better, please fork the repo and create a pull request
*** or simply open an issue with the tag "enhancement".
*** Don't forget to give the project a star!
*** Thanks again! Now go create something AMAZING! :D
-->
<!-- PROJECT SHIELDS -->
<!--
*** I'm using markdown "reference style" links for readability.
*** Reference links are enclosed in brackets [ ] instead of parentheses ( ).
*** See the bottom of this document for the declaration of the reference variables
*** for contributors-url, forks-url, etc. This is an optional, concise syntax you may use.
*** https://www.markdownguide.org/basic-syntax/#reference-style-links
-->
[![Contributors][contributors-shield]][contributors-url]
[![Stargazers][stars-shield]][stars-url]
[![Issues][issues-shield]][issues-url]
[![Apache License][license-shield]][license-url]
<!-- PROJECT LOGO -->
<br />
<div align="center">
<a href="https://github.com/GamerBoss101/HoyaHax2025">
<img src="/public/HoyaHax2025-icon.svg" alt="Logo" width="80" height="80">
</a>
<h3 align="center">PatSafe</h3>
<p align="center">
Bridging the gap between doctors and patients for seamless post-discharge care.
<br />
<br />
<a href="https://www.patsafe.co">Visit</a>
·
<a href="https://github.com/GamerBoss101/HoyaHax2025/issues/new?labels=bug&template=bug-report---.md">Report Bug</a>
·
<a href="https://github.com/GamerBoss101/HoyaHax2025/issues/new?labels=enhancement&template=feature-request---.md">Request Feature</a>
</p>
</div>
<!-- ABOUT THE PROJECT -->
## About the Project
PatSafe is a web-based application that connects discharged patients with their doctors for post-discharge care. It features a doctors 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 patients 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]
<p align="right">(<a href="#readme-top">back to top</a>)</p>
<!-- GETTING STARTED -->
## 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
```
- [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.
3. You can run the website locally with
```sh
npm run dev
```
or, if hosting on Vercel, with
```sh
vercel dev
```
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 -->
## License
Distributed under the Apache 2.0 License. See `LICENSE.txt` for more information.
* [Best README Template](https://github.com/othneildrew/Best-README-Template)
<p align="right">(<a href="#readme-top">back to top</a>)</p>
<!-- MARKDOWN LINKS & IMAGES -->
<!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
[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/)

View File

@@ -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 = () => {
<Button onClick={handleRoleChange} className="mb-4">
Change role to {userData.role === 'patient' ? 'caregiver' : 'patient'}
</Button>
{userData.role === 'caregiver' && (
<div>
<h2 className="text-xl font-bold mb-4">Patients</h2>
<ul className="mb-4">
{patients.map(patient => (
<Collapsible key={patient.id}>
<div className="flex items-center justify-between p-2 bg-gray-100 dark:bg-neutral-800 rounded-t-lg">
<div>
<h2 className="text-lg font-semibold">{patient.name}</h2>
<p className="text-sm text-gray-400">{patient.role}</p>
</div>
<CollapsibleTrigger asChild>
<Button variant="ghost" size="sm">
<ChevronDown className="h-4 w-4" />
<span className="sr-only">Toggle</span>
</Button>
</CollapsibleTrigger>
</div>
<CollapsibleContent className="p-4 border border-t-0 rounded-b-lg">
<PersonForm person={patient} />
</CollapsibleContent>
</Collapsible>
))}
</ul>
</div>
)}
</CardContent>
</Card>
</div>

View File

@@ -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 (
<Card>
<CardContent>
<Table>
<TableCaption>A list of your upcoming appointments.</TableCaption>
<TableHeader>
<TableRow>
<TableHead className="w-[100px]">ID</TableHead>
<TableHead>Date</TableHead>
<TableHead>Status</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{appointments.map((appointment) => {
return (
<TableRow key={appointment.id}>
<TableCell className="font-medium">{appointment.id}</TableCell>
<TableCell>{new Date(appointment.date).toLocaleString()}</TableCell>
<TableCell>{appointment.status}</TableCell>
</TableRow>
)
})}
</TableBody>
</Table>
</CardContent>
</Card>
)
}

View File

@@ -1,12 +1,15 @@
"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() {
@@ -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 (
<div className="container mx-auto">
<h1 className="text-3xl font-semibold mb-6">Dashboard</h1>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div className="h-20 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<PatientTable data={patients} />
<AppointmentList appointments={appointments} />
</div>
</div>
)

View File

@@ -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 (
<div className="container mx-auto">
<h1 className="text-3xl font-semibold mb-6">Patients</h1>
<div className="h-20 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<Card>
<CardContent>
{userData.role === 'caregiver' && (
<div>
<ul className="mb-4">
{patients.map(patient => (
<Collapsible key={patient.id}>
<div className="flex items-center justify-between p-2 bg-gray-100 dark:bg-neutral-800 rounded-t-lg">
<div>
<h2 className="text-lg font-semibold">{patient.name}</h2>
<p className="text-sm text-gray-400">{patient.role}</p>
</div>
<CollapsibleTrigger asChild>
<Button variant="ghost" size="sm">
<ChevronDown className="h-4 w-4" />
<span className="sr-only">Toggle</span>
</Button>
</CollapsibleTrigger>
</div>
<CollapsibleContent className="p-4 border border-t-0 rounded-b-lg">
<PersonForm person={patient} />
</CollapsibleContent>
</Collapsible>
))}
</ul>
</div>
)}
</CardContent>
</Card>
</div>
</div>
)
}

View File

@@ -1,9 +1,20 @@
"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<File | null>(null);
const [transcription, setTranscription] = useState<string | null>(null);
@@ -32,7 +43,6 @@ const AudioTranscriber: React.FC = () => {
const formData = new FormData();
formData.append("file", audioFile);
console.log(audioFile);
setLoading(true);
setError(null); // Clear previous errors
try {
@@ -44,8 +54,9 @@ const AudioTranscriber: React.FC = () => {
}
})
console.log("! response:", response);
response = await response.json();
console.log("Transcription response:", response);
console.log("@ response:", response);
// if (response.data && response.data.transcription) {
// setTranscription(response.data.transcription);
@@ -107,49 +118,56 @@ const AudioTranscriber: React.FC = () => {
};
return (
<div>
<h1>
<section className="mx-auto my-auto bg-white dark:bg-neutral-950 h-screen w-1/2">
<h1 className="text-4xl font-bold text-center mb-8">
<center>
Audio Transcription
</center>
</h1>
<br />
<div>
<h2>Upload or Record Audio</h2>
<input type="file" accept="audio/*" onChange={handleFileChange} />
<button onClick={() => file && handleTranscription(file)} disabled={loading || !file}>
<Input type="file" accept="audio/*" onChange={handleFileChange} />
<Button className="my-4" onClick={() => file && handleTranscription(file)} disabled={loading || !file}>
{loading ? "Transcribing..." : "Transcribe"}
</button>
</Button>
</div>
<div>
<h2>Record Audio</h2>
{!recording ? (
<button onClick={startRecording}>Start Recording</button>
<Button onClick={startRecording}>Start Recording</Button>
) : (
<button onClick={stopRecording} disabled={!recording}>
Stop Recording
</button>
<Button onClick={stopRecording} disabled={!recording}>Stop Recording</Button>
)}
</div>
<div>
<h2>Transcription:</h2>
{loading ? (
<p>Processing transcription...</p>
) : transcription ? (
<p>{transcription}</p>
) : (
<p>No transcription available yet.</p>
)}
</div>
<Card className="mt-4 bg-neutral-200 dark:bg-neutral-800">
<CardHeader>
<CardTitle>Transcription Result</CardTitle>
</CardHeader>
<CardContent>
<CardContent>
{loading
? "Processing transcription..."
: transcription
? "Your transcription is ready"
: "No transcription available yet"}
</CardContent>
</CardContent>
</Card>
{error && (
<div style={{ color: "red" }}>
<Alert variant="destructive">
<AlertCircle className="h-4 w-4" />
<AlertTitle>Error</AlertTitle>
<AlertDescription>
<strong>Error:</strong> {error}
</div>
</AlertDescription>
</Alert>
)}
</div>
</section>
);
};

View File

@@ -22,6 +22,6 @@
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "src/app/api/connectDB.js", "src/lib/utils.js", "src/app/(web)/account/page.jsx", "src/app/(panels)/suite/patient/account/page.jsx", "src/app/(panels)/suite/patient/dashboard/MedicationTable.jsx", "src/app/(panels)/suite/patient/dashboard/page.jsx", "src/app/api/transcribe/route.js", "src/components/ui/calendar.jsx", "src/app/(web)/page.jsx", "src/app/(web)/transcribe/page.jsx", "src/app/(web)/login/page.jsx", "src/app/(panels)/suite/layout.jsx", "src/app/(panels)/suite/doctor/dashboard/page.jsx", "src/app/(panels)/suite/patient/chat/page.jsx", "src/app/api/chat/route.js"],
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "src/app/api/connectDB.js", "src/lib/utils.js", "src/app/(web)/account/page.jsx", "src/app/(panels)/suite/patient/account/page.jsx", "src/app/(panels)/suite/patient/dashboard/MedicationTable.jsx", "src/app/(panels)/suite/patient/dashboard/page.jsx", "src/app/api/transcribe/route.js", "src/components/ui/calendar.jsx", "src/app/(web)/page.jsx", "src/app/(web)/transcribe/page.jsx", "src/app/(web)/login/page.jsx", "src/app/(panels)/suite/layout.jsx", "src/app/(panels)/suite/doctor/dashboard/page.jsx", "src/app/(panels)/suite/patient/chat/page.jsx", "src/app/(panels)/suite/doctor/dashboard/AppList.jsx", "src/app/(panels)/suite/doctor/patient/page.jsx"],
"exclude": ["node_modules"]
}