This commit is contained in:
Sir Blob
2025-01-25 06:42:37 -05:00
13 changed files with 316 additions and 46 deletions

View File

@@ -12,12 +12,14 @@
"@clerk/nextjs": "^6.10.2",
"@radix-ui/react-dialog": "^1.1.5",
"@radix-ui/react-dropdown-menu": "^2.1.5",
"@radix-ui/react-label": "^2.1.1",
"@radix-ui/react-navigation-menu": "^1.2.4",
"@radix-ui/react-separator": "^1.1.1",
"@radix-ui/react-slot": "^1.1.1",
"@radix-ui/react-tooltip": "^1.1.7",
"@vercel/analytics": "^1.4.1",
"@vercel/speed-insights": "^1.1.0",
"axios": "^1.7.9",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"hoyahax-2025": "file:",

View File

@@ -0,0 +1,162 @@
"use client"
import { useState, useEffect } from 'react';
import axios from 'axios';
import { useUser } from '@clerk/clerk-react';
import { Button } from '../../../components/ui/button';
import { Input } from '../../../components/ui/input';
import { Label } from '../../../components/ui/label';
import { Card, CardHeader, CardContent, CardFooter } from '../../../components/ui/card';
const AccountPage = () => {
const { user } = useUser();
const [userData, setUserData] = useState(null);
const [patients, setPatients] = useState([]);
const [selectedPatient, setSelectedPatient] = useState(null);
const [medications, setMedications] = useState([]);
const [diagnoses, setDiagnoses] = useState([]);
useEffect(() => {
if (user) {
axios.get(`/api/user?userId=${user.id}`).then(response => {
setUserData(response.data);
if (response.data.role === 'caregiver') {
axios.get('/api/patients').then(res => setPatients(res.data));
}
});
}
}, [user]);
const handleRoleChange = async () => {
const newRole = userData.role === 'patient' ? 'caregiver' : 'patient';
await axios.put(`/api/user?userId=${user.id}`, { role: newRole });
setUserData({ ...userData, role: newRole });
if (newRole === 'caregiver') {
axios.get('/api/patients').then(res => setPatients(res.data));
} else {
setPatients([]);
setSelectedPatient(null);
}
};
const handlePatientSelect = (patient) => {
setSelectedPatient(patient);
setMedications(patient.medications);
setDiagnoses(patient.medicalConditions);
};
const handleMedicationsChange = (index, field, value) => {
const updatedMedications = [...medications];
updatedMedications[index][field] = value;
setMedications(updatedMedications);
};
const handleDiagnosesChange = (e) => {
setDiagnoses(e.target.value.split(','));
};
const handleSave = async () => {
await axios.put(`/api/patients/${selectedPatient.email}`, {
medications,
medicalConditions: diagnoses,
});
alert('Patient data updated successfully');
};
if (!userData) return <div>Loading...</div>;
return (
<div className="container mx-auto p-4">
<Card>
<CardHeader>
<h1 className="text-2xl font-bold">Account Page</h1>
</CardHeader>
<CardContent>
<div className="mb-4">
<Label>Name:</Label>
<p>{userData.name}</p>
</div>
<div className="mb-4">
<Label>Email:</Label>
<p>{userData.email}</p>
</div>
<div className="mb-4">
<Label>Role:</Label>
<p>{userData.role}</p>
</div>
<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 => (
<li
key={patient.email}
onClick={() => handlePatientSelect(patient)}
className="cursor-pointer hover:bg-gray-200 p-2"
>
{patient.name}
</li>
))}
</ul>
{selectedPatient && (
<Card>
<CardHeader>
<h3 className="text-xl font-bold">Edit Patient: {selectedPatient.name}</h3>
</CardHeader>
<CardContent>
<div className="mb-4">
<Label>Medications:</Label>
{medications.map((medication, index) => (
<div key={index} className="mb-2">
<Input
type="text"
placeholder="Name"
value={medication.name}
onChange={(e) => handleMedicationsChange(index, 'name', e.target.value)}
className="mb-2"
/>
<Input
type="text"
placeholder="Dosage"
value={medication.dosage}
onChange={(e) => handleMedicationsChange(index, 'dosage', e.target.value)}
className="mb-2"
/>
<Input
type="text"
placeholder="Frequency"
value={medication.frequency}
onChange={(e) => handleMedicationsChange(index, 'frequency', e.target.value)}
className="mb-2"
/>
</div>
))}
</div>
<div className="mb-4">
<Label>Diagnoses:</Label>
<Input
type="text"
value={diagnoses.join(',')}
onChange={handleDiagnosesChange}
/>
</div>
</CardContent>
<CardFooter>
<Button onClick={handleSave}>Save</Button>
</CardFooter>
</Card>
)}
</div>
)}
</CardContent>
</Card>
</div>
);
};
export default AccountPage;

View File

