├── src ├── api │ └── authApi.js ├── index.css ├── redux │ ├── store.js │ └── authSlice.js ├── index.js ├── pages │ ├── AdminDashboard.js │ ├── DoctorProfile.js │ ├── AskAI.js │ ├── ConnectDoctors.js │ ├── Login.js │ ├── PatientDashboard.js │ ├── BookAppointmentPage.js │ ├── Home.js │ ├── DoctorDashboard.js │ └── SignUp.js ├── App.js └── components │ ├── UploadPatientDetails.js │ ├── ChatBox.js │ ├── MedicalHistoryPanel.js │ ├── Navbar.js │ ├── DocumentUploadPanel.js │ └── Footer.js ├── public ├── images │ ├── admin.jpg │ ├── doctor.png │ └── signupDoc.png └── index.html ├── postcss.config.js ├── .gitignore ├── tailwind.config.js ├── server ├── routes │ ├── aiRoutes.js │ ├── adminRoutes.js │ ├── doctorRoutes.js │ ├── appointmentRoutes.js │ ├── paymentRoutes.js │ ├── doctorDocumentRoutes.js │ ├── userRoute.js │ └── medicalNoteRoutes.js ├── utils │ ├── cloudinary.js │ ├── sendThankYouEmail.js │ ├── sendConfirmationEmails.js │ ├── sendAppointmentOTPEmail.js │ └── socket.js ├── config │ ├── cloudinary.js │ └── database.js ├── middleware │ ├── multer.js │ └── authMiddleware.js ├── models │ ├── otpModel.js │ ├── appointmentModel.js │ ├── Message.js │ ├── Admin.js │ ├── DoctorDocument.js │ ├── MedicalNote.js │ ├── userModel.js │ └── Doctor.js ├── package.json ├── controllers │ ├── doctorDocumentController.js │ ├── medicalNoteController.js │ ├── doctorController.js │ ├── aiController.js │ ├── adminController.js │ ├── paymentController.js │ ├── appointmentController.js │ └── authController.js └── index.js ├── package.json └── README.md /src/api/authApi.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /public/images/admin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prakash-pr07/DocTreat/HEAD/public/images/admin.jpg -------------------------------------------------------------------------------- /public/images/doctor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prakash-pr07/DocTreat/HEAD/public/images/doctor.png -------------------------------------------------------------------------------- /public/images/signupDoc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prakash-pr07/DocTreat/HEAD/public/images/signupDoc.png -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Node modules 2 | node_modules 3 | 4 | # Environment variables 5 | .env 6 | 7 | # Database files or folders 8 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ["./src/**/*.{js,jsx,ts,tsx}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | } 9 | -------------------------------------------------------------------------------- /server/routes/aiRoutes.js: -------------------------------------------------------------------------------- 1 | // server/routes/aiRoutes.js 2 | import express from "express"; 3 | import { askMedicalAI } from "../controllers/aiController.js"; 4 | 5 | const router = express.Router(); 6 | router.post("/ask-ai", askMedicalAI); 7 | export default router; 8 | -------------------------------------------------------------------------------- /server/routes/adminRoutes.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { addAdmin, loginAdmin } from "../controllers/adminController.js"; 3 | 4 | const router = express.Router(); 5 | 6 | router.post("/add", addAdmin); 7 | router.post("/login", loginAdmin); // ✅ Add this 8 | 9 | export default router; 10 | -------------------------------------------------------------------------------- /server/routes/doctorRoutes.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { searchDoctorsByCity } from "../controllers/doctorController.js"; 3 | 4 | const router = express.Router(); 5 | 6 | // ✅ Keep only this route 7 | router.get("/search/city", searchDoctorsByCity); 8 | 9 | export default router; 10 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | OTP Project 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /server/utils/cloudinary.js: -------------------------------------------------------------------------------- 1 | import { v2 as cloudinary } from "cloudinary"; 2 | import dotenv from "dotenv"; 3 | dotenv.config(); 4 | 5 | cloudinary.config({ 6 | cloud_name: process.env.CLOUD_NAME, 7 | api_key: process.env.CLOUD_API_KEY, 8 | api_secret: process.env.CLOUD_API_SECRET, 9 | }); 10 | 11 | export default cloudinary; 12 | -------------------------------------------------------------------------------- /server/config/cloudinary.js: -------------------------------------------------------------------------------- 1 | import cloudinary from "cloudinary"; 2 | import dotenv from "dotenv"; 3 | dotenv.config(); 4 | 5 | cloudinary.v2.config({ 6 | cloud_name: process.env.CLOUDINARY_CLOUD_NAME, 7 | api_key: process.env.CLOUDINARY_API_KEY, 8 | api_secret: process.env.CLOUDINARY_API_SECRET, 9 | }); 10 | 11 | export default cloudinary; 12 | -------------------------------------------------------------------------------- /src/redux/store.js: -------------------------------------------------------------------------------- 1 | // src/redux/store.js 2 | import { configureStore } from "@reduxjs/toolkit"; 3 | import authReducer from "./authSlice"; // ✅ Make sure you create this 4 | 5 | const store = configureStore({ 6 | reducer: { 7 | auth: authReducer, // You can add more reducers here like `user`, `cart`, etc. 8 | }, 9 | }); 10 | 11 | export default store; 12 | -------------------------------------------------------------------------------- /server/routes/appointmentRoutes.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { bookAppointment, verifyAppointmentOtp } from "../controllers/appointmentController.js"; 3 | const router = express.Router(); 4 | 5 | router.post("/book", bookAppointment); // for sending OTP 6 | router.post("/verify-otp", verifyAppointmentOtp); // for OTP verification 7 | 8 | export default router; 9 | -------------------------------------------------------------------------------- /server/routes/paymentRoutes.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { createOrder, verifyPayment } from "../controllers/paymentController.js"; 3 | import { isAuthenticated } from "../middleware/authMiddleware.js"; 4 | 5 | const router = express.Router(); 6 | 7 | router.post("/create-order", isAuthenticated, createOrder); 8 | router.post("/verify", isAuthenticated, verifyPayment); 9 | 10 | export default router; 11 | -------------------------------------------------------------------------------- /server/config/database.js: -------------------------------------------------------------------------------- 1 | // config/database.js 2 | import mongoose from "mongoose"; 3 | 4 | export const connect = async () => { 5 | try { 6 | await mongoose.connect(process.env.MONGODB_URI, { 7 | useNewUrlParser: true, 8 | useUnifiedTopology: true, 9 | }); 10 | console.log("MongoDB Connected"); 11 | } catch (err) { 12 | console.error("MongoDB Connection Failed:", err); 13 | process.exit(1); 14 | } 15 | }; 16 | 17 | -------------------------------------------------------------------------------- /server/middleware/multer.js: -------------------------------------------------------------------------------- 1 | import multer from "multer"; 2 | import { CloudinaryStorage } from "multer-storage-cloudinary"; 3 | import cloudinary from "../utils/cloudinary.js"; 4 | 5 | const storage = new CloudinaryStorage({ 6 | cloudinary, 7 | params: { 8 | folder: "doctreat-doctor-documents", 9 | allowed_formats: ["jpg", "jpeg", "png", "pdf"], 10 | }, 11 | }); 12 | 13 | const upload = multer({ storage }); 14 | 15 | export default upload; 16 | -------------------------------------------------------------------------------- /server/models/otpModel.js: -------------------------------------------------------------------------------- 1 | // models/otpModel.js 2 | 3 | import mongoose from "mongoose"; 4 | 5 | const otpSchema = new mongoose.Schema({ 6 | email: { 7 | type: String, 8 | required: true, 9 | }, 10 | otp: { 11 | type: String, 12 | required: true, 13 | }, 14 | createdAt: { 15 | type: Date, 16 | default: Date.now, 17 | expires: 300, // auto-delete after 5 mins 18 | }, 19 | }); 20 | 21 | export default mongoose.model("OTP", otpSchema); 22 | 23 | -------------------------------------------------------------------------------- /server/models/appointmentModel.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const appointmentSchema = new mongoose.Schema({ 4 | patientName: String, 5 | age: Number, 6 | gender: String, 7 | phoneNo: String, 8 | email: String, 9 | date: String, 10 | time: String, 11 | doctorId: { 12 | type: mongoose.Schema.Types.ObjectId, 13 | ref: "User", 14 | }, 15 | isConfirmed: { 16 | type: Boolean, 17 | default: false, 18 | }, 19 | otp: String, 20 | }); 21 | 22 | export default mongoose.model("Appointment", appointmentSchema); 23 | -------------------------------------------------------------------------------- /server/routes/doctorDocumentRoutes.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { uploadDoctorDocument, getAllDoctorDocuments } from "../controllers/doctorDocumentController.js"; 3 | import upload from "../middleware/multer.js"; // ✅ Make sure file is in `middlewares/` folder 4 | 5 | const router = express.Router(); 6 | 7 | // POST /api/doctor/upload-document 8 | router.post("/upload-document", upload.single("document"), uploadDoctorDocument); 9 | 10 | // GET /api/doctor/all-documents 11 | router.get("/all-documents", getAllDoctorDocuments); 12 | 13 | export default router; 14 | -------------------------------------------------------------------------------- /server/models/Message.js: -------------------------------------------------------------------------------- 1 | // models/Message.js 2 | 3 | import mongoose from "mongoose"; 4 | 5 | const messageSchema = new mongoose.Schema( 6 | { 7 | senderEmail: { 8 | type: String, 9 | required: true, 10 | }, 11 | receiverEmail: { 12 | type: String, 13 | required: true, 14 | }, 15 | text: { 16 | type: String, 17 | required: true, 18 | }, 19 | }, 20 | { 21 | timestamps: true, // createdAt and updatedAt 22 | } 23 | ); 24 | 25 | const Message = mongoose.model("Message", messageSchema); 26 | export default Message; 27 | -------------------------------------------------------------------------------- /server/models/Admin.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const adminSchema = new mongoose.Schema({ 4 | firstName: { type: String, required: true }, 5 | lastName: { type: String, required: true }, 6 | email: { type: String, required: true, unique: true }, 7 | phoneNo: { type: String, required: true }, 8 | city: { type: String, required: true }, 9 | state: { type: String, required: true }, 10 | password: { type: String, required: true }, // ✅ Add this 11 | role: { type: String, default: "Admin" }, 12 | }); 13 | 14 | export default mongoose.model("Admin", adminSchema); 15 | -------------------------------------------------------------------------------- /server/models/DoctorDocument.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const doctorDocumentSchema = new mongoose.Schema({ 4 | doctorEmail: { 5 | type: String, 6 | required: true, 7 | }, 8 | patientName: { 9 | type: String, 10 | required: true, 11 | }, 12 | patientEmail: { 13 | type: String, 14 | required: true, 15 | }, 16 | notes: { 17 | type: String, 18 | required: true, 19 | }, 20 | documentUrl: { 21 | type: String, 22 | required: true, 23 | }, 24 | uploadedAt: { 25 | type: Date, 26 | default: Date.now, 27 | }, 28 | }); 29 | 30 | export default mongoose.model("DoctorDocument", doctorDocumentSchema); 31 | -------------------------------------------------------------------------------- /server/models/MedicalNote.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const medicalNoteSchema = new mongoose.Schema( 4 | { 5 | patientId: { 6 | type: mongoose.Schema.Types.ObjectId, 7 | ref: "User", 8 | required: true, 9 | }, 10 | fileName: { 11 | type: String, 12 | required: true, 13 | }, 14 | notes: { 15 | type: String, 16 | }, 17 | documentUrl: { 18 | type: String, 19 | required: true, 20 | }, 21 | uploadedAt: { 22 | type: Date, 23 | default: Date.now, 24 | }, 25 | }, 26 | { timestamps: true } 27 | ); 28 | 29 | export const MedicalNote = mongoose.model("MedicalNote", medicalNoteSchema); 30 | -------------------------------------------------------------------------------- /server/models/userModel.js: -------------------------------------------------------------------------------- 1 | // models/userModel.js 2 | 3 | import mongoose from "mongoose"; 4 | 5 | const userSchema = new mongoose.Schema({ 6 | firstName: { type: String, required: true }, 7 | lastName: { type: String, required: true }, 8 | email: { type: String, required: true, unique: true }, 9 | phone: { type: String, required: true }, 10 | password: { type: String, required: true }, 11 | city: { type: String, required: true }, 12 | state: { type: String, required: true }, 13 | role: { type: String, enum: ["Patient", "Doctor"], default: "Patient" }, 14 | isPremium: { type: Boolean, default: false }, 15 | }); 16 | 17 | export default mongoose.model("User", userSchema); 18 | 19 | -------------------------------------------------------------------------------- /src/redux/authSlice.js: -------------------------------------------------------------------------------- 1 | // src/redux/authSlice.js 2 | import { createSlice } from "@reduxjs/toolkit"; 3 | 4 | const initialState = { 5 | user: null, 6 | token: null, 7 | isAuthenticated: false, 8 | }; 9 | 10 | const authSlice = createSlice({ 11 | name: "auth", 12 | initialState, 13 | reducers: { 14 | setUser(state, action) { 15 | state.user = action.payload; 16 | state.isAuthenticated = true; 17 | }, 18 | setToken(state, action) { 19 | state.token = action.payload; 20 | }, 21 | logout(state) { 22 | state.user = null; 23 | state.token = null; 24 | state.isAuthenticated = false; 25 | }, 26 | }, 27 | }); 28 | 29 | export const { setUser, setToken, logout } = authSlice.actions; 30 | export default authSlice.reducer; 31 | -------------------------------------------------------------------------------- /server/routes/userRoute.js: -------------------------------------------------------------------------------- 1 | 2 | import express from "express"; 3 | import { 4 | signup, 5 | login, 6 | sendOtp, 7 | verifyOtp, 8 | forgotPassword, 9 | resetPassword, 10 | updatePassword, 11 | } from "../controllers/authController.js"; 12 | 13 | import { isAuthenticated } from "../middleware/authMiddleware.js"; 14 | 15 | const router = express.Router(); 16 | 17 | // OTP routes 18 | router.post("/send-otp", sendOtp); 19 | router.post("/verify-otp", verifyOtp); 20 | 21 | // Auth routes 22 | router.post("/signup", signup); 23 | router.post("/login", login); 24 | 25 | // Password routes 26 | router.post("/forgot-password", forgotPassword); 27 | router.post("/reset-password", resetPassword); 28 | router.post("/update-password", isAuthenticated, updatePassword); 29 | 30 | export default router; 31 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App"; 4 | import { Provider } from "react-redux"; 5 | import store from "./redux/store"; 6 | import "./index.css"; 7 | 8 | const root = ReactDOM.createRoot(document.getElementById("root")); 9 | root.render( 10 | 11 | 12 | 13 | ); 14 | 15 | 16 | 17 | // import React from "react"; 18 | // import ReactDOM from "react-dom/client"; 19 | // import { BrowserRouter } from "react-router-dom"; 20 | // import App from "./App"; 21 | // import "./index.css"; 22 | 23 | // const root = ReactDOM.createRoot(document.getElementById("root")); 24 | // root.render( 25 | // 26 | // 27 | // 28 | // 29 | // 30 | // ); -------------------------------------------------------------------------------- /server/utils/sendThankYouEmail.js: -------------------------------------------------------------------------------- 1 | import nodemailer from "nodemailer"; 2 | 3 | export const sendThankYouEmail = async (email) => { 4 | try { 5 | const transporter = nodemailer.createTransport({ 6 | service: "gmail", 7 | auth: { 8 | user: process.env.EMAIL_USER, 9 | pass: process.env.EMAIL_PASS, 10 | }, 11 | }); 12 | 13 | const mailOptions = { 14 | from: `"DocTreat" <${process.env.EMAIL_USER}>`, 15 | to: email, 16 | subject: "Welcome to DocTreat Premium", 17 | text: "Thank you for becoming a Premium Member of DocTreat. You now have full access to doctor profiles and premium features.", 18 | }; 19 | 20 | await transporter.sendMail(mailOptions); 21 | console.log("Thank you email sent successfully to", email); 22 | } catch (error) { 23 | console.error("Failed to send thank-you email:", error.message); 24 | } 25 | }; 26 | 27 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "otp-backend", 3 | "version": "1.0.0", 4 | "description": "otp-backend", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "start": "node index.js", 9 | "dev": "nodemon index.js" 10 | }, 11 | "keywords": [ 12 | "otp-backend" 13 | ], 14 | "author": "Saikat Mukherjee", 15 | "license": "ISC", 16 | "dependencies": { 17 | "bcrypt": "^5.1.0", 18 | "bcryptjs": "^2.4.3", 19 | "cloudinary": "^1.36.4", 20 | "cookie-parser": "^1.4.6", 21 | "cors": "^2.8.5", 22 | "crypto-random-string": "^5.0.0", 23 | "dotenv": "^16.0.3", 24 | "express": "^4.18.2", 25 | "express-fileupload": "^1.4.0", 26 | "jsonwebtoken": "^9.0.0", 27 | "mongoose": "^7.0.3", 28 | "node-schedule": "^2.1.1", 29 | "nodemailer": "^6.9.1", 30 | "nodemon": "^2.0.22", 31 | "otp-generator": "^4.0.1", 32 | "socket.io": "^4.8.1", 33 | "razorpay": "^2.8.6" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /server/models/Doctor.js: -------------------------------------------------------------------------------- 1 | // server/models/Doctor.js 2 | import mongoose from "mongoose"; 3 | 4 | const doctorSchema = new mongoose.Schema({ 5 | firstName: { 6 | type: String, 7 | required: true, 8 | trim: true, 9 | }, 10 | lastName: { 11 | type: String, 12 | required: true, 13 | trim: true, 14 | }, 15 | email: { 16 | type: String, 17 | required: true, 18 | unique: true, 19 | lowercase: true, 20 | }, 21 | phoneNo: { 22 | type: String, 23 | required: true, 24 | }, 25 | city: { 26 | type: String, 27 | required: true, 28 | trim: true, 29 | }, 30 | state: { 31 | type: String, 32 | required: true, 33 | trim: true, 34 | }, 35 | profilePic: { 36 | type: String, 37 | default: "", // URL of image if needed 38 | }, 39 | role: { 40 | type: String, 41 | default: "Doctor", 42 | }, 43 | }); 44 | 45 | const Doctor = mongoose.model("Doctor", doctorSchema); 46 | 47 | export default Doctor; 48 | -------------------------------------------------------------------------------- /server/routes/medicalNoteRoutes.js: -------------------------------------------------------------------------------- 1 | // import express from "express"; 2 | // import multer from "multer"; 3 | // import { 4 | // uploadMedicalNote, 5 | // getMedicalNotes, 6 | // } from "../controllers/medicalNoteController.js"; 7 | 8 | // const router = express.Router(); 9 | 10 | // const storage = multer.diskStorage({}); 11 | // const upload = multer({ storage }); 12 | 13 | // router.post("/upload", upload.single("document"), uploadMedicalNote); 14 | // router.get("/:patientId", getMedicalNotes); 15 | 16 | // export default router; 17 | 18 | 19 | 20 | import express from "express"; 21 | import multer from "multer"; 22 | import { uploadMedicalNote, getMedicalNotes } from "../controllers/medicalNoteController.js"; 23 | 24 | const router = express.Router(); 25 | 26 | // 🧠 Multer Storage: use memory for temp file upload (can also use diskStorage if needed) 27 | const storage = multer.diskStorage({}); 28 | const upload = multer({ storage }); 29 | 30 | router.post("/upload", upload.single("document"), uploadMedicalNote); 31 | router.get("/:patientEmail", getMedicalNotes); 32 | 33 | export default router; 34 | -------------------------------------------------------------------------------- /server/utils/sendConfirmationEmails.js: -------------------------------------------------------------------------------- 1 | import nodemailer from "nodemailer"; 2 | 3 | export const sendConfirmationEmails = async (patientEmail, doctorEmail, details) => { 4 | try { 5 | const transporter = nodemailer.createTransport({ 6 | service: "gmail", 7 | auth: { 8 | user: process.env.EMAIL_USER, 9 | pass: process.env.EMAIL_PASS, 10 | }, 11 | }); 12 | 13 | const content = ` 14 | Appointment Confirmed 🎉 15 | 16 | Name: ${details.patientName} 17 | Age: ${details.age} 18 | Gender: ${details.gender} 19 | Phone: ${details.phoneNo} 20 | Email: ${details.email} 21 | Date: ${details.date} 22 | Time: ${details.time} 23 | `; 24 | 25 | await transporter.sendMail({ 26 | from: `"DocTreat" <${process.env.EMAIL_USER}>`, 27 | to: patientEmail, 28 | subject: "Appointment Confirmed", 29 | text: content, 30 | }); 31 | 32 | await transporter.sendMail({ 33 | from: `"DocTreat" <${process.env.EMAIL_USER}>`, 34 | to: doctorEmail, 35 | subject: "New Patient Appointment Confirmed", 36 | text: content, 37 | }); 38 | } catch (err) { 39 | console.error("Confirmation Email Error:", err); 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /server/controllers/doctorDocumentController.js: -------------------------------------------------------------------------------- 1 | import DoctorDocument from "../models/DoctorDocument.js"; 2 | 3 | export const uploadDoctorDocument = async (req, res) => { 4 | try { 5 | const { patientName, patientEmail, notes } = req.body; 6 | 7 | if (!req.file || !patientName || !patientEmail) { 8 | return res.status(400).json({ error: "Missing required fields" }); 9 | } 10 | 11 | const newDoc = new DoctorDocument({ 12 | doctorEmail: req.user.email, // Or directly from token/session 13 | patientName, 14 | patientEmail, 15 | notes, 16 | documentUrl: req.file.path, 17 | }); 18 | 19 | await newDoc.save(); 20 | res.status(201).json({ message: "Document uploaded successfully" }); 21 | } catch (err) { 22 | console.error("Upload Failed:", err); 23 | res.status(500).json({ error: "Upload failed" }); 24 | } 25 | }; 26 | 27 | export const getAllDoctorDocuments = async (req, res) => { 28 | try { 29 | const doctorEmail = req.user.email; 30 | 31 | const docs = await DoctorDocument.find({ doctorEmail }).sort({ uploadedAt: -1 }); 32 | res.status(200).json(docs); 33 | } catch (err) { 34 | res.status(500).json({ error: "Fetching failed" }); 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /server/utils/sendAppointmentOTPEmail.js: -------------------------------------------------------------------------------- 1 | import nodemailer from "nodemailer"; 2 | 3 | export const sendAppointmentOTPEmail = async ( 4 | doctorEmail, 5 | otp, 6 | name, 7 | age, 8 | gender, 9 | phoneNo, 10 | email, 11 | date, 12 | time 13 | ) => { 14 | try { 15 | const transporter = nodemailer.createTransport({ 16 | service: "gmail", 17 | auth: { 18 | user: process.env.EMAIL_USER, 19 | pass: process.env.EMAIL_PASS, 20 | }, 21 | }); 22 | 23 | const mailOptions = { 24 | from: `"DocTreat" <${process.env.EMAIL_USER}>`, 25 | to: doctorEmail, 26 | subject: "New Appointment Request - OTP Verification Required", 27 | text: `You have received a new appointment request from a patient. 28 | 29 | 📋 Patient Details: 30 | • Name : ${name} 31 | • Age : ${age} 32 | • Gender : ${gender} 33 | • Phone : ${phoneNo} 34 | • Email : ${email} 35 | • Date : ${date} 36 | • Time : ${time} 37 | 38 | 🔐 OTP for confirmation: ${otp} 39 | 40 | Please share this OTP with the Patient to confirm the appointment. 41 | 42 | - DocTreat Team`, 43 | }; 44 | 45 | await transporter.sendMail(mailOptions); 46 | } catch (err) { 47 | console.error("OTP Email Send Error:", err); 48 | } 49 | }; 50 | -------------------------------------------------------------------------------- /src/pages/AdminDashboard.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const AdminDashboard = () => { 4 | // Dummy data — in real case, you can fetch from localStorage or API 5 | const admin = { 6 | profilePic: "/images/admin.jpg", // ✅ path from public folder 7 | firstName: "Prakash Shekhar", 8 | lastName: "Singh", 9 | email: "prakashranjan8454@gmail.com", 10 | phoneNo: "9576409201", 11 | city: "Patna", 12 | state: "Bihar", 13 | }; 14 | 15 | return ( 16 |
17 |
18 | Admin Profile 23 |

