0% found this document useful (0 votes)
25 views30 pages

Admin (15 Files Merged)

Files
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)
25 views30 pages

Admin (15 Files Merged)

Files
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/ 30

import React, { useContext } from 'react'

import { assets } from '../assets/assets'


import { DoctorContext } from '../context/DoctorContext'
import { AdminContext } from '../context/AdminContext'
import { useNavigate } from 'react-router-dom'

const Navbar = () => {

const { dToken, setDToken } = useContext(DoctorContext)


const { aToken, setAToken } = useContext(AdminContext)

const navigate = useNavigate()

const logout = () => {


navigate('/')
dToken && setDToken('')
dToken && localStorage.removeItem('dToken')
aToken && setAToken('')
aToken && localStorage.removeItem('aToken')
}

return (
<div className='flex justify-between items-center px-4 sm:px-10 py-3
border-b bg-white'>
<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>
)
}

export default Navbar


import React, { useContext } from 'react'
import { assets } from '../assets/assets'
import { NavLink } from 'react-router-dom'
import { DoctorContext } from '../context/DoctorContext'
import { AdminContext } from '../context/AdminContext'

const 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='' />
<p className='hidden md:block'>Profile</p>
</NavLink>
</ul>}
</div>
)
}

export default Sidebar


import axios from "axios";
import { createContext, useState } from "react";
import { toast } from "react-toastify";

export const AdminContext = createContext()

