0% found this document useful (0 votes)
19 views11 pages

Doctor Booking System (27 Files Merged) (4 Files Merged)

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
19 views11 pages

Doctor Booking System (27 Files Merged) (4 Files Merged)

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 11

# Logs CURRENCY = "INR" import React from 'react' import React, { useContext, useState } from 'react'

logs JWT_SECRET="greatstack" import { assets } from '../assets/assets' import { assets } from '../assets/assets'
*.log import { NavLink, useNavigate } from 'react-router-dom'
npm-debug.log* # Admin Panel Credentials const Header = () => { import { AppContext } from '../context/AppContext'
yarn-debug.log* ADMIN_EMAIL = "[email protected]" return (
yarn-error.log* ADMIN_PASSWORD = "greatstack123" <div className='flex flex-col md:flex-row flex-wrap bg-primary const Navbar = () => {
pnpm-debug.log* rounded-lg px-6 md:px-10 lg:px-20 '>
lerna-debug.log* # MongoDB Setup ( required ) const navigate = useNavigate()
MONGODB_URI = {/* --------- Header Left --------- */}
node_modules "mongodb+srv://deepakhannyson:[email protected]/?retr <div className='md:w-1/2 flex flex-col items-start justify- const [showMenu, setShowMenu] = useState(false)
dist yWrites=true&w=majority&appName=Cluster0" center gap-4 py-10 m-auto md:py-[10vw] md:mb-[-30px]'> const { token, setToken, userData } = useContext(AppContext)
dist-ssr <p className='text-3xl md:text-4xl lg:text-5xl text-white
*.local # Cloudinary Setup ( required ) font-semibold leading-tight md:leading-tight lg:leading-tight'> const logout = () => {
CLOUDINARY_NAME = dfzvtwurw Book Appointment <br /> With Trusted Doctors localStorage.removeItem('token')
# Editor directories and files CLOUDINARY_API_KEY =149617587369565 </p> setToken(false)
.vscode/* CLOUDINARY_SECRET_KEY =bx6_MPviZCGsImabi5Uwy0NAdmo <div className='flex flex-col md:flex-row items-center navigate('/login')
!.vscode/extensions.json # Razorpay Payment Integration gap-3 text-white text-sm font-light'> }
.idea RAZORPAY_KEY_ID = "------ Razorpay Key Id here ------" <img className='w-28' src={assets.group_profiles}
.DS_Store RAZORPAY_KEY_SECRET = "------ Razorpay Key Secret here ------" alt="" /> return (
*.suo <p>Simply browse through our extensive list of <div className='flex items-center justify-between text-sm py-4 mb-5
*.ntvs* # Stripe Payment Integration trusted doctors, <br className='hidden sm:block' /> schedule your border-b border-b-[#ADADAD]'>
*.njsproj STRIPE_SECRET_KEY="------ Stripe Secret Key here ------" appointment hassle-free.</p> <img onClick={() => navigate('/')} className='w-44 cursor-pointer'
*.sln </div> src={assets.logo} alt="" />
*.sw? <a href='#speciality' className='flex items-center gap-2 <ul className='md:flex items-start gap-5 font-medium hidden'>
bg-white px-8 py-3 rounded-full text-[#595959] text-sm m-auto md:m-0 <NavLink to='/' >
hover:scale-105 transition-all duration-300'> <li className='py-1'>HOME</li>
Book appointment <img className='w-3' <hr className='border-none outline-none h-0.5 bg-primary w-3/5
src={assets.arrow_icon} alt="" /> m-auto hidden' />
</a> </NavLink>
</div> <NavLink to='/doctors' >
<li className='py-1'>ALL DOCTORS</li>
{/* --------- Header Right --------- */} <hr className='border-none outline-none h-0.5 bg-primary w-3/5
<div className='md:w-1/2 relative'> m-auto hidden' />
<img className='w-full md:absolute bottom-0 h-auto </NavLink>
rounded-lg' src={assets.header_img} alt="" /> <NavLink to='/about' >
</div> <li className='py-1'>ABOUT</li>
</div> <hr className='border-none outline-none h-0.5 bg-primary w-3/5
) m-auto hidden' />
} </NavLink>
<NavLink to='/contact' >
export default Header <li className='py-1'>CONTACT</li>
<hr className='border-none outline-none h-0.5 bg-primary w-3/5
m-auto hidden' />
</NavLink>
</ul>

<div className='flex items-center gap-4 '>


{
token && userData
? <div className='flex items-center gap-2 cursor-pointer
group relative'>
<img className='w-8 rounded-full' src={userData.image}
alt="" />
<img className='w-2.5' src={assets.dropdown_icon} alt="" />
<div className='absolute top-0 right-0 pt-14 text-base
font-medium text-gray-600 z-20 hidden group-hover:block'>
<div className='min-w-48 bg-gray-50 rounded flex flex-col
gap-4 p-4'>
<p onClick={() => navigate('/my-profile')}
className='hover:text-black cursor-pointer'>My Profile</p>

import React from 'react' import React from 'react' <p onClick={() => navigate('/my-appointments')} import React, { useContext, useEffect, useState } from 'react'
import { assets } from '../assets/assets' import { assets } from '../assets/assets' className='hover:text-black cursor-pointer'>My Appointments</p> import { useNavigate } from 'react-router-dom'
import { useNavigate } from 'react-router-dom' <p onClick={logout} className='hover:text-black cursor- import { AppContext } from '../context/AppContext'
const Footer = () => { pointer'>Logout</p> const RelatedDoctors = ({ speciality, docId }) => {
const Banner = () => { return ( </div>
<div className='md:mx-10'> </div> const navigate = useNavigate()
const navigate = useNavigate() <div className='flex flex-col sm:grid grid-cols-[3fr_1fr_1fr] gap- </div> const { doctors } = useContext(AppContext)
14 my-10 mt-40 text-sm'> : <button onClick={() => navigate('/login')} className='bg-
return ( primary text-white px-8 py-3 rounded-full font-light hidden const [relDoc, setRelDoc] = useState([])
<div className='flex bg-primary rounded-lg px-6 sm:px-10 md:px- <div> md:block'>Create account</button>
14 lg:px-12 my-20 md:mx-10'> <img className='mb-5 w-40' src={assets.logo} alt="" /> } useEffect(() => {
<p className='w-full md:w-2/3 text-gray-600 leading-6'>Lorem <img onClick={() => setShowMenu(true)} className='w-6 md:hidden' if (doctors.length > 0 && speciality) {
{/* ------- Left Side ------- */} Ipsum is simply dummy text of the printing and typesetting industry. src={assets.menu_icon} alt="" /> const doctorsData = doctors.filter((doc) => doc.speciality
<div className='flex-1 py-8 sm:py-10 md:py-16 lg:py-24 lg:pl- Lorem Ipsum has been the industry's standard dummy text ever since the === speciality && doc._id !== docId)
5'> 1500s, when an unknown printer took a galley of type and scrambled it to {/* ---- Mobile Menu ---- */} setRelDoc(doctorsData)
<div className='text-xl sm:text-2xl md:text-3xl lg:text- make a type specimen book.</p> <div className={`md:hidden ${showMenu ? 'fixed w-full' : 'h-0 w- }
5xl font-semibold text-white'> </div> 0'} right-0 top-0 bottom-0 z-20 overflow-hidden bg-white transition- }, [doctors, speciality, docId])
<p>Book Appointment</p> all`}>
<p className='mt-4'>With 100+ Trusted Doctors</p> <div> <div className='flex items-center justify-between px-5 py-6'> return (
</div> <p className='text-xl font-medium mb-5'>COMPANY</p> <img src={assets.logo} className='w-36' alt="" /> <div className='flex flex-col items-center gap-4 my-16 text-
<button onClick={() => { navigate('/login'); scrollTo(0, <ul className='flex flex-col gap-2 text-gray-600'> <img onClick={() => setShowMenu(false)} [#262626]'>
0) }} className='bg-white text-sm sm:text-base text-[#595959] px-8 py-3 <li>Home</li> src={assets.cross_icon} className='w-7' alt="" /> <h1 className='text-3xl font-medium'>Related Doctors</h1>
rounded-full mt-6 hover:scale-105 transition-all '>Create <li>About us</li> </div> <p className='sm:w-1/3 text-center text-sm'>Simply browse
account</button> <li>Delivery</li> <ul className='flex flex-col items-center gap-2 mt-5 px-5 text- through our extensive list of trusted doctors.</p>
</div> <li>Privacy policy</li> lg font-medium'> <div className='w-full grid grid-cols-auto gap-4 pt-5 gap-y-6
</ul> <NavLink onClick={() => setShowMenu(false)} to='/'><p px-3 sm:px-0'>
{/* ------- Right Side ------- */} </div> className='px-4 py-2 rounded full inline-block'>HOME</p></NavLink> {relDoc.map((item, index) => (
<div className='hidden md:block md:w-1/2 lg:w-[370px] <NavLink onClick={() => setShowMenu(false)} to='/doctors' ><p <div onClick={() => {
relative'> <div> className='px-4 py-2 rounded full inline-block'>ALL DOCTORS</p></NavLink> navigate(`/appointment/${item._id}`); scrollTo(0, 0) }} className='border
<img className='w-full absolute bottom-0 right-0 max-w- <p className='text-xl font-medium mb-5'>GET IN TOUCH</p> <NavLink onClick={() => setShowMenu(false)} to='/about' ><p border-[#C9D8FF] rounded-xl overflow-hidden cursor-pointer
md' src={assets.appointment_img} alt="" /> <ul className='flex flex-col gap-2 text-gray-600'> className='px-4 py-2 rounded full inline-block'>ABOUT</p></NavLink> hover:translate-y-[-10px] transition-all duration-500' key={index}>
</div> <li>212-456-7890</li> <NavLink onClick={() => setShowMenu(false)} to='/contact' ><p <img className='bg-[#EAEFFF]' src={item.image}
</div> <li>[email protected]</li> className='px-4 py-2 rounded full inline-block'>CONTACT</p></NavLink> alt="" />
) </ul> </ul> <div className='p-4'>
} </div> </div> <div className={`flex items-center gap-2
</div> text-sm text-center ${item.available ? 'text-green-500' : "text-gray-
export default Banner </div> </div> 500"}`}>
) <p className={`w-2 h-2 rounded-full
<div> } ${item.available ? 'bg-green-500' : "bg-gray-
<hr /> 500"}`}></p><p>{item.available ? 'Available' : "Not Available"}</p>
<p className='py-5 text-sm text-center'>Copyright 2024 @ export default Navbar </div>
Prescripto.com - All Right Reserved.</p> <p className='text-[#262626] text-lg font-
</div> medium'>{item.name}</p>
<p className='text-[#5C5C5C] text-
</div> sm'>{item.speciality}</p>
) </div>
} </div>
))}
export default Footer </div>
{/* <button className='bg-[#EAEFFF] text-gray-600 px-12 py-3
rounded-full mt-10'>more</button> */}
</div>
)
}

export default RelatedDoctors

