├── 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 |
126 | {/* ✅ Left - Signup Form */}
127 |
190 |
191 | {/* ✅ Right - Hospital Image with Zoom + Border + Shadow */}
192 |
193 | {/*
*/}
194 |
199 |
204 |
205 |
206 |
207 |
208 | );
209 | };
210 |
211 | export default SignupPage;
212 |
213 |
214 |
--------------------------------------------------------------------------------
/src/components/Footer.js:
--------------------------------------------------------------------------------
1 | // import { FaGithub, FaLinkedin, FaArrowUp, FaEnvelope, FaPhone, FaMapMarkerAlt, FaCode } from "react-icons/fa";
2 | // import { SiLeetcode } from "react-icons/si";
3 |
4 | // const Footer = () => {
5 | // return (
6 | //
7 | //
8 |
9 | // {/* About Us */}
10 | //
11 | //
About DocTreat
12 | //
13 | // DocTreat is a telemedicine platform that helps patients connect with trusted doctors based on their city,
14 | // book appointments, chat in real-time, and manage their health records—all in one place.
15 | //
16 | //
17 |
18 | // {/* Quick Links */}
19 | //
20 | //
Quick Links
21 | //
29 | //
30 |
31 | // {/* Our Mission */}
32 | //
33 | //
Our Mission
34 | //
35 | // We aim to make healthcare more accessible and transparent by simplifying how people discover,
36 | // connect, and consult with medical professionals. No more long queues or uncertainty—just smart, digital healthcare.
37 | //
38 | //
39 |
40 | // {/* Contact & Social */}
41 | //
42 | //
Contact
43 | //
44 | // prakashranjan.pr3636@gmail.com
45 | // +91 9576409209
46 | // Patna, India
47 | //
48 | //
49 | //
50 | //
51 | //
52 | //
53 | //
54 | //
55 |
56 | // {/* Footer Bottom */}
57 | //
63 | //
64 | // );
65 | // };
66 |
67 | // export default Footer;
68 |
69 |
70 |
71 | import { FaGithub, FaLinkedin, FaArrowUp, FaEnvelope, FaPhone, FaMapMarkerAlt, FaCode } from "react-icons/fa";
72 | import { SiLeetcode } from "react-icons/si";
73 |
74 | const Footer = () => {
75 | return (
76 |
77 |
78 |
79 | {/* About Us */}
80 |
81 |
About DocTreat
82 |
83 | DocTreat is a telemedicine platform designed to make healthcare smarter.
84 | Patients can easily find doctors by city , book appointments, chat in real-time, and upload medical records—all in one place.
85 |
86 |
87 |
88 | {/* Quick Links */}
89 |
90 |
Quick Links
91 |
99 |
100 |
101 | {/* Our Mission */}
102 |
103 |
Our Mission
104 |
105 | We want to transform access to healthcare by making it more digital, accessible, and hassle-free.
106 | No more long queues. No more city-wide doctor hunts. Just smart connections and better care .
107 |
108 |
109 |
110 | {/* Contact & Social */}
111 |
112 |
Contact Me
113 |
114 | prakashranjan.pr3636@gmail.com
115 | +91 9576409209
116 | Patna, India
117 |
118 |
123 |
124 |
125 |
126 | {/* Footer Bottom */}
127 |
128 |
129 | © {new Date().getFullYear()} DocTreat | Built with by
130 | Prakash Ranjan
131 |
132 |
133 | Back to Top
134 |
135 |
136 |
137 | );
138 | };
139 |
140 | export default Footer;
141 |
--------------------------------------------------------------------------------
/server/controllers/authController.js:
--------------------------------------------------------------------------------
1 | import bcrypt from "bcryptjs";
2 | import jwt from "jsonwebtoken";
3 | import otpGenerator from "otp-generator";
4 | import nodemailer from "nodemailer";
5 | import User from "../models/userModel.js";
6 | import OTP from "../models/otpModel.js";
7 | import Admin from "../models/Admin.js";
8 |
9 | //==================Send OTP===================//
10 | export const sendOtp = async (req, res) => {
11 | try {
12 | console.log(req.body);
13 | const { email } = req.body;
14 | if (!email)
15 | return res.status(400).json({ success: false, message: "Email is required" });
16 |
17 | const existingUser = await User.findOne({ email });
18 | if (existingUser)
19 | return res.status(400).json({ success: false, message: "User already exists" });
20 |
21 | const otp = otpGenerator.generate(6, {
22 | upperCaseAlphabets: false,
23 | lowerCaseAlphabets: false,
24 | specialChars: false,
25 | });
26 |
27 | await OTP.create({ email, otp, createdAt: new Date() });
28 |
29 | const transporter = nodemailer.createTransport({
30 | service: "gmail",
31 | auth: {
32 | user: process.env.EMAIL_USER,
33 | pass: process.env.EMAIL_PASS,
34 | },
35 | });
36 |
37 | const mailOptions = {
38 | from: `LoGic Legal <${process.env.EMAIL_USER}>`,
39 | to: email,
40 | subject: "Your OTP for LoGic Legal Verification",
41 | text: `Your OTP is: ${otp}. It is valid for 5 minutes.`,
42 | };
43 |
44 | await transporter.sendMail(mailOptions);
45 |
46 | return res.status(200).json({ success: true, message: "OTP sent successfully" });
47 | } catch (error) {
48 | console.error("Send OTP Error:", error);
49 | return res.status(500).json({ success: false, message: "OTP send failed" });
50 | }
51 | };
52 |
53 | //==================Verify OTP=======================//
54 | export const verifyOtp = async (req, res) => {
55 | try {
56 | const { email, otp } = req.body;
57 |
58 | if (!email || !otp) {
59 | return res.status(400).json({ success: false, message: "Email and OTP are required" });
60 | }
61 |
62 | const recentOtp = await OTP.findOne({ email }).sort({ createdAt: -1 });
63 |
64 | if (!recentOtp || recentOtp.otp !== otp) {
65 | return res.status(400).json({ success: false, message: "Invalid or expired OTP" });
66 | }
67 |
68 | return res.status(200).json({ success: true, message: "OTP verified" });
69 | } catch (error) {
70 | console.error("OTP verification error:", error);
71 | return res.status(500).json({ success: false, message: "OTP verification failed" });
72 | }
73 | };
74 |
75 | // ====================Signup====================//
76 | export const signup = async (req, res) => {
77 | try {
78 | const {
79 | firstName,
80 | lastName,
81 | email,
82 | phone,
83 | password,
84 | confirmPassword,
85 | city,
86 | state,
87 | role,
88 | } = req.body;
89 |
90 | if (
91 | !firstName || !lastName || !email || !phone ||
92 | !password || !confirmPassword || !city || !state || !role
93 | ) {
94 | return res.status(400).json({ success: false, message: "All fields are required" });
95 | }
96 |
97 | if (password !== confirmPassword) {
98 | return res.status(400).json({ success: false, message: "Passwords do not match" });
99 | }
100 |
101 | const existingUser = await User.findOne({ email });
102 | if (existingUser) {
103 | return res.status(409).json({ success: false, message: "User already exists" });
104 | }
105 |
106 | const hashedPassword = await bcrypt.hash(password, 12);
107 |
108 | const user = await User.create({
109 | firstName,
110 | lastName,
111 | email,
112 | phone,
113 | password: hashedPassword,
114 | role,
115 | city,
116 | state,
117 | });
118 |
119 | const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, {
120 | expiresIn: "7d",
121 | });
122 |
123 | return res.status(201).json({
124 | success: true,
125 | message: `Account created for ${firstName}`,
126 | user,
127 | token,
128 | });
129 | } catch (error) {
130 | console.error("Signup error:", error.message);
131 | return res.status(500).json({ success: false, message: "Signup failed" });
132 | }
133 | };
134 |
135 |
136 |
137 |
138 |
139 | //============== Login ==================//
140 | export const login = async (req, res) => {
141 | try {
142 | const { email, password } = req.body;
143 |
144 | if (!email || !password) {
145 | return res.status(400).json({
146 | success: false,
147 | message: "Email and Password are required",
148 | });
149 | }
150 |
151 | // Use "email" directly
152 | let user = await User.findOne({
153 | $or: [{ email: email }, { phone: email }],
154 | });
155 |
156 | //==================================================//
157 | let fromModel = "User";
158 | // If not found, try Admin collection
159 | if (!user) {
160 | user = await Admin.findOne({ email: email });
161 | fromModel = "Admin";
162 | }
163 | //===================================================//
164 |
165 |
166 | // Still not found
167 | if (!user) {
168 | return res.status(404).json({
169 | success: false,
170 | message: "User not found",
171 | });
172 | }
173 |
174 | const isMatch = await bcrypt.compare(password, user.password);
175 | if (!isMatch) {
176 | return res.status(401).json({
177 | success: false,
178 | message: "Invalid credentials",
179 | });
180 | }
181 |
182 | const token = jwt.sign(
183 | { id: user._id, role: user.role },
184 | process.env.JWT_SECRET,
185 | {
186 | expiresIn: "7d",
187 | }
188 | );
189 |
190 | res.cookie("token", token, {
191 | httpOnly: true,
192 | secure: false,
193 | sameSite: "lax",
194 | maxAge: 7 * 24 * 60 * 60 * 1000,
195 | });
196 |
197 | return res.status(200).json({
198 | success: true,
199 | message: "Login successful",
200 | token,
201 | user,
202 | });
203 | } catch (error) {
204 | console.error("Login error:", error);
205 | return res.status(500).json({
206 | success: false,
207 | message: "Login failed",
208 | error: error.message // log the exact error for debugging
209 | });
210 | }
211 | };
212 |
213 |
214 |
215 |
216 |
217 |
218 | // ================ Forgot Password ================= //
219 | export const forgotPassword = async (req, res) => {
220 | const { email } = req.body;
221 |
222 | try {
223 | const otp = otpGenerator.generate(6, {
224 | upperCaseAlphabets: false,
225 | lowerCaseAlphabets: false,
226 | specialChars: false,
227 | });
228 |
229 | await OTP.create({ email, otp });
230 |
231 | const transporter = nodemailer.createTransport({
232 | service: "gmail",
233 | auth: {
234 | user: process.env.EMAIL_USER,
235 | pass: process.env.EMAIL_PASS,
236 | },
237 | });
238 |
239 | await transporter.sendMail({
240 | from: `LoGic Legal <${process.env.EMAIL_USER}>`,
241 | to: email,
242 | subject: "OTP for Password Reset",
243 | text: `Your OTP is: ${otp}. Valid for 5 minutes.`,
244 | });
245 |
246 | res.status(200).json({ success: true, message: "OTP sent for password reset" });
247 | } catch (error) {
248 | console.error("Forgot Password error:", error);
249 | res.status(500).json({ success: false, message: "Failed to send OTP" });
250 | }
251 | };
252 |
253 | // ================ Reset Password =================== //
254 | export const resetPassword = async (req, res) => {
255 | const { email, otp, newPassword } = req.body;
256 |
257 | try {
258 | const recentOtp = await OTP.findOne({ email }).sort({ createdAt: -1 }).limit(1);
259 | if (!recentOtp || recentOtp.otp !== otp)
260 | return res.status(400).json({ message: "Invalid or expired OTP" });
261 |
262 | const hashed = await bcrypt.hash(newPassword, 12);
263 | await User.findOneAndUpdate({ email }, { password: hashed });
264 |
265 | res.status(200).json({ success: true, message: "Password reset successful" });
266 | } catch (error) {
267 | console.error("Reset password error:", error);
268 | res.status(500).json({ success: false, message: "Reset failed" });
269 | }
270 | };
271 |
272 | // ================= Update Password ====================== //
273 | export const updatePassword = async (req, res) => {
274 | const { currentPassword, newPassword } = req.body;
275 |
276 | try {
277 | const user = await User.findById(req.user.id);
278 | const match = await bcrypt.compare(currentPassword, user.password);
279 |
280 | if (!match)
281 | return res.status(400).json({ message: "Incorrect current password" });
282 |
283 | user.password = await bcrypt.hash(newPassword, 12);
284 | await user.save();
285 |
286 | res.status(200).json({ message: "Password updated" });
287 | } catch (error) {
288 | res.status(500).json({ message: "Failed to update password" });
289 | }
290 | };
291 |
292 |
293 |
294 |
--------------------------------------------------------------------------------