24 | {admin.firstName} {admin.lastName} 25 |

26 |

{admin.email}

27 |

{admin.phoneNo}

28 |

{admin.city}, {admin.state}

29 | 30 |
31 |

Owner

32 |
33 |
34 |
35 | ); 36 | }; 37 | 38 | export default AdminDashboard; 39 | -------------------------------------------------------------------------------- /server/controllers/medicalNoteController.js: -------------------------------------------------------------------------------- 1 | 2 | import MedicalNote from "../models/MedicalNote.js"; 3 | import cloudinary from "../utils/cloudinary.js"; 4 | 5 | export const uploadMedicalNote = async (req, res) => { 6 | try { 7 | const { fileName, notes, patientEmail } = req.body; 8 | 9 | if (!req.file || !fileName || !patientEmail) { 10 | return res.status(400).json({ 11 | success: false, 12 | message: "File, File Name, and Patient ID are required", 13 | }); 14 | } 15 | 16 | // Upload to Cloudinary 17 | const result = await cloudinary.uploader.upload(req.file.path, { 18 | folder: "medical-documents", 19 | resource_type: "auto", 20 | }); 21 | 22 | const newNote = await MedicalNote.create({ 23 | patientEmail, 24 | fileName, 25 | notes, 26 | documentUrl: result.secure_url, 27 | }); 28 | 29 | res.status(201).json({ success: true, note: newNote }); 30 | } catch (error) { 31 | console.error("Upload Error:", error); 32 | res.status(500).json({ success: false, message: "Upload failed", error }); 33 | } 34 | }; 35 | 36 | export const getMedicalNotes = async (req, res) => { 37 | try { 38 | const { patientEmail } = req.params; 39 | 40 | const notes = await MedicalNote.find({ patientEmail }).sort({ uploadedAt: -1 }); 41 | 42 | res.status(200).json({ success: true, notes }); 43 | } catch (error) { 44 | console.error("Fetch Error:", error); 45 | res.status(500).json({ success: false, message: "Fetch failed", error }); 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /server/controllers/doctorController.js: -------------------------------------------------------------------------------- 1 | // Update: Use User model instead of Doctor 2 | import User from "../models/userModel.js"; 3 | 4 | // 🔍 Search doctors by city from `User` model 5 | export const searchDoctorsByCity = async (req, res) => { 6 | try { 7 | const { city } = req.query; 8 | 9 | if (!city) { 10 | return res.status(400).json({ success: false, message: "City is required" }); 11 | } 12 | 13 | const doctors = await User.find({ 14 | city: { $regex: new RegExp(city, "i") }, 15 | role: "Doctor" 16 | }).select("-password"); // Don't send password 17 | 18 | res.status(200).json({ 19 | success: true, 20 | doctors, 21 | }); 22 | } catch (error) { 23 | console.error("Error searching doctors by city:", error); 24 | res.status(500).json({ success: false, message: "Server Error" }); 25 | } 26 | }; 27 | 28 | 29 | 30 | // Keep this if you still want to manually add Doctor via /api/v1/doctors // 31 | 32 | 33 | export const addDoctor = async (req, res) => { 34 | try { 35 | const { firstName, lastName, email, phoneNo, city, state } = req.body; 36 | 37 | if (!firstName || !lastName || !email || !phoneNo || !city || !state) { 38 | return res.status(400).json({ success: false, message: "All fields are required" }); 39 | } 40 | 41 | const newDoctor = new Doctor({ firstName, lastName, email, phoneNo, city, state }); 42 | await newDoctor.save(); 43 | 44 | res.status(201).json({ success: true, message: "Doctor added successfully", doctor: newDoctor }); 45 | } catch (error) { 46 | console.error("Error adding doctor:", error); 47 | res.status(500).json({ success: false, message: "Server Error" }); 48 | } 49 | }; 50 | 51 | 52 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import dotenv from "dotenv"; 3 | import cors from "cors"; 4 | import cookieParser from "cookie-parser"; 5 | import userRoutes from "./routes/userRoute.js"; 6 | import aiRoutes from "./routes/aiRoutes.js"; 7 | import doctorRoutes from "./routes/doctorRoutes.js"; 8 | import adminRoutes from "./routes/adminRoutes.js"; 9 | 10 | import paymentRoutes from "./routes/paymentRoutes.js"; 11 | import { connect } from "./config/database.js"; 12 | import appointmentRoutes from "./routes/appointmentRoutes.js"; 13 | 14 | import doctorDocumentRoutes from "./routes/doctorDocumentRoutes.js"; 15 | import medicalNoteRoutes from "./routes/medicalNoteRoutes.js"; 16 | import http from "http"; 17 | import { initSocket } from "./utils/socket.js"; // Import socket initializer 18 | 19 | 20 | 21 | dotenv.config(); 22 | const app = express(); 23 | const server = http.createServer(app); // Wrap app with HTTP server 24 | 25 | // Initialize Socket.IO 26 | initSocket(server); 27 | 28 | // Middlewares 29 | app.use(cors({ origin: "http://localhost:3000", credentials: true })); 30 | app.use(cookieParser()); 31 | app.use(express.json()); 32 | 33 | // DB connect 34 | connect(); 35 | 36 | // Routes 37 | app.use("/api/v1", userRoutes); 38 | app.use("/api/v1", aiRoutes); 39 | app.use("/api/v1/doctors", doctorRoutes); 40 | app.use("/api/v1/admins", adminRoutes); 41 | 42 | app.use("/api/payment", paymentRoutes); 43 | app.use("/api/appointment", appointmentRoutes); 44 | 45 | app.use("/api/medical-notes", medicalNoteRoutes); 46 | app.use("/api/doctor", doctorDocumentRoutes); 47 | 48 | // 👇 Start server 49 | const PORT = process.env.PORT || 8000; 50 | server.listen(PORT, () => { 51 | console.log(`🚀 Server running on port ${PORT}`); 52 | }); 53 | -------------------------------------------------------------------------------- /server/controllers/aiController.js: -------------------------------------------------------------------------------- 1 | // server/controllers/aiController.js 2 | import OpenAI from "openai"; 3 | import dotenv from "dotenv"; 4 | dotenv.config(); 5 | 6 | const openai = new OpenAI({ 7 | apiKey: process.env.OPENAI_API_KEY, 8 | }); 9 | 10 | export const askMedicalAI = async (req, res) => { 11 | try { 12 | const { message } = req.body; 13 | 14 | const response = await openai.chat.completions.create({ 15 | model: "gpt-3.5-turbo", 16 | messages: [ 17 | { 18 | role: "system", 19 | content: ` 20 | You are an intelligent and empathetic medical assistant trained to help users with health-related information only. Your core responsibility is to provide accurate, respectful, and easy-to-understand answers about medical symptoms, conditions, treatments, medications, general wellness, and healthcare practices. 21 | 22 | Maintain a professional, friendly, and non-alarming tone in all your responses. Your answers should be factual, concise, and accessible to a general audience — avoid medical jargon unless it's explained clearly. 23 | 24 | If a user asks something outside the scope of healthcare or medicine — such as legal, technical, entertainment, or unrelated personal questions — politely refuse and respond with: 25 | 26 | "I'm here to assist only with medical-related questions. Kindly ask something related to health or healthcare." 27 | 28 | Never provide any diagnosis, emergency medical advice, or prescriptions. Always encourage users to consult a qualified healthcare professional for personalized care or urgent situations. 29 | 30 | Stay strictly within the boundaries of medical domain knowledge. 31 | `.trim(), 32 | }, 33 | { 34 | role: "user", 35 | content: message, 36 | }, 37 | ], 38 | }); 39 | 40 | const reply = response.choices[0].message.content.trim(); 41 | res.status(200).json({ reply }); 42 | } catch (error) { 43 | console.error("AI Error:", error.message); 44 | res.status(500).json({ reply: "Internal server error" }); 45 | } 46 | }; 47 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | // src/App.js 2 | 3 | import React from "react"; 4 | import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; 5 | import Navbar from "./components/Navbar"; 6 | 7 | import Home from "./pages/Home"; 8 | import SignupPage from "./pages/SignUp"; 9 | import Login from "./pages/Login"; 10 | import DoctorDashboard from "./pages/DoctorDashboard"; 11 | import PatientDashboard from "./pages/PatientDashboard"; 12 | import ConnectDoctors from "./pages/ConnectDoctors"; 13 | import DoctorProfile from "./pages/DoctorProfile"; 14 | import AdminDashboard from "./pages/AdminDashboard"; 15 | import AskAI from "./pages/AskAI"; 16 | import BookAppointmentPage from "./pages/BookAppointmentPage"; 17 | // import ShareReview from "./pages/ShareReview"; 18 | 19 | import { ToastContainer } from "react-toastify"; 20 | import "react-toastify/dist/ReactToastify.css"; // Also make sure this is imported 21 | 22 | const App = () => { 23 | return ( 24 | 25 | 26 |
27 | 28 | } /> 29 | } /> 30 | } /> 31 | } /> 32 | } /> 33 | } /> 34 | } /> 35 | {/* } /> */} 36 | {/* Role-based dashboards */} 37 | } /> 38 | } /> 39 | } /> 40 | 41 |
42 | 43 | {/* ToastContainer should be outside Routes */} 44 | 45 |
46 | ); 47 | }; 48 | 49 | export default App; 50 | -------------------------------------------------------------------------------- /server/middleware/authMiddleware.js: -------------------------------------------------------------------------------- 1 | import jwt from "jsonwebtoken"; 2 | import User from "../models/userModel.js"; 3 | 4 | // ✅ Middleware to check if user is authenticated 5 | export const isAuthenticated = async (req, res, next) => { 6 | // const token = req.headers.authorization?.split(" ")[1]; 7 | // const token = req.cookies.token; 8 | 9 | 10 | if (req.headers.authorization && req.headers.authorization.startsWith("Bearer")) { 11 | token = req.headers.authorization.split(" ")[1]; 12 | } else if (req.cookies?.token) { 13 | token = req.cookies.token; 14 | } 15 | 16 | if (!token) { 17 | return res.status(401).json({ message: "Unauthorized - Token missing" }); 18 | } 19 | 20 | try { 21 | const decoded = jwt.verify(token, process.env.JWT_SECRET); 22 | req.user = await User.findById(decoded.id); 23 | if (!req.user) { 24 | return res.status(401).json({ message: "User not found" }); 25 | } 26 | next(); 27 | } catch (err) { 28 | console.error("JWT verification failed:", err); 29 | res.status(401).json({ message: "Invalid token" }); 30 | } 31 | }; 32 | 33 | 34 | 35 | // Check if user is Admin 36 | export const isAdmin = (req, res, next) => { 37 | if (req.user.role !== "Admin") { 38 | return res.status(403).json({ message: "Access denied. Admins only." }); 39 | } 40 | next(); 41 | }; 42 | 43 | 44 | // Optional: Check if user is Doctor 45 | export const isDoctor = (req, res, next) => { 46 | if (req.user.role !== "Doctor") { 47 | return res.status(403).json({ message: "Access denied. Doctors only." }); 48 | } 49 | next(); 50 | }; 51 | 52 | // Optional: Check if user is Patient 53 | export const isPatient = (req, res, next) => { 54 | if (req.user.role !== "Patient") { 55 | return res.status(403).json({ message: "Access denied. Patients only." }); 56 | } 57 | next(); 58 | }; 59 | 60 | // Check if user is Premium (e.g., paid member) 61 | export const checkPremium = (req, res, next) => { 62 | if (!req.user?.isPremium) { 63 | return res.status(403).json({ message: "Access denied. Premium members only." }); 64 | } 65 | next(); 66 | }; 67 | -------------------------------------------------------------------------------- /server/controllers/adminController.js: -------------------------------------------------------------------------------- 1 | import bcrypt from "bcryptjs"; 2 | import Admin from "../models/Admin.js"; 3 | 4 | export const addAdmin = async (req, res) => { 5 | try { 6 | const { firstName, lastName, email, phoneNo, city, state, password } = req.body; 7 | 8 | if (!firstName || !lastName || !email || !phoneNo || !city || !state || !password) { 9 | return res.status(400).json({ success: false, message: "All fields are required" }); 10 | } 11 | 12 | const existing = await Admin.findOne({ email }); 13 | if (existing) { 14 | return res.status(409).json({ success: false, message: "Admin already exists" }); 15 | } 16 | 17 | const hashedPassword = await bcrypt.hash(password, 10); 18 | 19 | const newAdmin = new Admin({ 20 | firstName, 21 | lastName, 22 | email, 23 | phoneNo, 24 | city, 25 | state, 26 | password: hashedPassword, 27 | }); 28 | 29 | await newAdmin.save(); 30 | 31 | res.status(201).json({ 32 | success: true, 33 | message: "Admin added successfully", 34 | admin: newAdmin, 35 | }); 36 | } catch (error) { 37 | console.error("❌ Error adding admin:", error); 38 | res.status(500).json({ success: false, message: "Server Error" }); 39 | } 40 | }; 41 | 42 | 43 | export const loginAdmin = async (req, res) => { 44 | try { 45 | const { email, password } = req.body; 46 | 47 | const admin = await Admin.findOne({ email }); 48 | if (!admin) { 49 | return res.status(404).json({ success: false, message: "Admin not found" }); 50 | } 51 | 52 | const isMatch = await bcrypt.compare(password, admin.password); 53 | if (!isMatch) { 54 | return res.status(401).json({ success: false, message: "Invalid credentials" }); 55 | } 56 | 57 | // Optionally: Generate JWT here (if needed) 58 | res.status(200).json({ 59 | success: true, 60 | message: "Admin logged in successfully", 61 | admin: { 62 | id: admin._id, 63 | firstName: admin.firstName, 64 | email: admin.email, 65 | role: admin.role, 66 | }, 67 | }); 68 | } catch (error) { 69 | console.error("❌ Login error:", error); 70 | res.status(500).json({ success: false, message: "Server Error" }); 71 | } 72 | }; 73 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "studynotion-client", 3 | "description": "This is the client side of the StudyNotion App", 4 | "version": "0.1.0", 5 | "private": true, 6 | "dependencies": { 7 | "@ramonak/react-progress-bar": "^5.0.3", 8 | "@reduxjs/toolkit": "^1.9.5", 9 | "@testing-library/jest-dom": "^5.16.5", 10 | "@testing-library/react": "^13.4.0", 11 | "@testing-library/user-event": "^13.5.0", 12 | "axios": "^1.3.5", 13 | "chart.js": "^4.3.0", 14 | "copy-to-clipboard": "^3.3.3", 15 | "cors": "^2.8.5", 16 | "multer": "^2.0.1", 17 | "multer-storage-cloudinary": "^4.0.0", 18 | "nodemon": "^3.1.10", 19 | "openai": "^4.104.0", 20 | "react": "^18.2.0", 21 | "react-chartjs-2": "^5.2.0", 22 | "react-dom": "^18.2.0", 23 | "react-dropzone": "^14.2.3", 24 | "react-hook-form": "^7.43.9", 25 | "react-hot-toast": "^2.4.0", 26 | "react-icons": "^4.8.0", 27 | "react-markdown": "^8.0.7", 28 | "react-otp-input": "^3.0.0", 29 | "react-rating-stars-component": "^2.2.0", 30 | "react-redux": "^8.0.5", 31 | "react-router-dom": "^6.9.0", 32 | "react-scripts": "5.0.1", 33 | "react-super-responsive-table": "^5.2.1", 34 | "react-toastify": "^11.0.5", 35 | "react-type-animation": "^3.0.1", 36 | "showdown": "^2.1.0", 37 | "socket.io": "^4.8.1", 38 | "socket.io-client": "^4.8.1", 39 | "swiper": "^9.3.1", 40 | "video-react": "^0.16.0", 41 | "web-vitals": "^2.1.4" 42 | }, 43 | "scripts": { 44 | "start": "react-scripts start", 45 | "build": "react-scripts build", 46 | "test": "react-scripts test", 47 | "eject": "react-scripts eject", 48 | "server": "cd server && npm run dev", 49 | "dev": "concurrently -n \"client,server\" -c \"bgBlue,bgYellow\" \"npm start\" \"npm run server\"" 50 | }, 51 | "eslintConfig": { 52 | "extends": [ 53 | "react-app", 54 | "react-app/jest" 55 | ] 56 | }, 57 | "browserslist": { 58 | "production": [ 59 | ">0.2%", 60 | "not dead", 61 | "not op_mini all" 62 | ], 63 | "development": [ 64 | "last 1 chrome version", 65 | "last 1 firefox version", 66 | "last 1 safari version" 67 | ] 68 | }, 69 | "devDependencies": { 70 | "@ianvs/prettier-plugin-sort-imports": "^3.7.2", 71 | "concurrently": "^8.2.2", 72 | "prettier": "^2.8.8", 73 | "prettier-plugin-tailwindcss": "^0.3.0", 74 | "tailwindcss": "^3.2.7" 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/pages/DoctorProfile.js: -------------------------------------------------------------------------------- 1 | 2 | import React from "react"; 3 | import { useLocation, useNavigate } from "react-router-dom"; 4 | 5 | const DoctorProfile = () => { 6 | const location = useLocation(); 7 | const navigate = useNavigate(); 8 | const doctor = location.state?.doctor; 9 | 10 | if (!doctor) { 11 | return ( 12 |
13 | Doctor information not available. 14 | 20 |
21 | ); 22 | } 23 | 24 | const getInitials = (firstName, lastName) => { 25 | return ( 26 | (firstName?.charAt(0).toUpperCase() || "") + 27 | (lastName?.charAt(0).toUpperCase() || "") 28 | ); 29 | }; 30 | 31 | const handleBookAppointment = () => { 32 | navigate("/book-appointment", { state: { doctor } }); 33 | }; 34 | 35 | return ( 36 |
37 |
46 |
47 |
48 | {getInitials(doctor.firstName, doctor.lastName)} 49 |
50 | 51 |

52 | {doctor.firstName.charAt(0).toUpperCase() + doctor.firstName.slice(1)}{" "} 53 | {doctor.lastName.charAt(0).toUpperCase() + doctor.lastName.slice(1)} 54 |

55 | 56 |

57 | Email: {doctor.email} 58 |

59 |

60 | Phone: {doctor.phoneNo} 61 |

62 |

63 | City: {doctor.city} 64 |

65 |

66 | State: {doctor.state} 67 |

68 | 69 | {/* 🆕 Book Appointment Button */} 70 | 76 |
77 |
78 |
79 | ); 80 | }; 81 | 82 | export default DoctorProfile; 83 | -------------------------------------------------------------------------------- /server/controllers/paymentController.js: -------------------------------------------------------------------------------- 1 | import Razorpay from "razorpay"; 2 | import crypto from "crypto"; 3 | import User from "../models/userModel.js"; 4 | import { sendThankYouEmail } from "../utils/sendThankYouEmail.js"; 5 | 6 | const instance = new Razorpay({ 7 | key_id: process.env.RAZORPAY_KEY_ID, 8 | key_secret: process.env.RAZORPAY_KEY_SECRET, 9 | }); 10 | 11 | // Create Razorpay Order 12 | export const createOrder = async (req, res) => { 13 | try { 14 | const options = { 15 | amount: 10 * 100, // ₹10 in paise 16 | currency: "INR", 17 | receipt: "receipt_order_" + Date.now(), 18 | }; 19 | 20 | const order = await instance.orders.create(options); 21 | console.log("🔵 Order Created:", order.id); 22 | 23 | return res.status(200).json({ 24 | success: true, 25 | order, 26 | message: "Order created successfully", 27 | }); 28 | } catch (error) { 29 | console.error(" Razorpay Order Error:", error); 30 | return res.status(500).json({ 31 | success: false, 32 | message: "Failed to create Razorpay order", 33 | }); 34 | } 35 | }; 36 | 37 | // Verify Razorpay Payment 38 | export const verifyPayment = async (req, res) => { 39 | try { 40 | const { razorpay_order_id, razorpay_payment_id, razorpay_signature } = req.body; 41 | 42 | // 🛡 Check for authenticated user 43 | if (!req.user || !req.user._id) { 44 | return res.status(401).json({ 45 | success: false, 46 | message: "Unauthorized: User not authenticated", 47 | }); 48 | } 49 | 50 | // Validate Razorpay signature 51 | const expectedSignature = crypto 52 | .createHmac("sha256", process.env.RAZORPAY_KEY_SECRET) 53 | .update(`${razorpay_order_id}|${razorpay_payment_id}`) 54 | .digest("hex"); 55 | 56 | if (expectedSignature !== razorpay_signature) { 57 | return res.status(400).json({ 58 | success: false, 59 | message: "Invalid Razorpay signature", 60 | }); 61 | } 62 | 63 | // Fetch and update user 64 | const user = await User.findById(req.user._id); 65 | if (!user) { 66 | return res.status(404).json({ 67 | success: false, 68 | message: "User not found", 69 | }); 70 | } 71 | 72 | // Update user's premium status only if not already premium 73 | if (!user.isPremium) { 74 | user.isPremium = true; 75 | await user.save(); 76 | 77 | // Send Thank You email 78 | await sendThankYouEmail(user.email); 79 | } 80 | 81 | return res.status(200).json({ 82 | success: true, 83 | message: "Payment verified and premium activated", 84 | }); 85 | } catch (error) { 86 | console.error(" Razorpay Verification Error:", error); 87 | return res.status(500).json({ 88 | success: false, 89 | message: "Payment verification failed", 90 | error: error.message, 91 | }); 92 | } 93 | }; 94 | 95 | -------------------------------------------------------------------------------- /src/components/UploadPatientDetails.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import axios from "axios"; 3 | import toast from "react-hot-toast"; 4 | import { FiUploadCloud } from "react-icons/fi"; 5 | 6 | const UploadPatientDetails = ({ doctorEmail }) => { 7 | const [patientName, setPatientName] = useState(""); 8 | const [patientEmail, setPatientEmail] = useState(""); 9 | const [notes, setNotes] = useState(""); 10 | const [file, setFile] = useState(null); 11 | 12 | const handleUpload = async () => { 13 | if (!patientName || !patientEmail || !notes || !file) { 14 | toast.error("All fields are required"); 15 | return; 16 | } 17 | 18 | const formData = new FormData(); 19 | formData.append("file", file); 20 | formData.append("patientName", patientName); 21 | formData.append("patientEmail", patientEmail); 22 | formData.append("notes", notes); 23 | formData.append("uploadedBy", doctorEmail); 24 | 25 | try { 26 | const res = await axios.post("/api/doctor/upload", formData); 27 | toast.success("Details uploaded successfully"); 28 | // Optionally clear form 29 | setPatientName(""); 30 | setPatientEmail(""); 31 | setNotes(""); 32 | setFile(null); 33 | } catch (error) { 34 | console.error(error); 35 | toast.error("Upload failed"); 36 | } 37 | }; 38 | 39 | return ( 40 |
41 |

Upload Patient Details

42 | 43 | setPatientName(e.target.value)} 48 | className="w-full mb-3 px-3 py-2 border rounded outline-none text-sm" 49 | /> 50 | 51 | setPatientEmail(e.target.value)} 56 | className="w-full mb-3 px-3 py-2 border rounded outline-none text-sm" 57 | /> 58 | 59 |