Our vision is to create a healthcare ecosystem where distance is no longer a barrier to quality care. The Virtual Clinic is not just a platform; it's a commitment to making healthcare accessible, affordable, and patient-centric. Join us as patient or doctor in shaping the future of healthcare by embracing technology to improve the overall healthcare experience for everyone involved.
Build Status⚒️
Project builds successfully and is fully functional. However, project is currently in development and improvements could be made.
Code Style 📜
The code style is enforced using eslint and prettier. The code style is enforced using pre-commit hooks and pre-commit github action.
Guests are allowed to use the site with limited functionality(role based)
Signup to the website
Receive a password reset email
Admin
View all Patients, joined Doctor and pending Doctors
Accept or reject requests to join the platform or add Another admins
Add,Edit and delete Health Packages
Doctor
Edit/update personal information (email, hourly rate, affiliation, etc.).
Change password with specific validation.
Upload Health records for his patients:
Upload and submit required documents upon registration (ID, Medical licenses, medical degree).
Upload/remove documents for medical history.
Appointment Management:
View a list of his patients.
Add available time slots for appointments.
Schedule a follow-up for a patient.
Accept or revoke a follow-up session request from a patient.
Reschedule and cancel an appointment for a patient.
Receive notifications of appointment changes.
Prescription Management:
Add/delete medicine to/from the prescription.
Add/update dosage for each medicine in the prescription.
Download prescriptions in PDF format.
Health Record Management:
Add new health records for a patient.
Communication:
Chat/video Chat with patients.
Financial Management:
View the amount accumulated in the wallet.
Receive payments for appointments.
Receive a refund in the wallet when canceling an appointment.
Patients:
Profile Management:
Edit/update personal information (email, date of birth, gender, etc.) and upload/remove medical history
Document Management:
Appointment Management:
View a list of all doctors along with their details.
View all available appointments of a selected doctor.
Select an appointment date and time.
View a list of all upcoming/past appointments.
Reschedule/Cancel an appointment for themselves or family members.
-Receive notifications of appointment changes.
Prescription Management:
View a list of all prescriptions and Choose to pay directly for the prescription items with a wallet or credit card.
Filter prescriptions based on date, doctor, or filled status.
Health Package Subscription:
View health package options and details and be able to subscribe or unsubscribe.
Family Management:
Add family members using name, National ID, age, gender, and relation.
Wallet Management:
View the amount in the wallet and be able to recieve refunds on cacelling an appointment .
Communication:
Chat/Video Chat with doctors.
Code Examples
patient (client)
React components are used to represent page named FamilyMemberForm for one of the patients as well as smaller reusable components in the frontend
constFamilyMemberForm=()=>{constsnackbarMessage=useContext(SnackbarContext);constnavigate=useNavigate();const[email,setEmail]=useState("");const[relation,setRelation]=useState("spouse");// Default to "spouse"constuser=useSelector((state)=>state.user);constrole=useSelector((state)=>state.user.role);consthandleSubmit=async(e)=>{e.preventDefault();// Check if the user ID is availableif(user&&user.id){try{// Make an API request using Axios to add the family memberconstresponse=awaitaxios.post(${API_URL}/patient/addFamilyLink/${email}/${user.id},{
relation,});if(response){snackbarMessage("You have successfully edited","success");navigate("/Home");}else{snackbarMessage(error: ${response}hasoccurred, "error");}console.log("Family member added successfully:",response.data);}catch(error){console.error("Error adding family member:",error.message);}}else{console.error("User ID not available.");}};return(role==="patient"?(<divstyle={{width: "80%",margin: "0 auto",padding: "20px",backgroundColor: "#f9f9f9",borderRadius: "8px",boxShadow: "0 0 10px rgba(0, 0, 0, 0.1)",}}><h2>link Family Member</h2><formonSubmit={handleSubmit}style={{display: "flex",flexDirection: "column",gap: "10px"}}><label>
Email:
<inputtype="email"value={email}onChange={(e)=>setEmail(e.target.value)}requiredstyle={{width: "100%",padding: "8px",borderRadius: "4px",border: "1px solid #ccc",}}/></label><label>
Relation:
<selectvalue={relation}onChange={(e)=>setRelation(e.target.value)}requiredstyle={{width: "100%",padding: "8px",borderRadius: "4px",border: "1px solid #ccc",}}><optionvalue="spouse">Spouse</option><optionvalue="child">Child</option></select></label><buttontype="submit"style={{width: "30%",padding: "10px",backgroundColor: "#007bff",color: "#fff",border: "none",borderRadius: "4px",postion:"relative",maraginLeft:"auto",cursor: "pointer",transition: "background-color 0.3s ease",}}>
link Family Member
</button></form><IconButtonsize="large"edge="start"color="inherit"aria-label="home"sx={{mr: 2}}onClick={()=>{window.location.href='/Home';}}><HomeIcon/></IconButton></div>): (<><Linkto="/Login"sx={{left: "100%"}}><Typographyvariant="h6"noWrapcomponent="div"sx={{flexGrow: 1,display: {xs: "none",sm: "flex"},fontSize: "20px",maragin: "auto",}}>
Login
</Typography></Link></>));};exportdefaultFamilyMemberForm;
Another example at patient is Download page
This code defines a React functional component named DownloadPage. Here's what it does: The main purpose of this component is to capture the content of a specified HTML element, convert it to an image using html2canvas, and then save that image as a PDF file using jsPDF. The code:
Another example at doctor is My appointments
Take a specific date and status and filter schedule about it :
consthandleFilterClick=()=>{// Filter appointments based on selectedDate and selectedStatusconstfilteredAppointments=appointments.filter((appointment)=>{constisDateMatch=!selectedDate||appointment.date===selectedDate.toISOString().split('T')[0];constisStatusMatch=selectedStatus==='Status'||appointment.status===selectedStatus;returnisDateMatch&&isStatusMatch;});// Filter by custom date inputconstcustomDateFilteredAppointments=customDate
? filteredAppointments.filter((appointment)=>appointment.date===customDate)
: filteredAppointments;// Set the filtered appointments to displaysetAppointments(customDateFilteredAppointments);};return(<div><Paperelevation={3}style={{padding: '20px'}}><i><IconButtoncolor="primary"aria-label="Back to Home"style={{position: 'absolute',bottom: '10px',right: '10px'}}onClick={()=>{navigate(-1);}}><HomeIcon/></IconButton></i><h2>Appointments</h2><divstyle={{display: 'flex',alignItems: 'center',marginBottom: '20px'}}><LocalizationProviderdateAdapter={AdapterDateFns}><DatePickerlabel="Select Date"value={selectedDate}onChange={(newDate)=>handleDateChange(newDate)}renderInput={(params)=>(<TextField{...params}variant="outlined"/>)}inputFormat="yyyy-MM-dd"inputProps={{endAdornment: <CalendarTodayIcon/>}}/></LocalizationProvider><Selectlabel="Status"value={selectedStatus}onChange={handleStatusChange}style={{marginLeft: '20px'}}><MenuItemvalue="completed">completed</MenuItem><MenuItemvalue="upcoming">upcoming</MenuItem><MenuItemvalue="cancelled">cancelled</MenuItem><MenuItemvalue="rescheduled">rescheduled</MenuItem></Select><TextFieldlabel="Custom Date"variant="outlined"value={customDate}onChange={handleCustomDateChange}style={{marginLeft: '20px'}}inputProps={{type: 'date'}}/><IconButtononClick={handleFilterClick}color="primary"><FilterListIcon/></IconButton></div><TableContainer><Table><TableHead><TableRow><TableCell>Finished</TableCell><TableCell>Date</TableCell><TableCell>Patient</TableCell><TableCell>Status</TableCell><TableCell>View</TableCell></TableRow></TableHead><TableBody>{appointments.map((appointment)=>(<TableRowkey={appointment._id}><TableCell></TableCell><TableCell>{appointment.startDate}</TableCell><TableCell>{appointment.pID.name}</TableCell><TableCell>{appointment.status}</TableCell><TableCell>{/* Use Link to navigate to the patient details page */}<Linkto={"/myappointments/patientdetails"}><Buttonvariant="outlined"color="primary">
View
</Button></Link></TableCell></TableRow>))}</TableBody></Table></TableContainer></Paper></div>);}exportdefaultAppointments;
server
1-At server this one example of method in patient routes that add family member and it is called addFamilyMemberByEmailAndId:
asyncfunctionaddFamilyMemberByEmailAndId(email,patientId,relation,type){try{consolr.log("hi");constfamilyMember=null;// Find the patient with the provided emailif(type==="email"){familyMember=awaitPatient.findOne({ email });}else{familyMember=awaitPatient.findOne({mobile: email});}console.log(familyMember);if(!familyMember){thrownewError("Family member not found with the provided email.");}// Find the patient with the provided IDconstpatientToUpdate=awaitPatient.findById(patientId);consolr.log(patientId);if(!patientToUpdate){thrownewError("Patient not found with the provided ID.");}// Check if the family member is already in the family arrayconstisAlreadyFamily=patientToUpdate.family.some((member)=>member.email===email);if(isAlreadyFamily){thrownewError("Family member is already added to the patient.");}constag=calculateAge(familyMember.dBirth);// Add the family member to the patient's family array with the provided relationpatientToUpdate.family.push({fullName: familyMember.name,NID: 12345678912,age: ag,gender: familyMember.gender,
relation,pid: familyMember._id,// Assign the related patient's ID to pid});// Save the updated patientawaitpatientToUpdate.save();returnpatientToUpdate;}catch(error){throwerror;}}
2-Another example at doctor controller which is try to find doctor wallet
constviewWallet=async(req,res)=>{const{doctorId}=req.params;try{constdoctor=awaitDoctor.findById(doctorId);if(!doctor){returnres.status(404).json({error: "Doctor not found"});}if(!doctor.wallet){// If the patient doesn't have a wallet attribute, add it with a value of zerodoctor.wallet=0;awaitdoctor.save();}constwalletAmount=doctor.wallet;res.status(200).json({message:" wallet amount is fetched successfully",doctor: doctor,wallet:walletAmount})}catch(error){res.status(500).json({error: "Internal Server Error"});}}
3-last one at patient controller that try to find available slot for doctor
constfreeAppiontmentSlot=async(req,res)=>{try{const{ doctorId }=req.params;// Validate the 'patientId' parameterif(!doctorId){returnres.status(400).json({error: "doctorId ID is required"});}// Find the patient by patientIdconstdoctor=awaitDoctor.findById(doctorId);if(!doctor){returnres.status(404).json({error: "Patient not found"});}// Fetch details about each free AppointmentconstAppointments=awaitAppointment.find({drID: doctorId,status:"Not_Reserved"}).populate("drID");returnres.status(200).json({message: "Free Appointments retrieved successfully",
Appointments,});}catch(error){console.error("Error retrieving Appointments:",error);returnres.status(500).json({error: "Internal Server Error"});}};
Installation ⬇️
Make sure you have npm and node installed
Go into the server directory and run npm i
Create a .env file, using .env.example as a template
Run node .
From the parent directory, go into the client directory and run npm i
Run npm start
`
API Refrences
Admin API Refrences
Gets a specific user from the database
GET /api/getUser
Gets all the admins from the database
GET /api/viewAdmin
Gets all the medicines in the database
GET /api/viewMedicine
Creates an admin in the database
POST /api/createAdmin
Gets all the doctors whose their working status are pending (their contarct is still pending)
GET /api/viewPendDr
Gets all the doctors whose their working status are accepted (their contarct is accepted)
GET /api/viewJoinedDr
Gets all the patients in the database
GET /api/viewPatients
Delete patient
DELETE /api/deletePatient/${id}
Parameter
Type
Description
id
String
Id of the patient to delete
DELETE doctor
DELETE /api/deleteDoctor/${id}
Parameter
Type
Description
id
String
Id of the doctor to delete
DELETE admin
DELETE /api/deleteAdmin/${id}
Parameter
Type
Description
id
String
Id of the admin to delete
adds a health package in the database
POST /api/addPack
DELETE health package
DELETE /api/deletePack/${id}
Parameter
Type
Description
id
String
Id of the health package to delete
UPDATE health package
PUT /api/updatePack/${id}
Parameter
Type
Description
id
String
Id of the health package to update
Gets all the health packages in the database
GET /api/viewHealthP
Sends an accept email to a doctor for his/her contract
POST /api/sendAcceptEmail
Sends a reject email to a doctor for his/her contract
POST /api/sendRejectEmail
Patient API Refrences
Creates an appointment for a specific patient with a specific doctor
POST /createAppointment/:patientID
Parameter
Type
Description
patientID
String
Id of the patient
ADDS a patient to the database
POST /api/addPatient
ADDS a family member of a specific patient to the database
POST /api/addFamilyMember/:patientId
Parameter
Type
Description
patientId
String
Id of the patient
VIEWS family members of a specific patient
GET /api/viewFamilyMembers/:patientId
Parameter
Type
Description
patientId
String
Id of the patient
VIEWS ALL THE DOCOTRS to a specific Patient with their rates based on the patient's subscribed health package
GET /api/viewDoctors/:patientId
Parameter
Type
Description
patientId
String
Id of the Patient
GETS the doctors by specifying their NAME OR SPECIALITY
GET /api/searchDoctorsByNameOrspeciality/:patientId
Parameter
Type
Description
patientId
String
Id of the patient (USELESS)
GETS the doctors by specifying their SPECIALITY AND REQUESTED TIME
GET /api/searchDoctorsBySpecialtyOrAvailability/:patientId"
Parameter
Type
Description
patientId
String
Id of the patient(USELESS)
VIEWS the prescriptioins of a specific patient
GET /api/viewPrescriptions/:patientId
Parameter
Type
Description
patientId
String
Id of the patient
reschedule an appointment of a patient with a doctor
PUT /api/rescheduleAppointment/:appointmentId
Parameter
Type
Description
appointmentId
String
Id of an appointment
filter appointments of a specific patient based on the start date , end date , and status of the appointment
GET /api/patientFilterAppointments
GETS all the appointments of a specific patient
GET /api/viewAppoints/:patientId"
Parameter
Type
Description
patientId
String
Id of the patient
GETS all the appointments of a doctor that has status Not_Reserved
GET /api/freeAppiontmentSlot/:doctorId
Parameter
Type
Description
doctorId
String
Id of the doctor
Reserve an appointemnt slot for a patient chnaging its status to upcoming
PATCH /api/reserveAppointmentSlot/:AppointmentId
Parameter
Type
Description
AppointmentId
String
Id of the appointment
GETS a user from the databse by specifying their username
GET /api/getUser
GETS all doctors in the database
GET /api/getAlldoctors
GETS a specific prescription from the database
GET /api/viewSpecificPrescription/:id
Parameter
Type
Description
id
String
Id of the prescription
GETS all the appointments of a doctor that has status Not_Reserved
GET /api/appointmentPatients/:doctorId
Parameter
Type
Description
doctorId
String
Id of the doctor
GETS all the health records of a patient
GET /api/viewPatientHealthRecords/:patientid
Parameter
Type
Description
patientid
String
Id of the patient
make patient subscribe to a health package using wallet
Create a .env file using .env.example as a template,
run node
Finally, navigate to the client directory and run npm i,
then npm start to start the application.
This will start the client server, and you can access the website by navigating to the URL returned from npm start in your web browser.
This will allow you to access the full range of features available on El7a2ni Virtual clinic.
Contribute ➕
Fork the repository
Clone the repository
Install dependencies
Create a new branch
Make your changes
Commit and push your changes
Create a pull request
Wait for your pull request to be reviewed and merged