import React, { useContext, useEffect, useState } from 'react' import React from 'react' import React from 'react'
import { useNavigate } from 'react-router-dom' import { specialityData } from '../assets/assets' useEffect(() => { import { assets } from '../assets/assets'
import { AppContext } from '../context/AppContext' import { Link } from 'react-router-dom' if (token) {
const RelatedDoctors = ({ speciality, docId }) => { loadUserProfileData() const About = () => {
const SpecialityMenu = () => { } return (
const navigate = useNavigate() return ( }, [token]) <div>
const { doctors } = useContext(AppContext) <div id='speciality' className='flex flex-col items-center gap-4
py-16 text-[#262626]'> const value = { <div className='text-center text-2xl pt-10 text-[#707070]'>
const [relDoc, setRelDoc] = useState([]) <h1 className='text-3xl font-medium'>Find by Speciality</h1> doctors, getDoctosData, <p>ABOUT <span className='text-gray-700 font-
<p className='sm:w-1/3 text-center text-sm'>Simply browse currencySymbol, semibold'>US</span></p>
useEffect(() => { through our extensive list of trusted doctors, schedule your appointment backendUrl, </div>
if (doctors.length > 0 && speciality) { hassle-free.</p> token, setToken,
const doctorsData = doctors.filter((doc) => doc.speciality <div className='flex sm:justify-center gap-4 pt-5 w-full userData, setUserData, loadUserProfileData <div className='my-10 flex flex-col md:flex-row gap-12'>
=== speciality && doc._id !== docId) overflow-scroll '> } <img className='w-full md:max-w-[360px]' src={assets.about_image}
setRelDoc(doctorsData) {specialityData.map((item, index) => ( alt="" />
} <Link to={`/doctors/${item.speciality}`} onClick={() return ( <div className='flex flex-col justify-center gap-6 md:w-2/4 text-
}, [doctors, speciality, docId]) => scrollTo(0, 0)} className='flex flex-col items-center text-xs cursor- <AppContext.Provider value={value}> sm text-gray-600'>
pointer flex-shrink-0 hover:translate-y-[-10px] transition-all duration- {props.children} <p>Welcome to Prescripto, your trusted partner in managing your
return ( 500' key={index}> </AppContext.Provider> healthcare needs conveniently and efficiently. At Prescripto, we
<div className='flex flex-col items-center gap-4 my-16 text- <img className='w-16 sm:w-24 mb-2 ' ) understand the challenges individuals face when it comes to scheduling
[#262626]'> src={item.image} alt="" /> doctor appointments and managing their health records.</p>
<h1 className='text-3xl font-medium'>Related Doctors</h1> <p>{item.speciality}</p> } <p>Prescripto is committed to excellence in healthcare
<p className='sm:w-1/3 text-center text-sm'>Simply browse </Link> technology. We continuously strive to enhance our platform, integrating
through our extensive list of trusted doctors.</p> ))} export default AppContextProvider the latest advancements to improve user experience and deliver superior
<div className='w-full grid grid-cols-auto gap-4 pt-5 gap-y-6 </div> service. Whether you're booking your first appointment or managing
px-3 sm:px-0'> </div> ongoing care, Prescripto is here to support you every step of the
{relDoc.map((item, index) => ( ) way.</p>
<div onClick={() => { } <b className='text-gray-800'>Our Vision</b>
navigate(`/appointment/${item._id}`); scrollTo(0, 0) }} className='border <p>Our vision at Prescripto is to create a seamless healthcare
border-[#C9D8FF] rounded-xl overflow-hidden cursor-pointer export default SpecialityMenu experience for every user. We aim to bridge the gap between patients and
hover:translate-y-[-10px] transition-all duration-500' key={index}> healthcare providers, making it easier for you to access the care you
<img className='bg-[#EAEFFF]' src={item.image} need, when you need it.</p>
alt="" /> </div>
<div className='p-4'> </div>
<div className={`flex items-center gap-2
text-sm text-center ${item.available ? 'text-green-500' : "text-gray- <div className='text-xl my-4'>
500"}`}> <p>WHY <span className='text-gray-700 font-semibold'>CHOOSE
<p className={`w-2 h-2 rounded-full US</span></p>
${item.available ? 'bg-green-500' : "bg-gray- </div>
500"}`}></p><p>{item.available ? 'Available' : "Not Available"}</p>
</div> <div className='flex flex-col md:flex-row mb-20'>
<p className='text-[#262626] text-lg font- <div className='border px-10 md:px-16 py-8 sm:py-16 flex flex-col
medium'>{item.name}</p> gap-5 text-[15px] hover:bg-primary hover:text-white transition-all
<p className='text-[#5C5C5C] text- duration-300 text-gray-600 cursor-pointer'>
sm'>{item.speciality}</p> <b>EFFICIENCY:</b>
</div> <p>Streamlined appointment scheduling that fits into your busy
</div> lifestyle.</p>
))} </div>
</div> <div className='border px-10 md:px-16 py-8 sm:py-16 flex flex-col
{/* <button className='bg-[#EAEFFF] text-gray-600 px-12 py-3 gap-5 text-[15px] hover:bg-primary hover:text-white transition-all
rounded-full mt-10'>more</button> */} duration-300 text-gray-600 cursor-pointer'>
</div> <b>CONVENIENCE: </b>
) <p>Access to a network of trusted healthcare professionals in
} your area.</p>
</div>
export default RelatedDoctors <div className='border px-10 md:px-16 py-8 sm:py-16 flex flex-col
gap-5 text-[15px] hover:bg-primary hover:text-white transition-all
duration-300 text-gray-600 cursor-pointer'>
<b>PERSONALIZATION:</b>

import React, { useContext } from 'react' import { createContext, useEffect, useState } from "react"; <p >Tailored recommendations and reminders to help you stay on import React, { useContext, useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom' import { toast } from "react-toastify"; top of your health.</p> import { useNavigate, useParams } from 'react-router-dom'
import { AppContext } from '../context/AppContext' import axios from 'axios' </div> import { AppContext } from '../context/AppContext'
const TopDoctors = () => { </div> import { assets } from '../assets/assets'
export const AppContext = createContext() import RelatedDoctors from '../components/RelatedDoctors'
const navigate = useNavigate() </div> import axios from 'axios'
const AppContextProvider = (props) => { ) import { toast } from 'react-toastify'
const { doctors } = useContext(AppContext) }
const currencySymbol = '₹' const Appointment = () => {
return ( const backendUrl = import.meta.env.VITE_BACKEND_URL export default About
<div className='flex flex-col items-center gap-4 my-16 text- const { docId } = useParams()
[#262626] md:mx-10'> const [doctors, setDoctors] = useState([]) const { doctors, currencySymbol, backendUrl, token, getDoctosData } =
<h1 className='text-3xl font-medium'>Top Doctors to Book</h1> const [token, setToken] = useState(localStorage.getItem('token') ? useContext(AppContext)
<p className='sm:w-1/3 text-center text-sm'>Simply browse localStorage.getItem('token') : '') const daysOfWeek = ['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT']
through our extensive list of trusted doctors.</p> const [userData, setUserData] = useState(false)
<div className='w-full grid grid-cols-auto gap-4 pt-5 gap-y-6 const [docInfo, setDocInfo] = useState(false)
px-3 sm:px-0'> // Getting Doctors using API const [docSlots, setDocSlots] = useState([])
{doctors.slice(0, 10).map((item, index) => ( const getDoctosData = async () => { const [slotIndex, setSlotIndex] = useState(0)
<div onClick={() => { const [slotTime, setSlotTime] = useState('')
navigate(`/appointment/${item._id}`); scrollTo(0, 0) }} className='border try {
border-[#C9D8FF] rounded-xl overflow-hidden cursor-pointer const navigate = useNavigate()
hover:translate-y-[-10px] transition-all duration-500' key={index}> const { data } = await axios.get(backendUrl +
<img className='bg-[#EAEFFF]' src={item.image} '/api/doctor/list') const fetchDocInfo = async () => {
alt="" /> if (data.success) { const docInfo = doctors.find((doc) => doc._id === docId)
<div className='p-4'> setDoctors(data.doctors) setDocInfo(docInfo)
<div className={`flex items-center gap-2 } else { }
text-sm text-center ${item.available ? 'text-green-500' : "text-gray- toast.error(data.message)
500"}`}> } const getAvailableSolts = async () => {
<p className={`w-2 h-2 rounded-full
${item.available ? 'bg-green-500' : "bg-gray- } catch (error) { setDocSlots([])
500"}`}></p><p>{item.available ? 'Available' : "Not Available"}</p> console.log(error)
</div> toast.error(error.message) // getting current date
<p className='text-[#262626] text-lg font- } let today = new Date()
medium'>{item.name}</p>
<p className='text-[#5C5C5C] text- } for (let i = 0; i < 7; i++) {
sm'>{item.speciality}</p>
</div> // Getting User Profile using API // getting date with index
</div> const loadUserProfileData = async () => { let currentDate = new Date(today)
))} currentDate.setDate(today.getDate() + i)
</div> try {
<button onClick={() => { navigate('/doctors'); scrollTo(0, 0) // setting end time of the date with index
}} className='bg-[#EAEFFF] text-gray-600 px-12 py-3 rounded-full mt- const { data } = await axios.get(backendUrl + '/api/user/get- let endTime = new Date()
10'>more</button> profile', { headers: { token } }) endTime.setDate(today.getDate() + i)
</div> endTime.setHours(21, 0, 0, 0)
if (data.success) {
) setUserData(data.userData) // setting hours
} } else { if (today.getDate() === currentDate.getDate()) {
toast.error(data.message) currentDate.setHours(currentDate.getHours() > 10 ?
export default TopDoctors } currentDate.getHours() + 1 : 10)
currentDate.setMinutes(currentDate.getMinutes() > 30 ? 30
} catch (error) { : 0)
console.log(error) } else {
toast.error(error.message) currentDate.setHours(10)
} currentDate.setMinutes(0)
}
}
let timeSlots = [];
useEffect(() => {
getDoctosData()
}, []) while (currentDate < endTime) {
let formattedTime = currentDate.toLocaleTimeString([], { } catch (error) { import React, { useContext, useEffect, useState } from 'react' transition-all cursor-pointer ${speciality === 'Neurologist' ? 'bg-
hour: '2-digit', minute: '2-digit' }); console.log(error) import { AppContext } from '../context/AppContext' [#E2E5FF] text-black ' : ''}`}>Neurologist</p>
toast.error(error.message) import { useNavigate, useParams } from 'react-router-dom' <p onClick={() => speciality === 'Gastroenterologist' ?
let day = currentDate.getDate() } navigate('/doctors') : navigate('/doctors/Gastroenterologist')}
let month = currentDate.getMonth() + 1 const Doctors = () => { className={`w-[94vw] sm:w-auto pl-3 py-1.5 pr-16 border border-gray-300
let year = currentDate.getFullYear() } rounded transition-all cursor-pointer ${speciality ===
const { speciality } = useParams() 'Gastroenterologist' ? 'bg-[#E2E5FF] text-black ' :
const slotDate = day + "_" + month + "_" + year useEffect(() => { ''}`}>Gastroenterologist</p>
const slotTime = formattedTime if (doctors.length > 0) { const [filterDoc, setFilterDoc] = useState([]) </div>
fetchDocInfo() const [showFilter, setShowFilter] = useState(false) <div className='w-full grid grid-cols-auto gap-4 gap-y-6'>
const isSlotAvailable = docInfo.slots_booked[slotDate] && } const navigate = useNavigate(); {filterDoc.map((item, index) => (
docInfo.slots_booked[slotDate].includes(slotTime) ? false : true }, [doctors, docId]) <div onClick={() => { navigate(`/appointment/${item._id}`);
const { doctors } = useContext(AppContext) scrollTo(0, 0) }} className='border border-[#C9D8FF] rounded-xl overflow-
if (isSlotAvailable) { useEffect(() => { hidden cursor-pointer hover:translate-y-[-10px] transition-all duration-
if (docInfo) { const applyFilter = () => { 500' key={index}>
// Add slot to array getAvailableSolts() if (speciality) { <img className='bg-[#EAEFFF]' src={item.image} alt="" />
timeSlots.push({ } setFilterDoc(doctors.filter(doc => doc.speciality === speciality)) <div className='p-4'>
datetime: new Date(currentDate), }, [docInfo]) } else { <div className={`flex items-center gap-2 text-sm text-
time: formattedTime setFilterDoc(doctors) center ${item.available ? 'text-green-500' : "text-gray-500"}`}>
}) return docInfo ? ( } <p className={`w-2 h-2 rounded-full ${item.available ?
} <div> } 'bg-green-500' : "bg-gray-500"}`}></p><p>{item.available ? 'Available' :
"Not Available"}</p>
// Increment current time by 30 minutes {/* ---------- Doctor Details ----------- */} useEffect(() => { </div>
currentDate.setMinutes(currentDate.getMinutes() + 30); <div className='flex flex-col sm:flex-row gap-4'> applyFilter() <p className='text-[#262626] text-lg font-
} <div> }, [doctors, speciality]) medium'>{item.name}</p>
<img className='bg-primary w-full sm:max-w-72 <p className='text-[#5C5C5C] text-
setDocSlots(prev => ([...prev, timeSlots])) rounded-lg' src={docInfo.image} alt="" /> return ( sm'>{item.speciality}</p>
</div> <div> </div>
} <p className='text-gray-600'>Browse through the doctors </div>
<div className='flex-1 border border-[#ADADAD] rounded-lg specialist.</p> ))}
} p-8 py-7 bg-white mx-2 sm:mx-0 mt-[-80px] sm:mt-0'> <div className='flex flex-col sm:flex-row items-start gap-5 mt-5'> </div>
<button onClick={() => setShowFilter(!showFilter)} </div>
const bookAppointment = async () => { {/* ----- Doc Info : name, degree, experience ----- className={`py-1 px-3 border rounded text-sm transition-all sm:hidden </div>
*/} ${showFilter ? 'bg-primary text-white' : ''}`}>Filters</button> )
if (!token) { <div className={`flex-col gap-4 text-sm text-gray-600 }
toast.warning('Login to book appointment') <p className='flex items-center gap-2 text-3xl font- ${showFilter ? 'flex' : 'hidden sm:flex'}`}>
return navigate('/login') medium text-gray-700'>{docInfo.name} <img className='w-5' <p onClick={() => speciality === 'General physician' ? export default Doctors
} src={assets.verified_icon} alt="" /></p> navigate('/doctors') : navigate('/doctors/General physician')}
<div className='flex items-center gap-2 mt-1 text- className={`w-[94vw] sm:w-auto pl-3 py-1.5 pr-16 border border-gray-300
const date = docSlots[slotIndex][0].datetime gray-600'> rounded transition-all cursor-pointer ${speciality === 'General
<p>{docInfo.degree} - {docInfo.speciality}</p> physician' ? 'bg-[#E2E5FF] text-black ' : ''}`}>General physician</p>
let day = date.getDate() <button className='py-0.5 px-2 border text-xs <p onClick={() => speciality === 'Gynecologist' ?
let month = date.getMonth() + 1 rounded-full'>{docInfo.experience}</button> navigate('/doctors') : navigate('/doctors/Gynecologist')} className={`w-
let year = date.getFullYear() </div> [94vw] sm:w-auto pl-3 py-1.5 pr-16 border border-gray-300 rounded
transition-all cursor-pointer ${speciality === 'Gynecologist' ? 'bg-
const slotDate = day + "_" + month + "_" + year {/* ----- Doc About ----- */} [#E2E5FF] text-black ' : ''}`}>Gynecologist</p>
<div> <p onClick={() => speciality === 'Dermatologist' ?
try { <p className='flex items-center gap-1 text-sm navigate('/doctors') : navigate('/doctors/Dermatologist')} className={`w-
font-medium text-[#262626] mt-3'>About <img className='w-3' [94vw] sm:w-auto pl-3 py-1.5 pr-16 border border-gray-300 rounded
const { data } = await axios.post(backendUrl + src={assets.info_icon} alt="" /></p> transition-all cursor-pointer ${speciality === 'Dermatologist' ? 'bg-
'/api/user/book-appointment', { docId, slotDate, slotTime }, { headers: { <p className='text-sm text-gray-600 max-w-[700px] [#E2E5FF] text-black ' : ''}`}>Dermatologist</p>
token } }) mt-1'>{docInfo.about}</p> <p onClick={() => speciality === 'Pediatricians' ?
if (data.success) { </div> navigate('/doctors') : navigate('/doctors/Pediatricians')} className={`w-
toast.success(data.message) [94vw] sm:w-auto pl-3 py-1.5 pr-16 border border-gray-300 rounded
getDoctosData() <p className='text-gray-600 font-medium mt- transition-all cursor-pointer ${speciality === 'Pediatricians' ? 'bg-
navigate('/my-appointments') 4'>Appointment fee: <span className='text-gray- [#E2E5FF] text-black ' : ''}`}>Pediatricians</p>
} else { 800'>{currencySymbol}{docInfo.fees}</span> </p> <p onClick={() => speciality === 'Neurologist' ?
toast.error(data.message) </div> navigate('/doctors') : navigate('/doctors/Neurologist')} className={`w-
} </div> [94vw] sm:w-auto pl-3 py-1.5 pr-16 border border-gray-300 rounded

{/* Booking slots */} import React from 'react' import React from 'react' import React, { useContext, useEffect, useState } from 'react'
<div className='sm:ml-72 sm:pl-4 mt-8 font-medium text- import { assets } from '../assets/assets' import Header from '../components/Header' import { AppContext } from '../context/AppContext'
[#565656]'> import SpecialityMenu from '../components/SpecialityMenu' import axios from 'axios'
<p >Booking slots</p> const Contact = () => { import TopDoctors from '../components/TopDoctors' import { toast } from 'react-toastify'
<div className='flex gap-3 items-center w-full overflow- return ( import Banner from '../components/Banner' import { useNavigate } from 'react-router-dom'
x-scroll mt-4'> <div>
{docSlots.length && docSlots.map((item, index) => ( const Home = () => { const Login = () => {
<div onClick={() => setSlotIndex(index)} <div className='text-center text-2xl pt-10 text-[#707070]'> return (
key={index} className={`text-center py-6 min-w-16 rounded-full cursor- <p>CONTACT <span className='text-gray-700 font- <div> const [state, setState] = useState('Sign Up')
pointer ${slotIndex === index ? 'bg-primary text-white' : 'border border- semibold'>US</span></p> <Header />
[#DDDDDD]'}`}> </div> <SpecialityMenu /> const [name, setName] = useState('')
<p>{item[0] && <TopDoctors /> const [email, setEmail] = useState('')
daysOfWeek[item[0].datetime.getDay()]}</p> <div className='my-10 flex flex-col justify-center md:flex-row gap- <Banner /> const [password, setPassword] = useState('')
<p>{item[0] && 10 mb-28 text-sm'> </div>
item[0].datetime.getDate()}</p> <img className='w-full md:max-w-[360px]' ) const navigate = useNavigate()
</div> src={assets.contact_image} alt="" /> } const { backendUrl, token, setToken } = useContext(AppContext)
))} <div className='flex flex-col justify-center items-start gap-6'>
</div> <p className=' font-semibold text-lg text-gray-600'>OUR export default Home const onSubmitHandler = async (event) => {
OFFICE</p> event.preventDefault();
<div className='flex items-center gap-3 w-full overflow- <p className=' text-gray-500'>54709 Willms Station <br /> Suite
x-scroll mt-4'> 350, Washington, USA</p> if (state === 'Sign Up') {
{docSlots.length && docSlots[slotIndex].map((item, <p className=' text-gray-500'>Tel: (415) 555-0132 <br /> Email:
index) => ( [email protected]</p> const { data } = await axios.post(backendUrl +
<p onClick={() => setSlotTime(item.time)} <p className=' font-semibold text-lg text-gray-600'>CAREERS AT '/api/user/register', { name, email, password })
key={index} className={`text-sm font-light flex-shrink-0 px-5 py-2 PRESCRIPTO</p>
rounded-full cursor-pointer ${item.time === slotTime ? 'bg-primary text- <p className=' text-gray-500'>Learn more about our teams and if (data.success) {
white' : 'text-[#949494] border border- job openings.</p> localStorage.setItem('token', data.token)
[#B4B4B4]'}`}>{item.time.toLowerCase()}</p> <button className='border border-black px-8 py-4 text-sm setToken(data.token)
))} hover:bg-black hover:text-white transition-all duration-500'>Explore } else {
</div> Jobs</button> toast.error(data.message)
</div> }
<button onClick={bookAppointment} className='bg-primary </div>
text-white text-sm font-light px-20 py-3 rounded-full my-6'>Book an } else {
appointment</button> </div>
</div> ) const { data } = await axios.post(backendUrl + '/api/user/login', {
} email, password })
{/* Listing Releated Doctors */}
<RelatedDoctors speciality={docInfo.speciality} docId={docId} export default Contact if (data.success) {
/> localStorage.setItem('token', data.token)
</div> setToken(data.token)
) : null } else {
} toast.error(data.message)
}
export default Appointment
}

useEffect(() => {
if (token) {
navigate('/')
}
}, [token])

return (
<form onSubmit={onSubmitHandler} className='min-h-[80vh] flex items-
center'>
<div className='flex flex-col gap-3 m-auto items-start p-8 min-w-
[340px] sm:min-w-96 border rounded-xl text-[#5E5E5E] text-sm shadow-lg'>

<p className='text-2xl font-semibold'>{state === 'Sign Up' ? import React, { useContext, useEffect, useState } from 'react' {!item.cancelled && item.payment && import React, { useContext, useEffect, useState } from 'react'
'Create Account' : 'Login'}</p> import { useNavigate } from 'react-router-dom' !item.isCompleted && <button className='sm:min-w-48 py-2 border rounded import { AppContext } from '../context/AppContext'
<p>Please {state === 'Sign Up' ? 'sign up' : 'log in'} to book import { AppContext } from '../context/AppContext' text-[#696969] bg-[#EAEFFF]'>Paid</button>} import axios from 'axios'
appointment</p> import axios from 'axios' import { toast } from 'react-toastify'
{state === 'Sign Up' import { toast } from 'react-toastify' {item.isCompleted && <button import { assets } from '../assets/assets'
? <div className='w-full '> import { assets } from '../assets/assets' className='sm:min-w-48 py-2 border border-green-500 rounded text-green-
<p>Full Name</p> 500'>Completed</button>} const MyProfile = () => {
<input onChange={(e) => setName(e.target.value)} value={name} const MyAppointments = () => {
className='border border-[#DADADA] rounded w-full p-2 mt-1' type="text" {!item.cancelled && !item.isCompleted && const [isEdit, setIsEdit] = useState(false)
required /> const { backendUrl, token } = useContext(AppContext) <button onClick={() => cancelAppointment(item._id)} className='text-
</div> const navigate = useNavigate() [#696969] sm:min-w-48 py-2 border rounded hover:bg-red-600 hover:text- const [image, setImage] = useState(false)
: null white transition-all duration-300'>Cancel appointment</button>}
} const [appointments, setAppointments] = useState([]) {item.cancelled && !item.isCompleted && const { token, backendUrl, userData, setUserData, loadUserProfileData
<div className='w-full '> const [payment, setPayment] = useState('') <button className='sm:min-w-48 py-2 border border-red-500 rounded text- } = useContext(AppContext)
<p>Email</p> red-500'>Appointment cancelled</button>}
<input onChange={(e) => setEmail(e.target.value)} value={email} const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", </div> // Function to update user profile data using API
className='border border-[#DADADA] rounded w-full p-2 mt-1' type="email" "Aug", "Sep", "Oct", "Nov", "Dec"]; </div> const updateUserProfileData = async () => {
required /> ))}
</div> // Function to format the date eg. ( 20_01_2000 => 20 Jan 2000 ) </div> try {
<div className='w-full '> const slotDateFormat = (slotDate) => { </div>
<p>Password</p> const dateArray = slotDate.split('_') ) const formData = new FormData();
<input onChange={(e) => setPassword(e.target.value)} return dateArray[0] + " " + months[Number(dateArray[1])] + " " + }
value={password} className='border border-[#DADADA] rounded w-full p-2 dateArray[2] formData.append('name', userData.name)
mt-1' type="password" required /> } export default MyAppointments formData.append('phone', userData.phone)
</div> formData.append('address', JSON.stringify(userData.address))
<button className='bg-primary text-white w-full py-2 my-2 // Getting User Appointments Data Using API formData.append('gender', userData.gender)
rounded-md text-base'>{state === 'Sign Up' ? 'Create account' : const getUserAppointments = async () => { formData.append('dob', userData.dob)
'Login'}</button> try {
{state === 'Sign Up' image && formData.append('image', image)
? <p>Already have an account? <span onClick={() => const { data } = await axios.get(backendUrl +
setState('Login')} className='text-primary underline cursor- '/api/user/appointments', { headers: { token } }) const { data } = await axios.post(backendUrl +
pointer'>Login here</span></p> setAppointments(data.appointments.reverse()) '/api/user/update-profile', formData, { headers: { token } })
: <p>Create an new account? <span onClick={() => setState('Sign
Up')} className='text-primary underline cursor-pointer'>Click } catch (error) { if (data.success) {
here</span></p> console.log(error) toast.success(data.message)
} toast.error(error.message) await loadUserProfileData()
</div> } setIsEdit(false)
</form> } setImage(false)
) } else {
} // Function to cancel appointment Using API toast.error(data.message)
const cancelAppointment = async (appointmentId) => { }
export default Login
try { } catch (error) {
console.log(error)
const { data } = await axios.post(backendUrl + toast.error(error.message)
'/api/user/cancel-appointment', { appointmentId }, { headers: { token } }
})
}
if (data.success) {
toast.success(data.message) return userData ? (
getUserAppointments() <div className='max-w-lg flex flex-col gap-2 text-sm pt-5'>
} else {
toast.error(data.message) {isEdit
} ? <label htmlFor='image' >
<div className='inline-block relative cursor-
} catch (error) { pointer'>
console.log(error) <img className='w-36 rounded opacity-75'
toast.error(error.message) src={image ? URL.createObjectURL(image) : userData.image} alt="" />
} <img className='w-10 absolute bottom-12 right-12'
src={image ? '' : assets.upload_icon} alt="" />

} console.log(error) </div>
toast.error(error.message) <input onChange={(e) => setImage(e.target.files[0])} {isEdit
const initPay = (order) => { } type="file" id="image" hidden /> ? <select className='max-w-20 bg-gray-50'
const options = { } </label> onChange={(e) => setUserData(prev => ({ ...prev, gender: e.target.value
key: import.meta.env.VITE_RAZORPAY_KEY_ID, : <img className='w-36 rounded' src={userData.image} }))} value={userData.gender} >
amount: order.amount, alt="" /> <option value="Not Selected">Not
currency: order.currency, } Selected</option>
name: 'Appointment Payment', useEffect(() => { <option value="Male">Male</option>
description: "Appointment Payment", if (token) { {isEdit <option value="Female">Female</option>
order_id: order.id, getUserAppointments() ? <input className='bg-gray-50 text-3xl font-medium max- </select>
receipt: order.receipt, } w-60' type="text" onChange={(e) => setUserData(prev => ({ ...prev, name: : <p className='text-gray-
handler: async (response) => { }, [token]) e.target.value }))} value={userData.name} /> 500'>{userData.gender}</p>
: <p className='font-medium text-3xl text-[#262626] mt- }
console.log(response) return ( 4'>{userData.name}</p>
<div> } <p className='font-medium'>Birthday:</p>
try { <p className='pb-3 mt-12 text-lg font-medium text-gray-600
const { data } = await axios.post(backendUrl + border-b'>My appointments</p> <hr className='bg-[#ADADAD] h-[1px] border-none' /> {isEdit
"/api/user/verifyRazorpay", response, { headers: { token } }); <div className=''> ? <input className='max-w-28 bg-gray-50'
if (data.success) { {appointments.map((item, index) => ( <div> type='date' onChange={(e) => setUserData(prev => ({ ...prev, dob:
navigate('/my-appointments') <div key={index} className='grid grid-cols-[1fr_2fr] <p className='text-gray-600 underline mt-3'>CONTACT e.target.value }))} value={userData.dob} />
getUserAppointments() gap-4 sm:flex sm:gap-6 py-4 border-b'> INFORMATION</p> : <p className='text-gray-500'>{userData.dob}</p>
} <div> <div className='grid grid-cols-[1fr_3fr] gap-y-2.5 mt-3 }
} catch (error) { <img className='w-36 bg-[#EAEFFF]' text-[#363636]'>
console.log(error) src={item.docData.image} alt="" /> <p className='font-medium'>Email id:</p> </div>
toast.error(error.message) </div> <p className='text-blue-500'>{userData.email}</p> </div>
} <div className='flex-1 text-sm text-[#5E5E5E]'> <p className='font-medium'>Phone:</p> <div className='mt-10'>
} <p className='text-[#262626] text-base font-
}; semibold'>{item.docData.name}</p> {isEdit {isEdit
const rzp = new window.Razorpay(options); <p>{item.docData.speciality}</p> ? <input className='bg-gray-50 max-w-52' ? <button onClick={updateUserProfileData}
rzp.open(); <p className='text-[#464646] font-medium mt- type="text" onChange={(e) => setUserData(prev => ({ ...prev, phone: className='border border-primary px-8 py-2 rounded-full hover:bg-primary
}; 1'>Address:</p> e.target.value }))} value={userData.phone} /> hover:text-white transition-all'>Save information</button>
<p : <p className='text-blue- : <button onClick={() => setIsEdit(true)}
// Function to make payment using razorpay className=''>{item.docData.address.line1}</p> 500'>{userData.phone}</p> className='border border-primary px-8 py-2 rounded-full hover:bg-primary
const appointmentRazorpay = async (appointmentId) => { <p } hover:text-white transition-all'>Edit</button>
try { className=''>{item.docData.address.line2}</p> }
const { data } = await axios.post(backendUrl + <p className=' mt-1'><span className='text-sm <p className='font-medium'>Address:</p>
'/api/user/payment-razorpay', { appointmentId }, { headers: { token } }) text-[#3C3C3C] font-medium'>Date & Time:</span> </div>
if (data.success) { {slotDateFormat(item.slotDate)} | {item.slotTime}</p> {isEdit </div>
initPay(data.order) </div> ? <p> ) : null
}else{ <div></div> <input className='bg-gray-50' type="text" }
toast.error(data.message) <div className='flex flex-col gap-2 justify-end onChange={(e) => setUserData(prev => ({ ...prev, address: {
} text-sm text-center'> ...prev.address, line1: e.target.value } }))} export default MyProfile
} catch (error) { {!item.cancelled && !item.payment && value={userData.address.line1} />
console.log(error) !item.isCompleted && payment !== item._id && <button onClick={() => <br />
toast.error(error.message) setPayment(item._id)} className='text-[#696969] sm:min-w-48 py-2 border <input className='bg-gray-50' type="text"
} rounded hover:bg-primary hover:text-white transition-all duration- onChange={(e) => setUserData(prev => ({ ...prev, address: {
} 300'>Pay Online</button>} ...prev.address, line2: e.target.value } }))}
{!item.cancelled && !item.payment && value={userData.address.line2} /></p>
// Function to make payment using stripe !item.isCompleted && payment === item._id && <button onClick={() => : <p className='text-gray-
const appointmentStripe = async (appointmentId) => { appointmentStripe(item._id)} className='text-[#696969] sm:min-w-48 py-2 500'>{userData.address.line1} <br /> {userData.address.line2}</p>
try { border rounded hover:bg-gray-100 hover:text-white transition-all }
const { data } = await axios.post(backendUrl + duration-300 flex items-center justify-center'><img className='max-w-20
'/api/user/payment-stripe', { appointmentId }, { headers: { token } }) max-h-5' src={assets.stripe_logo} alt="" /></button>} </div>
if (data.success) { {!item.cancelled && !item.payment && </div>
const { session_url } = data !item.isCompleted && payment === item._id && <button onClick={() => <div>
window.location.replace(session_url) appointmentRazorpay(item._id)} className='text-[#696969] sm:min-w-48 py-2 <p className='text-[#797979] underline mt-3'>BASIC
}else{ border rounded hover:bg-gray-100 hover:text-white transition-all INFORMATION</p>
toast.error(data.message) duration-300 flex items-center justify-center'><img className='max-w-20 <div className='grid grid-cols-[1fr_3fr] gap-y-2.5 mt-3
} max-h-5' src={assets.razorpay_logo} alt="" /></button>} text-gray-600'>
} catch (error) { <p className='font-medium'>Gender:</p>
import axios from 'axios'; import React from 'react' import React, { useContext } from 'react' import React, { useContext } from 'react'
import React, { useContext, useEffect } from 'react' import ReactDOM from 'react-dom/client' import { assets } from '../assets/assets' import { assets } from '../assets/assets'
import { useNavigate, useSearchParams } from 'react-router-dom' import App from './App.jsx' import { DoctorContext } from '../context/DoctorContext' import { NavLink } from 'react-router-dom'
import { AppContext } from '../context/AppContext'; import './index.css' import { AdminContext } from '../context/AdminContext' import { DoctorContext } from '../context/DoctorContext'
import { toast } from 'react-toastify'; import { BrowserRouter } from 'react-router-dom' import { useNavigate } from 'react-router-dom' import { AdminContext } from '../context/AdminContext'
import AppContextProvider from './context/AppContext.jsx'
const Verify = () => { const Navbar = () => { const Sidebar = () => {
ReactDOM.createRoot(document.getElementById('root')).render(
const [searchParams, setSearchParams] = useSearchParams() <BrowserRouter> const { dToken, setDToken } = useContext(DoctorContext) const { dToken } = useContext(DoctorContext)
<AppContextProvider> const { aToken, setAToken } = useContext(AdminContext) const { aToken } = useContext(AdminContext)
const success = searchParams.get("success") <App />
const appointmentId = searchParams.get("appointmentId") </AppContextProvider> const navigate = useNavigate() return (
</BrowserRouter>, <div className='min-h-screen bg-white border-r'>
const { backendUrl, token } = useContext(AppContext) ) const logout = () => { {aToken && <ul className='text-[#515151] mt-5'>
navigate('/')
const navigate = useNavigate() dToken && setDToken('') <NavLink to={'/admin-dashboard'} className={({ isActive }) =>
dToken && localStorage.removeItem('dToken') `flex items-center gap-3 py-3.5 px-3 md:px-9 md:min-w-72 cursor-pointer
// Function to verify stripe payment aToken && setAToken('') ${isActive ? 'bg-[#F2F3FF] border-r-4 border-primary' : ''}`}>
const verifyStripe = async () => { aToken && localStorage.removeItem('aToken') <img className='min-w-5' src={assets.home_icon} alt='' />
} <p className='hidden md:block'>Dashboard</p>
try { </NavLink>
return ( <NavLink to={'/all-appointments'} className={({ isActive }) =>
const { data } = await axios.post(backendUrl + <div className='flex justify-between items-center px-4 sm:px-10 py-3 `flex items-center gap-3 py-3.5 px-3 md:px-9 md:min-w-72 cursor-pointer
"/api/user/verifyStripe", { success, appointmentId }, { headers: { token border-b bg-white'> ${isActive ? 'bg-[#F2F3FF] border-r-4 border-primary' : ''}`}>
} }) <div className='flex items-center gap-2 text-xs'> <img className='min-w-5' src={assets.appointment_icon} alt=''
<img onClick={() => navigate('/')} className='w-36 sm:w-40 />
if (data.success) { cursor-pointer' src={assets.admin_logo} alt="" /> <p className='hidden md:block'>Appointments</p>
toast.success(data.message) <p className='border px-2.5 py-0.5 rounded-full border-gray-500 </NavLink>
} else { text-gray-600'>{aToken ? 'Admin' : 'Doctor'}</p> <NavLink to={'/add-doctor'} className={({ isActive }) => `flex
toast.error(data.message) </div> items-center gap-3 py-3.5 px-3 md:px-9 md:min-w-72 cursor-pointer
} <button onClick={() => logout()} className='bg-primary text-white ${isActive ? 'bg-[#F2F3FF] border-r-4 border-primary' : ''}`}>
text-sm px-10 py-2 rounded-full'>Logout</button> <img className='min-w-5' src={assets.add_icon} alt='' />
navigate("/my-appointments") </div> <p className='hidden md:block'>Add Doctor</p>
) </NavLink>
} catch (error) { } <NavLink to={'/doctor-list'} className={({ isActive }) => `flex
toast.error(error.message) items-center gap-3 py-3.5 px-3 md:px-9 md:min-w-72 cursor-pointer
console.log(error) export default Navbar ${isActive ? 'bg-[#F2F3FF] border-r-4 border-primary' : ''}`}>
} <img className='min-w-5' src={assets.people_icon} alt='' />
<p className='hidden md:block'>Doctors List</p>
} </NavLink>
</ul>}
useEffect(() => {
if (token, appointmentId, success) { {dToken && <ul className='text-[#515151] mt-5'>
verifyStripe() <NavLink to={'/doctor-dashboard'} className={({ isActive }) =>
} `flex items-center gap-3 py-3.5 px-3 md:px-9 md:min-w-72 cursor-pointer
}, [token]) ${isActive ? 'bg-[#F2F3FF] border-r-4 border-primary' : ''}`}>
<img className='min-w-5' src={assets.home_icon} alt='' />
return ( <p className='hidden md:block'>Dashboard</p>
<div className='min-h-[60vh] flex items-center justify-center'> </NavLink>
<div className="w-20 h-20 border-4 border-gray-300 border-t-4 <NavLink to={'/doctor-appointments'} className={({ isActive }) =>
border-t-primary rounded-full animate-spin"></div> `flex items-center gap-3 py-3.5 px-3 md:px-9 md:min-w-72 cursor-pointer
</div> ${isActive ? 'bg-[#F2F3FF] border-r-4 border-primary' : ''}`}>
) <img className='min-w-5' src={assets.appointment_icon} alt=''
} />
<p className='hidden md:block'>Appointments</p>
export default Verify </NavLink>
<NavLink to={'/doctor-profile'} className={({ isActive }) =>
`flex items-center gap-3 py-3.5 px-3 md:px-9 md:min-w-72 cursor-pointer
${isActive ? 'bg-[#F2F3FF] border-r-4 border-primary' : ''}`}>
<img className='min-w-5' src={assets.people_icon} alt='' />

import React from 'react' VITE_BACKEND_URL = 'http://localhost:4000' <p className='hidden md:block'>Profile</p> import axios from "axios";
import Navbar from './components/Navbar' VITE_RAZORPAY_KEY_ID = '------ Razorpay Key Id here ------' </NavLink> import { createContext, useState } from "react";
import { Routes, Route } from 'react-router-dom' </ul>} import { toast } from "react-toastify";
import Home from './pages/Home' </div>
import Doctors from './pages/Doctors' )
import Login from './pages/Login' } export const AdminContext = createContext()
import About from './pages/About'
import Contact from './pages/Contact' export default Sidebar const AdminContextProvider = (props) => {
import Appointment from './pages/Appointment'
import MyAppointments from './pages/MyAppointments' const backendUrl = import.meta.env.VITE_BACKEND_URL
import MyProfile from './pages/MyProfile'
import Footer from './components/Footer' const [aToken, setAToken] = useState(localStorage.getItem('aToken') ?
import { ToastContainer } from 'react-toastify'; localStorage.getItem('aToken') : '')
import 'react-toastify/dist/ReactToastify.css';
import Verify from './pages/Verify' const [appointments, setAppointments] = useState([])
const [doctors, setDoctors] = useState([])
const App = () => { const [dashData, setDashData] = useState(false)
return (
<div className='mx-4 sm:mx-[10%]'> // Getting all Doctors data from Database using API
<ToastContainer /> const getAllDoctors = async () => {
<Navbar />
<Routes> try {
<Route path='/' element={<Home />} />
<Route path='/doctors' element={<Doctors />} /> const { data } = await axios.get(backendUrl +
<Route path='/doctors/:speciality' element={<Doctors />} /> '/api/admin/all-doctors', { headers: { aToken } })
<Route path='/login' element={<Login />} /> if (data.success) {
<Route path='/about' element={<About />} /> setDoctors(data.doctors)
<Route path='/contact' element={<Contact />} /> } else {
<Route path='/appointment/:docId' element={<Appointment />} /> toast.error(data.message)
<Route path='/my-appointments' element={<MyAppointments />} /> }
<Route path='/my-profile' element={<MyProfile />} />
<Route path='/verify' element={<Verify />} /> } catch (error) {
</Routes> toast.error(error.message)
<Footer /> }
</div>
) }
}
// Function to change doctor availablity using API
export default App const changeAvailability = async (docId) => {
try {

const { data } = await axios.post(backendUrl +


'/api/admin/change-availability', { docId }, { headers: { aToken } })
if (data.success) {
toast.success(data.message)
getAllDoctors()
} else {
toast.error(data.message)
}

} catch (error) {
console.log(error)
toast.error(error.message)
}
}

// Getting all appointment data from Database using API


const getAllAppointments = async () => {

import React, { useContext } from 'react'


import { assets } from '../assets/assets'
try { const value = {
aToken, setAToken,
const { data } = await axios.get(backendUrl + doctors,
'/api/admin/appointments', { headers: { aToken } })
if (data.success) {
getAllDoctors,
changeAvailability,
import { DoctorContext } from '../context/DoctorContext'
setAppointments(data.appointments.reverse())
} else {
appointments,
getAllAppointments, import { AdminContext } from '../context/AdminContext'
toast.error(data.message) getDashData,
} cancelAppointment,
dashData
import { useNavigate } from 'react-router-dom'
} catch (error) { }
toast.error(error.message)

const Navbar = () => {


console.log(error) return (
} <AdminContext.Provider value={value}>
{props.children}
} </AdminContext.Provider>
)
// Function to cancel appointment using API
const cancelAppointment = async (appointmentId) => { } const { dToken, setDToken } = useContext(DoctorContext)
try { export default AdminContextProvider const { aToken, setAToken } = useContext(AdminContext)
const { data } = await axios.post(backendUrl +
'/api/admin/cancel-appointment', { appointmentId }, { headers: { aToken }

const navigate = useNavigate()


})

if (data.success) {
toast.success(data.message)
getAllAppointments()
} else {
toast.error(data.message) const logout = () => {
}

} catch (error) {
navigate('/')
toast.error(error.message)
console.log(error) dToken && setDToken('')
dToken && localStorage.removeItem('dToken')
}

// Getting Admin Dashboard data from Database using API


aToken && setAToken('')
const getDashData = async () => {
try { aToken && localStorage.removeItem('aToken')
const { data } = await axios.get(backendUrl +
'/api/admin/dashboard', { headers: { aToken } })
}
if (data.success) {

return (
setDashData(data.dashData)
} else {
toast.error(data.message)
}
<div className='flex justify-between items-center px-4 sm:px-10 py-3
} catch (error) {
console.log(error) border-b bg-white'>
toast.error(error.message)
} <div className='flex items-center gap-2 text-xs'>
}
<img onClick={() => navigate('/')} className='w-36 sm:w-40
cursor-pointer' src={assets.admin_logo} alt="" />
<p className='border px-2.5 py-0.5 rounded-full border-gray-500
text-gray-600'>{aToken ? 'Admin' : 'Doctor'}</p>
</div>
<button onClick={() => logout()} className='bg-primary text-white
text-sm px-10 py-2 rounded-full'>Logout</button>
</div>
import { createContext } from "react"; )
export const AppContext = createContext()
}
const AppContextProvider = (props) => {

const currency = import.meta.env.VITE_CURRENCY


const backendUrl = import.meta.env.VITE_BACKEND_URL
export default Navbar
const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
"Aug", "Sep", "Oct", "Nov", "Dec"]

// Function to format the date eg. ( 20_01_2000 => 20 Jan 2000 )


const slotDateFormat = (slotDate) => {
const dateArray = slotDate.split('_')
return dateArray[0] + " " + months[Number(dateArray[1])] + " " +
dateArray[2]
}

// Function to calculate the age eg. ( 20_01_2000 => 24 )


const calculateAge = (dob) => {
const today = new Date()
const birthDate = new Date(dob)
let age = today.getFullYear() - birthDate.getFullYear()
return age
}

const value = {
backendUrl,
currency,
slotDateFormat,
calculateAge,
}

return (
<AppContext.Provider value={value}>
{props.children}
</AppContext.Provider>
)

export default AppContextProvider


import React, { useContext } from 'react' <p className='hidden md:block'>Profile</p>
import { assets } from '../assets/assets' </NavLink>
import { NavLink } from 'react-router-dom' </ul>}
import { DoctorContext } from '../context/DoctorContext' </div>
import { AdminContext } from '../context/AdminContext' )
}
const Sidebar = () => {
export default Sidebar
const { dToken } = useContext(DoctorContext)
const { aToken } = useContext(AdminContext)

return (
<div className='min-h-screen bg-white border-r'>
{aToken && <ul className='text-[#515151] mt-5'>

<NavLink to={'/admin-dashboard'} className={({ isActive }) =>


`flex items-center gap-3 py-3.5 px-3 md:px-9 md:min-w-72 cursor-pointer
${isActive ? 'bg-[#F2F3FF] border-r-4 border-primary' : ''}`}>
<img className='min-w-5' src={assets.home_icon} alt='' />
<p className='hidden md:block'>Dashboard</p>
</NavLink>
<NavLink to={'/all-appointments'} className={({ isActive }) =>
`flex items-center gap-3 py-3.5 px-3 md:px-9 md:min-w-72 cursor-pointer
${isActive ? 'bg-[#F2F3FF] border-r-4 border-primary' : ''}`}>
<img className='min-w-5' src={assets.appointment_icon} alt=''
/>
<p className='hidden md:block'>Appointments</p>
</NavLink>
<NavLink to={'/add-doctor'} className={({ isActive }) => `flex
items-center gap-3 py-3.5 px-3 md:px-9 md:min-w-72 cursor-pointer
${isActive ? 'bg-[#F2F3FF] border-r-4 border-primary' : ''}`}>
<img className='min-w-5' src={assets.add_icon} alt='' />
<p className='hidden md:block'>Add Doctor</p>
</NavLink>
<NavLink to={'/doctor-list'} className={({ isActive }) => `flex
items-center gap-3 py-3.5 px-3 md:px-9 md:min-w-72 cursor-pointer
${isActive ? 'bg-[#F2F3FF] border-r-4 border-primary' : ''}`}>
<img className='min-w-5' src={assets.people_icon} alt='' />
<p className='hidden md:block'>Doctors List</p>
</NavLink>
</ul>}

{dToken && <ul className='text-[#515151] mt-5'>


<NavLink to={'/doctor-dashboard'} className={({ isActive }) =>
`flex items-center gap-3 py-3.5 px-3 md:px-9 md:min-w-72 cursor-pointer
${isActive ? 'bg-[#F2F3FF] border-r-4 border-primary' : ''}`}>
<img className='min-w-5' src={assets.home_icon} alt='' />
<p className='hidden md:block'>Dashboard</p>
</NavLink>
<NavLink to={'/doctor-appointments'} className={({ isActive }) =>
`flex items-center gap-3 py-3.5 px-3 md:px-9 md:min-w-72 cursor-pointer
${isActive ? 'bg-[#F2F3FF] border-r-4 border-primary' : ''}`}>
<img className='min-w-5' src={assets.appointment_icon} alt=''
/>
<p className='hidden md:block'>Appointments</p>
</NavLink>
<NavLink to={'/doctor-profile'} className={({ isActive }) =>
`flex items-center gap-3 py-3.5 px-3 md:px-9 md:min-w-72 cursor-pointer
${isActive ? 'bg-[#F2F3FF] border-r-4 border-primary' : ''}`}>
<img className='min-w-5' src={assets.people_icon} alt='' />

import axios from "axios"; try {


import { createContext, useState } from "react";
import { toast } from "react-toastify"; const { data } = await axios.get(backendUrl +
'/api/admin/appointments', { headers: { aToken } })
if (data.success) {
export const AdminContext = createContext() setAppointments(data.appointments.reverse())
} else {
const AdminContextProvider = (props) => { toast.error(data.message)
}
const backendUrl = import.meta.env.VITE_BACKEND_URL
} catch (error) {
const [aToken, setAToken] = useState(localStorage.getItem('aToken') ? toast.error(error.message)
localStorage.getItem('aToken') : '') console.log(error)
}
const [appointments, setAppointments] = useState([])
const [doctors, setDoctors] = useState([]) }
const [dashData, setDashData] = useState(false)
// Function to cancel appointment using API
// Getting all Doctors data from Database using API const cancelAppointment = async (appointmentId) => {
const getAllDoctors = async () => {
try {
try {
const { data } = await axios.post(backendUrl +
const { data } = await axios.get(backendUrl + '/api/admin/cancel-appointment', { appointmentId }, { headers: { aToken }
'/api/admin/all-doctors', { headers: { aToken } }) })
if (data.success) {
setDoctors(data.doctors) if (data.success) {
} else { toast.success(data.message)
toast.error(data.message) getAllAppointments()
} } else {
toast.error(data.message)
} catch (error) { }
toast.error(error.message)
} } catch (error) {
toast.error(error.message)
} console.log(error)
}
// Function to change doctor availablity using API
const changeAvailability = async (docId) => { }
try {
// Getting Admin Dashboard data from Database using API
const { data } = await axios.post(backendUrl + const getDashData = async () => {
'/api/admin/change-availability', { docId }, { headers: { aToken } }) try {
if (data.success) {
toast.success(data.message) const { data } = await axios.get(backendUrl +
getAllDoctors() '/api/admin/dashboard', { headers: { aToken } })
} else {
toast.error(data.message) if (data.success) {
} setDashData(data.dashData)
} else {
} catch (error) { toast.error(data.message)
console.log(error) }
toast.error(error.message)
} } catch (error) {
} console.log(error)
toast.error(error.message)
}
// Getting all appointment data from Database using API
const getAllAppointments = async () => { }
const value = { import { createContext } from "react";
aToken, setAToken,
doctors,
getAllDoctors, export const AppContext = createContext()
changeAvailability,
appointments, const AppContextProvider = (props) => {
getAllAppointments,
getDashData, const currency = import.meta.env.VITE_CURRENCY
cancelAppointment, const backendUrl = import.meta.env.VITE_BACKEND_URL
dashData
} const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
"Aug", "Sep", "Oct", "Nov", "Dec"]
return (
<AdminContext.Provider value={value}> // Function to format the date eg. ( 20_01_2000 => 20 Jan 2000 )
{props.children} const slotDateFormat = (slotDate) => {
</AdminContext.Provider> const dateArray = slotDate.split('_')
) return dateArray[0] + " " + months[Number(dateArray[1])] + " " +
dateArray[2]
} }

export default AdminContextProvider // Function to calculate the age eg. ( 20_01_2000 => 24 )
const calculateAge = (dob) => {
const today = new Date()
const birthDate = new Date(dob)
let age = today.getFullYear() - birthDate.getFullYear()
return age
}

const value = {
backendUrl,
currency,
slotDateFormat,
calculateAge,
}

return (
<AppContext.Provider value={value}>
{props.children}
</AppContext.Provider>
)

export default AppContextProvider

import { createContext, useState } from "react"; if (data.success) {


import axios from 'axios' toast.success(data.message)
import { toast } from 'react-toastify' getAppointments()
// after creating dashboard
getDashData()
export const DoctorContext = createContext() } else {
toast.error(data.message)
const DoctorContextProvider = (props) => { }

const backendUrl = import.meta.env.VITE_BACKEND_URL } catch (error) {


toast.error(error.message)
const [dToken, setDToken] = useState(localStorage.getItem('dToken') ? console.log(error)
localStorage.getItem('dToken') : '') }
const [appointments, setAppointments] = useState([])
const [dashData, setDashData] = useState(false) }
const [profileData, setProfileData] = useState(false)
// Function to Mark appointment completed using API
// Getting Doctor appointment data from Database using API const completeAppointment = async (appointmentId) => {
const getAppointments = async () => {
try { try {

const { data } = await axios.get(backendUrl + const { data } = await axios.post(backendUrl +


'/api/doctor/appointments', { headers: { dToken } }) '/api/doctor/complete-appointment', { appointmentId }, { headers: {
dToken } })
if (data.success) {
setAppointments(data.appointments.reverse()) if (data.success) {
} else { toast.success(data.message)
toast.error(data.message) getAppointments()
} // Later after creating getDashData Function
getDashData()
} catch (error) { } else {
console.log(error) toast.error(data.message)
toast.error(error.message) }
}
} } catch (error) {
toast.error(error.message)
// Getting Doctor profile data from Database using API console.log(error)
const getProfileData = async () => { }
try {
}
const { data } = await axios.get(backendUrl +
'/api/doctor/profile', { headers: { dToken } }) // Getting Doctor dashboard data using API
console.log(data.profileData) const getDashData = async () => {
setProfileData(data.profileData) try {

} catch (error) { const { data } = await axios.get(backendUrl +


console.log(error) '/api/doctor/dashboard', { headers: { dToken } })
toast.error(error.message)
} if (data.success) {
} setDashData(data.dashData)
} else {
// Function to cancel doctor appointment using API toast.error(data.message)
const cancelAppointment = async (appointmentId) => { }

try { } catch (error) {


console.log(error)
const { data } = await axios.post(backendUrl + toast.error(error.message)
'/api/doctor/cancel-appointment', { appointmentId }, { headers: { dToken }
} })
}
import React, { useContext, useState } from 'react'
const value = { import { assets } from '../../assets/assets'
dToken, setDToken, backendUrl, import { toast } from 'react-toastify'
appointments, import axios from 'axios'
getAppointments, import { AdminContext } from '../../context/AdminContext'
cancelAppointment, import { AppContext } from '../../context/AppContext'
completeAppointment,
dashData, getDashData, const AddDoctor = () => {
profileData, setProfileData,
getProfileData, const [docImg, setDocImg] = useState(false)
} const [name, setName] = useState('')
const [email, setEmail] = useState('')
return ( const [password, setPassword] = useState('')
<DoctorContext.Provider value={value}> const [experience, setExperience] = useState('1 Year')
{props.children} const [fees, setFees] = useState('')
</DoctorContext.Provider> const [about, setAbout] = useState('')
) const [speciality, setSpeciality] = useState('General physician')
const [degree, setDegree] = useState('')
const [address1, setAddress1] = useState('')
} const [address2, setAddress2] = useState('')

export default DoctorContextProvider const { backendUrl } = useContext(AppContext)


const { aToken } = useContext(AdminContext)

const onSubmitHandler = async (event) => {


event.preventDefault()

try {

if (!docImg) {
return toast.error('Image Not Selected')
}

const formData = new FormData();

formData.append('image', docImg)
formData.append('name', name)
formData.append('email', email)
formData.append('password', password)
formData.append('experience', experience)
formData.append('fees', Number(fees))
formData.append('about', about)
formData.append('speciality', speciality)
formData.append('degree', degree)
formData.append('address', JSON.stringify({ line1: address1,
line2: address2 }))

// console log formdata


formData.forEach((value, key) => {
console.log(`${key}: ${value}`);
});

const { data } = await axios.post(backendUrl +


'/api/admin/add-doctor', formData, { headers: { aToken } })
if (data.success) {
toast.success(data.message)
setDocImg(false)
setName('')
setPassword('')
setEmail('')

setAddress1('') <input onChange={e =>


setAddress2('') setPassword(e.target.value)} value={password} className='border rounded
setDegree('') px-3 py-2' type="password" placeholder='Password' required />
setAbout('') </div>
setFees('')
} else { <div className='flex-1 flex flex-col gap-1'>
toast.error(data.message) <p>Experience</p>
} <select onChange={e =>
setExperience(e.target.value)} value={experience} className='border
} catch (error) { rounded px-2 py-2' >
toast.error(error.message) <option value="1 Year">1 Year</option>
console.log(error) <option value="2 Year">2 Years</option>
} <option value="3 Year">3 Years</option>
<option value="4 Year">4 Years</option>
} <option value="5 Year">5 Years</option>
<option value="6 Year">6 Years</option>
return ( <option value="8 Year">8 Years</option>
<form onSubmit={onSubmitHandler} className='m-5 w-full'> <option value="9 Year">9 Years</option>
<option value="10 Year">10 Years</option>
<p className='mb-3 text-lg font-medium'>Add Doctor</p> </select>
</div>
<div className='bg-white px-8 py-8 border rounded w-full max-
w-4xl max-h-[80vh] overflow-y-scroll'> <div className='flex-1 flex flex-col gap-1'>
<div className='flex items-center gap-4 mb-8 text-gray- <p>Fees</p>
500'> <input onChange={e =>
<label htmlFor="doc-img"> setFees(e.target.value)} value={fees} className='border rounded px-3 py-
<img className='w-16 bg-gray-100 rounded-full 2' type="number" placeholder='Doctor fees' required />
cursor-pointer' src={docImg ? URL.createObjectURL(docImg) : </div>
assets.upload_area} alt="" />
</label> </div>
<input onChange={(e) => setDocImg(e.target.files[0])}
type="file" name="" id="doc-img" hidden /> <div className='w-full lg:flex-1 flex flex-col gap-
<p>Upload doctor <br /> picture</p> 4'>
</div>
<div className='flex-1 flex flex-col gap-1'>
<div className='flex flex-col lg:flex-row items-start <p>Speciality</p>
gap-10 text-gray-600'> <select onChange={e =>
setSpeciality(e.target.value)} value={speciality} className='border
<div className='w-full lg:flex-1 flex flex-col gap- rounded px-2 py-2'>
4'> <option value="General physician">General
physician</option>
<div className='flex-1 flex flex-col gap-1'> <option
<p>Your name</p> value="Gynecologist">Gynecologist</option>
<input onChange={e => <option
setName(e.target.value)} value={name} className='border rounded px-3 py- value="Dermatologist">Dermatologist</option>
2' type="text" placeholder='Name' required /> <option
</div> value="Pediatricians">Pediatricians</option>
<option
<div className='flex-1 flex flex-col gap-1'> value="Neurologist">Neurologist</option>
<p>Doctor Email</p> <option
<input onChange={e => value="Gastroenterologist">Gastroenterologist</option>
setEmail(e.target.value)} value={email} className='border rounded px-3 </select>
py-2' type="email" placeholder='Email' required /> </div>
</div>

<div className='flex-1 flex flex-col gap-1'>


<div className='flex-1 flex flex-col gap-1'> <p>Degree</p>
<p>Set Password</p> <input onChange={e =>
setDegree(e.target.value)} value={degree} className='border rounded px-3
py-2' type="text" placeholder='Degree' required />
</div> import React, { useEffect } from 'react'
import { assets } from '../../assets/assets'
<div className='flex-1 flex flex-col gap-1'> import { useContext } from 'react'
<p>Address</p> import { AdminContext } from '../../context/AdminContext'
<input onChange={e => import { AppContext } from '../../context/AppContext'
setAddress1(e.target.value)} value={address1} className='border rounded
px-3 py-2' type="text" placeholder='Address 1' required /> const AllAppointments = () => {
<input onChange={e =>
setAddress2(e.target.value)} value={address2} className='border rounded const { aToken, appointments, cancelAppointment, getAllAppointments } =
px-3 py-2' type="text" placeholder='Address 2' required /> useContext(AdminContext)
</div> const { slotDateFormat, calculateAge, currency } =
useContext(AppContext)
</div>
useEffect(() => {
</div> if (aToken) {
getAllAppointments()
<div> }
<p className='mt-4 mb-2'>About Doctor</p> }, [aToken])
<textarea onChange={e => setAbout(e.target.value)}
value={about} className='w-full px-4 pt-2 border rounded' rows={5} return (
placeholder='write about doctor'></textarea> <div className='w-full max-w-6xl m-5 '>
</div>
<p className='mb-3 text-lg font-medium'>All Appointments</p>
<button type='submit' className='bg-primary px-10 py-3
mt-4 text-white rounded-full'>Add doctor</button> <div className='bg-white border rounded text-sm max-h-[80vh]
overflow-y-scroll'>
</div> <div className='hidden sm:grid grid-cols-
[0.5fr_3fr_1fr_3fr_3fr_1fr_1fr] grid-flow-col py-3 px-6 border-b'>
<p>#</p>
</form> <p>Patient</p>
) <p>Age</p>
} <p>Date & Time</p>
<p>Doctor</p>
export default AddDoctor <p>Fees</p>
<p>Action</p>
</div>
{appointments.map((item, index) => (
<div className='flex flex-wrap justify-between max-sm:gap-2
sm:grid sm:grid-cols-[0.5fr_3fr_1fr_3fr_3fr_1fr_1fr] items-center text-
gray-500 py-3 px-6 border-b hover:bg-gray-50' key={index}>
<p className='max-sm:hidden'>{index+1}</p>
<div className='flex items-center gap-2'>
<img src={item.userData.image} className='w-8 rounded-full'
alt="" /> <p>{item.userData.name}</p>
</div>
<p className='max-
sm:hidden'>{calculateAge(item.userData.dob)}</p>
<p>{slotDateFormat(item.slotDate)}, {item.slotTime}</p>
<div className='flex items-center gap-2'>
<img src={item.docData.image} className='w-8 rounded-full
bg-gray-200' alt="" /> <p>{item.docData.name}</p>
</div>
<p>{currency}{item.amount}</p>
{item.cancelled ? <p className='text-red-400 text-xs font-
medium'>Cancelled</p> : item.isCompleted ? <p className='text-green-500
text-xs font-medium'>Completed</p> : <img onClick={() =>
cancelAppointment(item._id)} className='w-10 cursor-pointer'
src={assets.cancel_icon} alt="" />}
</div>
))}

</div> import React, { useContext, useEffect } from 'react'


import { assets } from '../../assets/assets'
</div> import { AdminContext } from '../../context/AdminContext'
) import { AppContext } from '../../context/AppContext'
}
const Dashboard = () => {
export default AllAppointments
const { aToken, getDashData, cancelAppointment, dashData } =
useContext(AdminContext)
const { slotDateFormat } = useContext(AppContext)

useEffect(() => {
if (aToken) {
getDashData()
}
}, [aToken])

return dashData && (


<div className='m-5'>

<div className='flex flex-wrap gap-3'>


<div className='flex items-center gap-2 bg-white p-4 min-w-52
rounded border-2 border-gray-100 cursor-pointer hover:scale-105
transition-all'>
<img className='w-14' src={assets.doctor_icon} alt="" />
<div>
<p className='text-xl font-semibold text-gray-
600'>{dashData.doctors}</p>
<p className='text-gray-400'>Doctors</p>
</div>
</div>
<div className='flex items-center gap-2 bg-white p-4 min-w-52
rounded border-2 border-gray-100 cursor-pointer hover:scale-105
transition-all'>
<img className='w-14' src={assets.appointments_icon} alt="" />
<div>
<p className='text-xl font-semibold text-gray-
600'>{dashData.appointments}</p>
<p className='text-gray-400'>Appointments</p>
</div>
</div>
<div className='flex items-center gap-2 bg-white p-4 min-w-52
rounded border-2 border-gray-100 cursor-pointer hover:scale-105
transition-all'>
<img className='w-14' src={assets.patients_icon} alt="" />
<div>
<p className='text-xl font-semibold text-gray-
600'>{dashData.patients}</p>
<p className='text-gray-400'>Patients</p></div>
</div>
</div>

<div className='bg-white'>
<div className='flex items-center gap-2.5 px-4 py-4 mt-10
rounded-t border'>
<img src={assets.list_icon} alt="" />
<p className='font-semibold'>Latest Bookings</p>
</div>

<div className='pt-4 border border-t-0'>


{dashData.latestAppointments.slice(0, 5).map((item, index) => ( import React, { useContext, useEffect } from 'react'
<div className='flex items-center px-6 py-3 gap-3 hover:bg- import { AdminContext } from '../../context/AdminContext'
gray-100' key={index}>
<img className='rounded-full w-10' src={item.docData.image} const DoctorsList = () => {
alt="" />
<div className='flex-1 text-sm'> const { doctors, changeAvailability , aToken , getAllDoctors} =
<p className='text-gray-800 font- useContext(AdminContext)
medium'>{item.docData.name}</p>
<p className='text-gray-600 '>Booking on useEffect(() => {
{slotDateFormat(item.slotDate)}</p> if (aToken) {
</div> getAllDoctors()
{item.cancelled ? <p className='text-red-400 text-xs font- }
medium'>Cancelled</p> : item.isCompleted ? <p className='text-green-500 }, [aToken])
text-xs font-medium'>Completed</p> : <img onClick={() =>
cancelAppointment(item._id)} className='w-10 cursor-pointer' return (
src={assets.cancel_icon} alt="" />} <div className='m-5 max-h-[90vh] overflow-y-scroll'>
</div> <h1 className='text-lg font-medium'>All Doctors</h1>
))} <div className='w-full flex flex-wrap gap-4 pt-5 gap-y-6'>
</div> {doctors.map((item, index) => (
</div> <div className='border border-[#C9D8FF] rounded-xl max-w-56
overflow-hidden cursor-pointer group' key={index}>
</div> <img className='bg-[#EAEFFF] group-hover:bg-primary
) transition-all duration-500' src={item.image} alt="" />
} <div className='p-4'>
<p className='text-[#262626] text-lg font-
export default Dashboard medium'>{item.name}</p>
<p className='text-[#5C5C5C] text-sm'>{item.speciality}</p>
<div className='mt-2 flex items-center gap-1 text-sm'>
<input onChange={()=>changeAvailability(item._id)}
type="checkbox" checked={item.available} />
<p>Available</p>
</div>
</div>
</div>
))}
</div>
</div>
)
}

export default DoctorsList

import React from 'react' ? <p className='text-green-500 text-xs font-


import { useContext, useEffect } from 'react' medium'>Completed</p>
import { DoctorContext } from '../../context/DoctorContext' : <div className='flex'>
import { AppContext } from '../../context/AppContext' <img onClick={() => cancelAppointment(item._id)}
import { assets } from '../../assets/assets' className='w-10 cursor-pointer' src={assets.cancel_icon} alt="" />
<img onClick={() => completeAppointment(item._id)}
const DoctorAppointments = () => { className='w-10 cursor-pointer' src={assets.tick_icon} alt="" />
</div>
const { dToken, appointments, getAppointments, cancelAppointment, }
completeAppointment } = useContext(DoctorContext) </div>
const { slotDateFormat, calculateAge, currency } = ))}
useContext(AppContext) </div>

useEffect(() => { </div>


if (dToken) { )
getAppointments() }
}
}, [dToken]) export default DoctorAppointments

return (
<div className='w-full max-w-6xl m-5 '>

<p className='mb-3 text-lg font-medium'>All Appointments</p>

<div className='bg-white border rounded text-sm max-h-[80vh]


overflow-y-scroll'>
<div className='max-sm:hidden grid grid-cols-
[0.5fr_2fr_1fr_1fr_3fr_1fr_1fr] gap-1 py-3 px-6 border-b'>
<p>#</p>
<p>Patient</p>
<p>Payment</p>
<p>Age</p>
<p>Date & Time</p>
<p>Fees</p>
<p>Action</p>
</div>
{appointments.map((item, index) => (
<div className='flex flex-wrap justify-between max-sm:gap-5
max-sm:text-base sm:grid grid-cols-[0.5fr_2fr_1fr_1fr_3fr_1fr_1fr] gap-1
items-center text-gray-500 py-3 px-6 border-b hover:bg-gray-50'
key={index}>
<p className='max-sm:hidden'>{index}</p>
<div className='flex items-center gap-2'>
<img src={item.userData.image} className='w-8 rounded-full'
alt="" /> <p>{item.userData.name}</p>
</div>
<div>
<p className='text-xs inline border border-primary px-2
rounded-full'>
{item.payment?'Online':'CASH'}
</p>
</div>
<p className='max-
sm:hidden'>{calculateAge(item.userData.dob)}</p>
<p>{slotDateFormat(item.slotDate)}, {item.slotTime}</p>
<p>{currency}{item.amount}</p>
{item.cancelled
? <p className='text-red-400 text-xs font-
medium'>Cancelled</p>
: item.isCompleted
import React from 'react' <img src={assets.list_icon} alt="" />
import { useContext } from 'react' <p className='font-semibold'>Latest Bookings</p>
import { useEffect } from 'react' </div>
import { DoctorContext } from '../../context/DoctorContext'
import { assets } from '../../assets/assets' <div className='pt-4 border border-t-0'>
import { AppContext } from '../../context/AppContext' {dashData.latestAppointments.slice(0, 5).map((item, index) => (
<div className='flex items-center px-6 py-3 gap-3 hover:bg-
const DoctorDashboard = () => { gray-100' key={index}>
<img className='rounded-full w-10'
const { dToken, dashData, getDashData, cancelAppointment, src={item.userData.image} alt="" />
completeAppointment } = useContext(DoctorContext) <div className='flex-1 text-sm'>
const { slotDateFormat, currency } = useContext(AppContext) <p className='text-gray-800 font-
medium'>{item.userData.name}</p>
<p className='text-gray-600 '>Booking on
useEffect(() => { {slotDateFormat(item.slotDate)}</p>
</div>
if (dToken) { {item.cancelled
getDashData() ? <p className='text-red-400 text-xs font-
} medium'>Cancelled</p>
: item.isCompleted
}, [dToken]) ? <p className='text-green-500 text-xs font-
medium'>Completed</p>
return dashData && ( : <div className='flex'>
<div className='m-5'> <img onClick={() => cancelAppointment(item._id)}
className='w-10 cursor-pointer' src={assets.cancel_icon} alt="" />
<div className='flex flex-wrap gap-3'> <img onClick={() => completeAppointment(item._id)}
<div className='flex items-center gap-2 bg-white p-4 min-w-52 className='w-10 cursor-pointer' src={assets.tick_icon} alt="" />
rounded border-2 border-gray-100 cursor-pointer hover:scale-105 </div>
transition-all'> }
<img className='w-14' src={assets.earning_icon} alt="" /> </div>
<div> ))}
<p className='text-xl font-semibold text-gray-600'>{currency} </div>
{dashData.earnings}</p> </div>
<p className='text-gray-400'>Earnings</p>
</div> </div>
</div> )
<div className='flex items-center gap-2 bg-white p-4 min-w-52 }
rounded border-2 border-gray-100 cursor-pointer hover:scale-105
transition-all'> export default DoctorDashboard
<img className='w-14' src={assets.appointments_icon} alt="" />
<div>
<p className='text-xl font-semibold text-gray-
600'>{dashData.appointments}</p>
<p className='text-gray-400'>Appointments</p>
</div>
</div>
<div className='flex items-center gap-2 bg-white p-4 min-w-52
rounded border-2 border-gray-100 cursor-pointer hover:scale-105
transition-all'>
<img className='w-14' src={assets.patients_icon} alt="" />
<div>
<p className='text-xl font-semibold text-gray-
600'>{dashData.patients}</p>
<p className='text-gray-400'>Patients</p></div>
</div>
</div>

<div className='bg-white'>
<div className='flex items-center gap-2.5 px-4 py-4 mt-10
rounded-t border'>

import React, { useContext, useEffect, useState } from 'react'


import { DoctorContext } from '../../context/DoctorContext' {/* ----- Doc Info : name, degree, experience -----
import { AppContext } from '../../context/AppContext' */}
import { toast } from 'react-toastify'
import axios from 'axios' <p className='flex items-center gap-2 text-3xl font-
medium text-gray-700'>{profileData.name}</p>
const DoctorProfile = () => { <div className='flex items-center gap-2 mt-1 text-
gray-600'>
const { dToken, profileData, setProfileData, getProfileData } = <p>{profileData.degree} -
useContext(DoctorContext) {profileData.speciality}</p>
const { currency, backendUrl } = useContext(AppContext) <button className='py-0.5 px-2 border text-xs
const [isEdit, setIsEdit] = useState(false) rounded-full'>{profileData.experience}</button>
</div>
const updateProfile = async () => {
{/* ----- Doc About ----- */}
try { <div>
<p className='flex items-center gap-1 text-sm
const updateData = { font-medium text-[#262626] mt-3'>About :</p>
address: profileData.address, <p className='text-sm text-gray-600 max-w-[700px]
fees: profileData.fees, mt-1'>
about: profileData.about, {
available: profileData.available isEdit
} ? <textarea onChange={(e) =>
setProfileData(prev => ({ ...prev, about: e.target.value }))} type='text'
const { data } = await axios.post(backendUrl + className='w-full outline-primary p-2' rows={8} value={profileData.about}
'/api/doctor/update-profile', updateData, { headers: { dToken } }) />
: profileData.about
if (data.success) { }
toast.success(data.message) </p>
setIsEdit(false) </div>
getProfileData()
} else { <p className='text-gray-600 font-medium mt-4'>
toast.error(data.message) Appointment fee: <span className='text-gray-
} 800'>{currency} {isEdit ? <input type='number' onChange={(e) =>
setProfileData(prev => ({ ...prev, fees: e.target.value }))}
setIsEdit(false) value={profileData.fees} /> : profileData.fees}</span>
</p>
} catch (error) {
toast.error(error.message) <div className='flex gap-2 py-2'>
console.log(error) <p>Address:</p>
} <p className='text-sm'>
{isEdit ? <input type='text' onChange={(e) =>
} setProfileData(prev => ({ ...prev, address: { ...prev.address, line1:
e.target.value } }))} value={profileData.address.line1} /> :
useEffect(() => { profileData.address.line1}
if (dToken) { <br />
getProfileData() {isEdit ? <input type='text' onChange={(e) =>
} setProfileData(prev => ({ ...prev, address: { ...prev.address, line2:
}, [dToken]) e.target.value } }))} value={profileData.address.line2} /> :
profileData.address.line2}
return profileData && ( </p>
<div> </div>
<div className='flex flex-col gap-4 m-5'>
<div> <div className='flex gap-1 pt-2'>
<img className='bg-primary/80 w-full sm:max-w-64 <input type="checkbox" onChange={() => isEdit &&
rounded-lg' src={profileData.image} alt="" /> setProfileData(prev => ({ ...prev, available: !prev.available }))}
</div> checked={profileData.available} />
<label htmlFor="">Available</label>
<div className='flex-1 border border-stone-100 rounded-lg </div>
p-8 py-7 bg-white'>
{ import axios from 'axios'
isEdit import React, { useContext, useState } from 'react'
? <button onClick={updateProfile} import { DoctorContext } from '../context/DoctorContext'
className='px-4 py-1 border border-primary text-sm rounded-full mt-5 import { AdminContext } from '../context/AdminContext'
hover:bg-primary hover:text-white transition-all'>Save</button> import { toast } from 'react-toastify'
: <button onClick={() => setIsEdit(prev =>
!prev)} className='px-4 py-1 border border-primary text-sm rounded-full const Login = () => {
mt-5 hover:bg-primary hover:text-white transition-all'>Edit</button>
} const [state, setState] = useState('Admin')

</div> const [email, setEmail] = useState('')


</div> const [password, setPassword] = useState('')
</div>
) const backendUrl = import.meta.env.VITE_BACKEND_URL
}
const { setDToken } = useContext(DoctorContext)
export default DoctorProfile const { setAToken } = useContext(AdminContext)

const onSubmitHandler = async (event) => {


event.preventDefault();

if (state === 'Admin') {

const { data } = await axios.post(backendUrl + '/api/admin/login',


{ email, password })
if (data.success) {
setAToken(data.token)
localStorage.setItem('aToken', data.token)
} else {
toast.error(data.message)
}

} else {

const { data } = await axios.post(backendUrl + '/api/doctor/login',


{ email, password })
if (data.success) {
setDToken(data.token)
localStorage.setItem('dToken', data.token)
} else {
toast.error(data.message)
}

return (
<form onSubmit={onSubmitHandler} className='min-h-[80vh] flex items-
center'>
<div className='flex flex-col gap-3 m-auto items-start p-8 min-w-
[340px] sm:min-w-96 border rounded-xl text-[#5E5E5E] text-sm shadow-lg'>
<p className='text-2xl font-semibold m-auto'><span
className='text-primary'>{state}</span> Login</p>
<div className='w-full '>
<p>Email</p>
<input onChange={(e) => setEmail(e.target.value)} value={email}
className='border border-[#DADADA] rounded w-full p-2 mt-1' type="email"
required />
</div>

<div className='w-full '> import React, { useContext } from 'react'


<p>Password</p> import { DoctorContext } from './context/DoctorContext';
<input onChange={(e) => setPassword(e.target.value)} import { AdminContext } from './context/AdminContext';
value={password} className='border border-[#DADADA] rounded w-full p-2 import { Route, Routes } from 'react-router-dom'
mt-1' type="password" required /> import { ToastContainer } from 'react-toastify';
</div> import 'react-toastify/dist/ReactToastify.css';
<button className='bg-primary text-white w-full py-2 rounded-md import Navbar from './components/Navbar'
text-base'>Login</button> import Sidebar from './components/Sidebar'
{ import Dashboard from './pages/Admin/Dashboard';
state === 'Admin' import AllAppointments from './pages/Admin/AllAppointments';
? <p>Doctor Login? <span onClick={() => setState('Doctor')} import AddDoctor from './pages/Admin/AddDoctor';
className='text-primary underline cursor-pointer'>Click here</span></p> import DoctorsList from './pages/Admin/DoctorsList';
: <p>Admin Login? <span onClick={() => setState('Admin')} import Login from './pages/Login';
className='text-primary underline cursor-pointer'>Click here</span></p> import DoctorAppointments from './pages/Doctor/DoctorAppointments';
} import DoctorDashboard from './pages/Doctor/DoctorDashboard';
</div> import DoctorProfile from './pages/Doctor/DoctorProfile';
</form>
) const App = () => {
}
const { dToken } = useContext(DoctorContext)
export default Login const { aToken } = useContext(AdminContext)

return dToken || aToken ? (


<div className='bg-[#F8F9FD]'>
<ToastContainer />
<Navbar />
<div className='flex items-start'>
<Sidebar />
<Routes>
<Route path='/' element={<></>} />
<Route path='/admin-dashboard' element={<Dashboard />} />
<Route path='/all-appointments' element={<AllAppointments />}
/>
<Route path='/add-doctor' element={<AddDoctor />} />
<Route path='/doctor-list' element={<DoctorsList />} />
<Route path='/doctor-dashboard' element={<DoctorDashboard />}
/>
<Route path='/doctor-appointments' element={<DoctorAppointments
/>} />
<Route path='/doctor-profile' element={<DoctorProfile />} />
</Routes>
</div>
</div>
) : (
<>
<ToastContainer />
<Login />
</>
)
}

export default App


import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css' VITE_CURRENCY = '₹'
import { BrowserRouter } from 'react-router-dom' VITE_BACKEND_URL = 'http://localhost:4000'
import AdminContextProvider from './context/AdminContext.jsx'
import DoctorContextProvider from './context/DoctorContext.jsx'
import AppContextProvider from './context/AppContext.jsx'

ReactDOM.createRoot(document.getElementById('root')).render(
<BrowserRouter>
<AdminContextProvider>
<DoctorContextProvider>
<AppContextProvider>
<App />
</AppContextProvider>
</DoctorContextProvider>
</AdminContextProvider>
</BrowserRouter>,
)

module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:react/jsx-runtime',
'plugin:react-hooks/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
settings: { react: { version: '18.2' } },
plugins: ['react-refresh'],
rules: {
'react/jsx-no-target-blank': 'off',
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}

You might also like