@@ -1,5 +1,6 @@
"use client"
import { ClerkProvider } from '@clerk/nextjs';
import { Navbar } from '@/components/navbar';
import { Footer } from '@/components/footer';
import { ThemeProvider } from '@/components/theme-provider';
@@ -14,11 +15,13 @@ export default function RootLayout({
return (
<html lang="en">
<body>
<ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange>
<Navbar />
<main suppressHydrationWarning>{children}</main>
<Footer />
</ThemeProvider>
<ClerkProvider>
<ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange>
<Navbar />
<main suppressHydrationWarning>{children}</main>
<Footer />
</ThemeProvider>
</ClerkProvider>
</body>
</html>
)

16
src/app/api/layout.tsx Normal file
View File

@@ -0,0 +1,16 @@
export const metadata = {
title: 'Next.js',
description: 'Generated by Next.js',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}

9
src/app/api/patients.js Normal file
View File

@@ -0,0 +1,9 @@
import User from '../../models/User';
import { connectDB } from '../../lib/utils';
export default async (req, res) => {
await connectDB();
const patients = await User.find({ role: 'patient' });
res.json(patients);
};

View File

@@ -0,0 +1,21 @@
import User from '../../../models/User';
import connectDB from '../../../lib/utils';
export default async (req, res) => {
await connectDB();
const { email } = req.query;
const { medications, medicalConditions } = req.body;
const patient = await User.findOne({ email });
if (!patient) {
return res.status(404).json({ message: 'Patient not found' });
}
patient.medications = medications;
patient.medicalConditions = medicalConditions;
await patient.save();
res.json(patient);
};

28
src/app/api/user.js Normal file
View File

@@ -0,0 +1,28 @@
import User from '../../models/User';
import { connectDB } from '../../lib/utils';
export default async (req, res) => {
await connectDB();
const { userId } = req.query;
if (!userId) {
return res.status(401).json({ message: 'Unauthorized' });
}
const user = await User.findOne({ id: userId });
if (!user) {
return res.status(404).json({ message: 'User not found' });
}
if (req.method === 'GET') {
res.json(user);
} else if (req.method === 'PUT') {
const { role } = req.body;
user.role = role;
await user.save();
res.json(user);
} else {
res.status(405).json({ message: 'Method not allowed' });
}
};

View File

@@ -1,42 +1,10 @@
import { User } from '../../../models/User';
import { NextResponse } from 'next/server';
import { Webhook } from 'svix';
import mongoose from "mongoose";
import { connectDB } from '../../../lib/utils';
const CLERK_WEBHOOK_SECRET = process.env.CLERK_WEBHOOK_SECRET;
const DATABASE_URL = process.env.MONGO_URI;
let cached = global.mongoose;
if (!cached) {
cached = global.mongoose = { conn: null, promise: null };
}
async function connectDB() {
if (cached.conn) {
return cached.conn;
}
if (!cached.promise) {
const opts = {
bufferCommands: false,
};
try {
cached.promise = mongoose.connect(DATABASE_URL, opts).then((mongoose) => {
console.log('MongoDB connected successfully');
return mongoose;
});
} catch (error) {
console.error('Error connecting to MongoDB:', error.message);
throw new Error('Error connecting to MongoDB');
}
}
cached.conn = await cached.promise;
return cached.conn;
}
export async function POST(req) {
console.log('Received request:', req);
@@ -81,7 +49,7 @@ export async function POST(req) {
const eventType = evt.type;
if (eventType === 'user.created') {
const { first_name, last_name, email_addresses } = evt.data;
const { id, first_name, last_name, email_addresses } = evt.data;
const email = email_addresses?.[0]?.email_address || null;
if (!email) {
@@ -106,6 +74,7 @@ export async function POST(req) {
}
user = new User({
id,
name,
email,
role: 'patient',

View File

@@ -0,0 +1,26 @@
"use client"
import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
)
const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => (
<LabelPrimitive.Root
ref={ref}
className={cn(labelVariants(), className)}
{...props}
/>
))
Label.displayName = LabelPrimitive.Root.displayName
export { Label }

35
src/lib/utils.js Normal file
View File

@@ -0,0 +1,35 @@
import { clsx } from "clsx";
import { twMerge } from "tailwind-merge";
import mongoose from "mongoose";
export function cn(...inputs) {
return twMerge(clsx(inputs));
}
const cached = global.mongoose || { conn: null, promise: null };
export async function connectDB() {
const DATABASE_URL = process.env.MONGO_URI;
if (cached.conn) {
return cached.conn;
}
if (!cached.promise) {
const opts = {
bufferCommands: false,
};
cached.promise = mongoose.connect(DATABASE_URL, opts).then((mongoose) => {
console.log('MongoDB connected successfully');
return mongoose;
}).catch((error) => {
console.error('Error connecting to MongoDB:', error.message);
throw new Error('Error connecting to MongoDB');
});
}
cached.conn = await cached.promise;
global.mongoose = cached;
return cached.conn;
}

View File

@@ -1,6 +0,0 @@
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}

View File

@@ -2,6 +2,11 @@ import { Schema, model, models } from "mongoose";
const UserSchema = new Schema(
{
id: {
type: String,
required: true,
unique: true,
},
name: {
type: String,
required: true,

View File

@@ -22,6 +22,6 @@
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "src/app/api/connectDB.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"],
"exclude": ["node_modules"]
}