const AdminContextProvider = (props) => {

const backendUrl = import.meta.env.VITE_BACKEND_URL

const [aToken, setAToken] = useState(localStorage.getItem('aToken') ?


localStorage.getItem('aToken') : '')

const [appointments, setAppointments] = useState([])


const [doctors, setDoctors] = useState([])
const [dashData, setDashData] = useState(false)

// Getting all Doctors data from Database using API


const getAllDoctors = async () => {

try {

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


'/api/admin/all-doctors', { headers: { aToken } })
if (data.success) {
setDoctors(data.doctors)
} else {
toast.error(data.message)
}

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

// Function to change doctor availablity using API


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 () => {
try {

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


'/api/admin/appointments', { headers: { aToken } })
if (data.success) {
setAppointments(data.appointments.reverse())
} else {
toast.error(data.message)
}

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

// Function to cancel appointment using API


const cancelAppointment = async (appointmentId) => {

try {

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


'/api/admin/cancel-appointment', { appointmentId }, { headers: { aToken }
})

if (data.success) {
toast.success(data.message)
getAllAppointments()
} else {
toast.error(data.message)
}

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

// Getting Admin Dashboard data from Database using API


const getDashData = async () => {
try {

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


'/api/admin/dashboard', { headers: { aToken } })

if (data.success) {
setDashData(data.dashData)
} else {
toast.error(data.message)
}

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

}
const value = {
aToken, setAToken,
doctors,
getAllDoctors,
changeAvailability,
appointments,
getAllAppointments,
getDashData,
cancelAppointment,
dashData
}

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

export default AdminContextProvider


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

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 { createContext, useState } from "react";
import axios from 'axios'
import { toast } from 'react-toastify'

export const DoctorContext = createContext()

const DoctorContextProvider = (props) => {

const backendUrl = import.meta.env.VITE_BACKEND_URL

const [dToken, setDToken] = useState(localStorage.getItem('dToken') ?


localStorage.getItem('dToken') : '')
const [appointments, setAppointments] = useState([])
const [dashData, setDashData] = useState(false)
const [profileData, setProfileData] = useState(false)

// Getting Doctor appointment data from Database using API


const getAppointments = async () => {
try {

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


'/api/doctor/appointments', { headers: { dToken } })

if (data.success) {
setAppointments(data.appointments.reverse())
} else {
toast.error(data.message)
}

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

// Getting Doctor profile data from Database using API


const getProfileData = async () => {
try {

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


'/api/doctor/profile', { headers: { dToken } })
console.log(data.profileData)
setProfileData(data.profileData)

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

// Function to cancel doctor appointment using API


const cancelAppointment = async (appointmentId) => {

try {

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


'/api/doctor/cancel-appointment', { appointmentId }, { headers: { dToken
} })
if (data.success) {
toast.success(data.message)
getAppointments()
// after creating dashboard
getDashData()
} else {
toast.error(data.message)
}

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

// Function to Mark appointment completed using API


const completeAppointment = async (appointmentId) => {

try {

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


'/api/doctor/complete-appointment', { appointmentId }, { headers: {
dToken } })

if (data.success) {
toast.success(data.message)
getAppointments()
// Later after creating getDashData Function
getDashData()
} else {
toast.error(data.message)
}

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

// Getting Doctor dashboard data using API


const getDashData = async () => {
try {

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


'/api/doctor/dashboard', { headers: { dToken } })

if (data.success) {
setDashData(data.dashData)
} else {
toast.error(data.message)
}

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

}
const value = {
dToken, setDToken, backendUrl,
appointments,
getAppointments,
cancelAppointment,
completeAppointment,
dashData, getDashData,
profileData, setProfileData,
getProfileData,
}

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

export default DoctorContextProvider


import React, { useContext, useState } from 'react'
import { assets } from '../../assets/assets'
import { toast } from 'react-toastify'
import axios from 'axios'
import { AdminContext } from '../../context/AdminContext'
import { AppContext } from '../../context/AppContext'

const AddDoctor = () => {

const [docImg, setDocImg] = useState(false)


const [name, setName] = useState('')
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [experience, setExperience] = useState('1 Year')
const [fees, setFees] = useState('')
const [about, setAbout] = useState('')
const [speciality, setSpeciality] = useState('General physician')
const [degree, setDegree] = useState('')
const [address1, setAddress1] = useState('')
const [address2, setAddress2] = useState('')

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('')
setAddress2('')
setDegree('')
setAbout('')
setFees('')
} else {
toast.error(data.message)
}

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

return (
<form onSubmit={onSubmitHandler} className='m-5 w-full'>

<p className='mb-3 text-lg font-medium'>Add Doctor</p>

<div className='bg-white px-8 py-8 border rounded w-full max-


w-4xl max-h-[80vh] overflow-y-scroll'>
<div className='flex items-center gap-4 mb-8 text-gray-
500'>
<label htmlFor="doc-img">
<img className='w-16 bg-gray-100 rounded-full
cursor-pointer' src={docImg ? URL.createObjectURL(docImg) :
assets.upload_area} alt="" />
</label>
<input onChange={(e) => setDocImg(e.target.files[0])}
type="file" name="" id="doc-img" hidden />
<p>Upload doctor <br /> picture</p>
</div>

<div className='flex flex-col lg:flex-row items-start


gap-10 text-gray-600'>

<div className='w-full lg:flex-1 flex flex-col gap-


4'>

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


<p>Your name</p>
<input onChange={e =>
setName(e.target.value)} value={name} className='border rounded px-3 py-
2' type="text" placeholder='Name' required />
</div>

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


<p>Doctor Email</p>
<input onChange={e =>
setEmail(e.target.value)} value={email} className='border rounded px-3
py-2' type="email" placeholder='Email' required />
</div>

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


<p>Set Password</p>
<input onChange={e =>
setPassword(e.target.value)} value={password} className='border rounded
px-3 py-2' type="password" placeholder='Password' required />
</div>

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


<p>Experience</p>
<select onChange={e =>
setExperience(e.target.value)} value={experience} className='border
rounded px-2 py-2' >
<option value="1 Year">1 Year</option>
<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>
<option value="8 Year">8 Years</option>
<option value="9 Year">9 Years</option>
<option value="10 Year">10 Years</option>
</select>
</div>

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


<p>Fees</p>
<input onChange={e =>
setFees(e.target.value)} value={fees} className='border rounded px-3 py-
2' type="number" placeholder='Doctor fees' required />
</div>

</div>

<div className='w-full lg:flex-1 flex flex-col gap-


4'>

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


<p>Speciality</p>
<select onChange={e =>
setSpeciality(e.target.value)} value={speciality} className='border
rounded px-2 py-2'>
<option value="General physician">General
physician</option>
<option
value="Gynecologist">Gynecologist</option>
<option
value="Dermatologist">Dermatologist</option>
<option
value="Pediatricians">Pediatricians</option>
<option
value="Neurologist">Neurologist</option>
<option
value="Gastroenterologist">Gastroenterologist</option>
</select>
</div>

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


<p>Degree</p>
<input onChange={e =>
setDegree(e.target.value)} value={degree} className='border rounded px-3
py-2' type="text" placeholder='Degree' required />
</div>

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


<p>Address</p>
<input onChange={e =>
setAddress1(e.target.value)} value={address1} className='border rounded
px-3 py-2' type="text" placeholder='Address 1' required />
<input onChange={e =>
setAddress2(e.target.value)} value={address2} className='border rounded
px-3 py-2' type="text" placeholder='Address 2' required />
</div>

</div>

</div>

<div>
<p className='mt-4 mb-2'>About Doctor</p>
<textarea onChange={e => setAbout(e.target.value)}
value={about} className='w-full px-4 pt-2 border rounded' rows={5}
placeholder='write about doctor'></textarea>
</div>

<button type='submit' className='bg-primary px-10 py-3


mt-4 text-white rounded-full'>Add doctor</button>

</div>

</form>
)
}

export default AddDoctor


import React, { useEffect } from 'react'
import { assets } from '../../assets/assets'
import { useContext } from 'react'
import { AdminContext } from '../../context/AdminContext'
import { AppContext } from '../../context/AppContext'

const AllAppointments = () => {

const { aToken, appointments, cancelAppointment, getAllAppointments } =


useContext(AdminContext)
const { slotDateFormat, calculateAge, currency } =
useContext(AppContext)

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

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='hidden sm:grid grid-cols-
[0.5fr_3fr_1fr_3fr_3fr_1fr_1fr] grid-flow-col py-3 px-6 border-b'>
<p>#</p>
<p>Patient</p>
<p>Age</p>
<p>Date & Time</p>
<p>Doctor</p>
<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>

</div>
)
}

export default AllAppointments


import React, { useContext, useEffect } from 'react'
import { assets } from '../../assets/assets'
import { AdminContext } from '../../context/AdminContext'
import { AppContext } from '../../context/AppContext'

const Dashboard = () => {

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) => (
<div className='flex items-center px-6 py-3 gap-3 hover:bg-
gray-100' key={index}>
<img className='rounded-full w-10' src={item.docData.image}
alt="" />
<div className='flex-1 text-sm'>
<p className='text-gray-800 font-
medium'>{item.docData.name}</p>
<p className='text-gray-600 '>Booking on
{slotDateFormat(item.slotDate)}</p>
</div>
{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>
</div>

</div>
)
}

export default Dashboard


import React, { useContext, useEffect } from 'react'
import { AdminContext } from '../../context/AdminContext'

const DoctorsList = () => {

const { doctors, changeAvailability , aToken , getAllDoctors} =


useContext(AdminContext)

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

return (
<div className='m-5 max-h-[90vh] overflow-y-scroll'>
<h1 className='text-lg font-medium'>All Doctors</h1>
<div className='w-full flex flex-wrap gap-4 pt-5 gap-y-6'>
{doctors.map((item, index) => (
<div className='border border-[#C9D8FF] rounded-xl max-w-56
overflow-hidden cursor-pointer group' key={index}>
<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-
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'
import { useContext, useEffect } from 'react'
import { DoctorContext } from '../../context/DoctorContext'
import { AppContext } from '../../context/AppContext'
import { assets } from '../../assets/assets'

const DoctorAppointments = () => {

const { dToken, appointments, getAppointments, cancelAppointment,


completeAppointment } = useContext(DoctorContext)
const { slotDateFormat, calculateAge, currency } =
useContext(AppContext)

useEffect(() => {
if (dToken) {
getAppointments()
}
}, [dToken])

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
? <p className='text-green-500 text-xs font-
medium'>Completed</p>
: <div className='flex'>
<img onClick={() => cancelAppointment(item._id)}
className='w-10 cursor-pointer' src={assets.cancel_icon} alt="" />
<img onClick={() => completeAppointment(item._id)}
className='w-10 cursor-pointer' src={assets.tick_icon} alt="" />
</div>
}
</div>
))}
</div>

</div>
)
}

export default DoctorAppointments


import React from 'react'
import { useContext } from 'react'
import { useEffect } from 'react'
import { DoctorContext } from '../../context/DoctorContext'
import { assets } from '../../assets/assets'
import { AppContext } from '../../context/AppContext'

const DoctorDashboard = () => {

const { dToken, dashData, getDashData, cancelAppointment,


completeAppointment } = useContext(DoctorContext)
const { slotDateFormat, currency } = useContext(AppContext)

useEffect(() => {

if (dToken) {
getDashData()
}

}, [dToken])

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.earning_icon} alt="" />
<div>
<p className='text-xl font-semibold text-gray-600'>{currency}
{dashData.earnings}</p>
<p className='text-gray-400'>Earnings</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) => (
<div className='flex items-center px-6 py-3 gap-3 hover:bg-
gray-100' key={index}>
<img className='rounded-full w-10'
src={item.userData.image} alt="" />
<div className='flex-1 text-sm'>
<p className='text-gray-800 font-
medium'>{item.userData.name}</p>
<p className='text-gray-600 '>Booking on
{slotDateFormat(item.slotDate)}</p>
</div>
{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>
: <div className='flex'>
<img onClick={() => cancelAppointment(item._id)}
className='w-10 cursor-pointer' src={assets.cancel_icon} alt="" />
<img onClick={() => completeAppointment(item._id)}
className='w-10 cursor-pointer' src={assets.tick_icon} alt="" />
</div>
}
</div>
))}
</div>
</div>

</div>
)
}

export default DoctorDashboard


import React, { useContext, useEffect, useState } from 'react'
import { DoctorContext } from '../../context/DoctorContext'
import { AppContext } from '../../context/AppContext'
import { toast } from 'react-toastify'
import axios from 'axios'

const DoctorProfile = () => {

const { dToken, profileData, setProfileData, getProfileData } =


useContext(DoctorContext)
const { currency, backendUrl } = useContext(AppContext)
const [isEdit, setIsEdit] = useState(false)

const updateProfile = async () => {

try {

const updateData = {
address: profileData.address,
fees: profileData.fees,
about: profileData.about,
available: profileData.available
}

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


'/api/doctor/update-profile', updateData, { headers: { dToken } })

if (data.success) {
toast.success(data.message)
setIsEdit(false)
getProfileData()
} else {
toast.error(data.message)
}

setIsEdit(false)

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

useEffect(() => {
if (dToken) {
getProfileData()
}
}, [dToken])

return profileData && (


<div>
<div className='flex flex-col gap-4 m-5'>
<div>
<img className='bg-primary/80 w-full sm:max-w-64
rounded-lg' src={profileData.image} alt="" />
</div>

<div className='flex-1 border border-stone-100 rounded-lg


p-8 py-7 bg-white'>
{/* ----- Doc Info : name, degree, experience -----
*/}

<p className='flex items-center gap-2 text-3xl font-


medium text-gray-700'>{profileData.name}</p>
<div className='flex items-center gap-2 mt-1 text-
gray-600'>
<p>{profileData.degree} -
{profileData.speciality}</p>
<button className='py-0.5 px-2 border text-xs
rounded-full'>{profileData.experience}</button>
</div>

{/* ----- Doc About ----- */}


<div>
<p className='flex items-center gap-1 text-sm
font-medium text-[#262626] mt-3'>About :</p>
<p className='text-sm text-gray-600 max-w-[700px]
mt-1'>
{
isEdit
? <textarea onChange={(e) =>
setProfileData(prev => ({ ...prev, about: e.target.value }))} type='text'
className='w-full outline-primary p-2' rows={8} value={profileData.about}
/>
: profileData.about
}
</p>
</div>

<p className='text-gray-600 font-medium mt-4'>


Appointment fee: <span className='text-gray-
800'>{currency} {isEdit ? <input type='number' onChange={(e) =>
setProfileData(prev => ({ ...prev, fees: e.target.value }))}
value={profileData.fees} /> : profileData.fees}</span>
</p>

<div className='flex gap-2 py-2'>


<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} /> :
profileData.address.line1}
<br />
{isEdit ? <input type='text' onChange={(e) =>
setProfileData(prev => ({ ...prev, address: { ...prev.address, line2:
e.target.value } }))} value={profileData.address.line2} /> :
profileData.address.line2}
</p>
</div>

<div className='flex gap-1 pt-2'>


<input type="checkbox" onChange={() => isEdit &&
setProfileData(prev => ({ ...prev, available: !prev.available }))}
checked={profileData.available} />
<label htmlFor="">Available</label>
</div>
{
isEdit
? <button onClick={updateProfile}
className='px-4 py-1 border border-primary text-sm rounded-full mt-5
hover:bg-primary hover:text-white transition-all'>Save</button>
: <button onClick={() => setIsEdit(prev =>
!prev)} className='px-4 py-1 border border-primary text-sm rounded-full
mt-5 hover:bg-primary hover:text-white transition-all'>Edit</button>
}

</div>
</div>
</div>
)
}

export default DoctorProfile


import axios from 'axios'
import React, { useContext, useState } from 'react'
import { DoctorContext } from '../context/DoctorContext'
import { AdminContext } from '../context/AdminContext'
import { toast } from 'react-toastify'

const Login = () => {

const [state, setState] = useState('Admin')

const [email, setEmail] = useState('')


const [password, setPassword] = useState('')

const backendUrl = import.meta.env.VITE_BACKEND_URL

const { setDToken } = useContext(DoctorContext)


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 '>
<p>Password</p>
<input onChange={(e) => setPassword(e.target.value)}
value={password} className='border border-[#DADADA] rounded w-full p-2
mt-1' type="password" required />
</div>
<button className='bg-primary text-white w-full py-2 rounded-md
text-base'>Login</button>
{
state === 'Admin'
? <p>Doctor Login? <span onClick={() => setState('Doctor')}
className='text-primary underline cursor-pointer'>Click here</span></p>
: <p>Admin Login? <span onClick={() => setState('Admin')}
className='text-primary underline cursor-pointer'>Click here</span></p>
}
</div>
</form>
)
}

export default Login


import React, { useContext } from 'react'
import { DoctorContext } from './context/DoctorContext';
import { AdminContext } from './context/AdminContext';
import { Route, Routes } from 'react-router-dom'
import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import Navbar from './components/Navbar'
import Sidebar from './components/Sidebar'
import Dashboard from './pages/Admin/Dashboard';
import AllAppointments from './pages/Admin/AllAppointments';
import AddDoctor from './pages/Admin/AddDoctor';
import DoctorsList from './pages/Admin/DoctorsList';
import Login from './pages/Login';
import DoctorAppointments from './pages/Doctor/DoctorAppointments';
import DoctorDashboard from './pages/Doctor/DoctorDashboard';
import DoctorProfile from './pages/Doctor/DoctorProfile';

const App = () => {

const { dToken } = useContext(DoctorContext)


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'
import { BrowserRouter } from 'react-router-dom'
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>,
)

You might also like