- π Motivation
- 𧱠Build Status
- π¨ Code Style
- βοΈ Tech and Frameworks used
- π₯ Features & Screenshots
- π» Code Examples
- βοΈ Installation
- π API Reference
- π§ͺ Tests
- π§π»βπ« How to Use
- π€ Contribute
- Β©οΈ Credits
- π License
Welcome to Copilot & Sons El7a2ny Clinic, a cutting-edge virtual clinic management software. This project is driven by the vision to enhance the healthcare experience for clinics, doctors, and patients by introducing a comprehensive platform that simplifies and automates key interactions within the healthcare ecosystem.
- This project is under development and should not be used in a production settings
- Check Issues for a list of all the reported issues
- More automated tests should be added in the future
- More documentation should be added
We use Prettier and ESLint to enforce a consistent code style. We use an edited version of the default ESLint TypeScript config. You can check the config in the .eslintrc.js file.
Useful Commands
- Check formatting using Prettier
npm run format
- And then fix formatting using Prettier
npm run format:fix
- Check linting using ESLint
npm run lint
- And then fix linting using ESLint
npm run lint:fix
- Check compilation of all subpackages using TypeScript
npm run compile:all
- NodeJs
- Express
- ReactJs
- MongoDB
- Mongoose
- TypeScript
- Docker
- Docker Compose
- ESLint
- Prettier
- MUI
- React Router
- React Hook Form
- React Query
- Formik
- Toastify
- Socket.io
- Firebase Storage
- NodeMailer
- JsonWebToken
- Bcrypt
- Postman
User Registration π
Administrator Functions π©βπΌ
- Add/remove another administrator. - Manage doctors and patients. - Accept or reject doctor registration requests. - View information uploaded by doctors.Health Packages πΌ
- Administrators can add/update/delete health packages with different price ranges.Account Management π
- Change password. - Reset forgotten password via email. - Edit/update email, hourly rate, or affiliation.Doctor Functions π©Ί
- Accept employment contract. - Add available time slots for appointments. - View information and health records of registered patients. - View a list of all patients.Patient Functions π€
- Add family members and link accounts. - Pay for appointments using wallet or credit card. - Subscribe to health packages for self and family. - View subscribed health packages and subscription status. - Cancel a subscription.Appointment Management π
- Filter appointments by date/status. - View upcoming/past appointments. - Patient can reschedule or cancel appointments. - Doctor can reschedule appointments for patients.Prescription Management π
- Doctor can add/delete medicine to/from prescriptions. - Doctor can add/update dosage for each medicine. - Patients can view and filter prescriptions based on various criteria.Wallet Management π°
- Receive a refund in the wallet for canceled appointments. - View the amount in the wallet.Communication π¬
- Patients and doctors can chat with each other. - Doctors and Patients can start/end video calls.BE Routes Example
router.use('/auth', authRouter)
router.use('/doctors', doctorsRouter)
router.use('/debug', debugRouter)
router.use('/prescriptions', prescriptionsRouter)
router.use('/family-members', familyMemberRouter)
router.use('/health-packages', healthPackagesRouter)
router.use('/patients', patientRouter)
router.use('/appointment', appointmentsRouter)
router.use('/admins', asyncWrapper(allowAdmins), adminRouter)
BE Add Health Package Controller Example
export const healthPackagesRouter = Router()
healthPackagesRouter.post(
'/',
asyncWrapper(allowAdmins),
validate(CreateHealthPackageRequestValidator),
asyncWrapper<createHealthPackageRequest>(async (req, res) => {
const healthPackage = await addHealthPackages(req.body)
res.send({
name: healthPackage.name,
id: healthPackage.id,
pricePerYear: healthPackage.pricePerYear,
sessionDiscount: healthPackage.sessionDiscount,
medicineDiscount: healthPackage.medicineDiscount,
familyMemberSubscribtionDiscount:
'healthPackage'.familyMemberSubscribtionDiscount,
} satisfies AddHealthPackageResponse)
})
)
BE Add Health Package Service Example
export async function addHealthPackages(
request: createHealthPackageRequest
): Promise<HydratedDocument<HealthPackageDocument>> {
const healthPackage = await HealthPackageModel.create({
name: request.name,
pricePerYear: request.pricePerYear,
sessionDiscount: request.sessionDiscount,
medicineDiscount: request.medicineDiscount,
familyMemberSubscribtionDiscount: request.familyMemberSubscribtionDiscount,
})
return healthPackage
}
BE Health Package Model Example
import mongoose from 'mongoose'
const Schema = mongoose.Schema
const healthPackageSchema = new Schema(
{
name: { type: String, required: true, unique: true },
pricePerYear: { type: Number, required: true },
sessionDiscount: { type: Number, required: true },
medicineDiscount: { type: Number, required: true },
familyMemberSubscribtionDiscount: {
type: Number,
required: true,
},
},
{ timestamps: true }
)
export type HealthPackageDocument = mongoose.InferSchemaType<
typeof healthPackageSchema
>
export const HealthPackageModel = mongoose.model(
'HealthPackage',
healthPackageSchema
)
Add Health Package Validator Example
import * as zod from 'zod'
export const CreateHealthPackageRequestValidator = zod.object({
name: zod.string().min(1),
pricePerYear: zod.number(),
sessionDiscount: zod.number(),
medicineDiscount: zod.number(),
familyMemberSubscribtionDiscount: zod.number(),
})
Health Package TypeScript Types Example
export type createHealthPackageRequest = z.infer<
typeof CreateHealthPackageRequestValidator
>
export type UpdateHealthPackageRequest = z.infer<
typeof UpdateHealthPackageRequestValidator
>
export interface HealthPackageResponseBase {
name: string
id: string
pricePerYear: number
sessionDiscount: number
medicineDiscount: number
familyMemberSubscribtionDiscount: number
}
export interface UpdateHealthPackageResponse
extends HealthPackageResponseBase {}
export interface AddHealthPackageResponse extends HealthPackageResponseBase {}
FE Admin Dashboard Routes Example
export const adminDashboardRoutes: RouteObject[] = [
{
element: <AdminDashboardLayout />,
children: [
{
path: '',
element: <AdminDashboardHome />,
},
{
path: 'change-password',
element: <ChangePassword />,
},
{
path: 'pending-doctors',
element: <PendingDoctors />,
},
{
path: 'pending-doctors/:username',
element: <PendingDoctorDetails />,
},
{
path: 'health-packages',
element: <HealthPackages />,
},
{
path: 'add-health-package',
element: <AddHealthPackage />,
},
{
path: 'update-health-package/:id',
element: <UpdateHealthPackage />,
},
{
path: 'add-admin',
element: <AddAdmin />,
},
{
path: 'add-admin',
element: <AddAdmin />,
},
{
path: 'users',
element: <Users />,
},
],
},
]
FE Add Health Package Page Example
export function AddHealthPackage() {
const navigate = useNavigate()
return (
<ApiForm<createHealthPackageRequest>
fields={[
{ label: 'Name', property: 'name' },
{
label: 'Price Per Year',
property: 'pricePerYear',
valueAsNumber: true,
},
{
label: 'Session Discount Percentage',
property: 'sessionDiscount',
valueAsNumber: true,
},
{
label: 'Medicine Discount Percentage',
property: 'medicineDiscount',
valueAsNumber: true,
},
{
label: 'Family Member Subscribtion Discount Percentage',
property: 'familyMemberSubscribtionDiscount',
valueAsNumber: true,
},
]}
validator={CreateHealthPackageRequestValidator}
successMessage="Added health package successfully"
action={(data) => addHealthPackage(data)}
onSuccess={() => {
navigate('/admin-dashboard/health-packages')
}}
/>
)
}
mkdir Copilot-and-Sons
cd Copilot-and-Sons
- Clone this repo + pharmacy repo
git clone https://github.com/advanced-computer-lab-2023/Copilot-and-Sons-Clinic
git clone https://github.com/advanced-computer-lab-2023/Copilot-and-Sons-Pharmacy
- Install dependencies for clinic
cd Copilot-and-Sons-Clinic
npm install
- Install dependencies for pharmacy
cd ../Copilot-and-Sons-Pharmacy
npm install
Admin Endpoints
POST /admins
- Add a new admin- Request body
username
: stringpassword
: stringemail
: string
- Response: The created admin
- Request body
GET /admins/get-users
- Get all users- Response: A list of all users
[ { username: string, type: string } ]
DELETE /admins/username/:id
- Delete a user by username
Appointment Endpoints
GET /appointment/filter
- Returns a list of all appointments- Response Body
[ { 'id': string, 'patientID': string, 'doctorID': string, 'doctorName': string, 'doctorTimes': string[], 'date': string, 'familyID': string, 'reservedFor': string 'status': AppointmentStatus } ]
POST /appointment/makeappointment
- Reserve an appointment- Request Body
{ 'doctorid': string, 'date': Date | null, 'familyID': string, 'reservedFor': string, 'toPayUsingWallet': number, 'sessionPrice': number }
- Response Body
{ 'id': string, 'patientID': string, 'doctorID': string, 'doctorName': string, 'doctorTimes': string[], 'date': string, 'familyID': string, 'reservedFor': string 'status': AppointmentStatus }
POST /appointment/delete/:id
- Delete an appointment- Request Body
{ 'appointmentId': string, 'cancelledByDoctor': boolean }
POST /appointment/reschedule
- Reschedule an appointment - Request Body{ appointment: string, rescheduleDate: string }
- Response Body{ 'id': string, 'patientID': string, 'doctorID': string, 'doctorName': string, 'doctorTimes': string[], 'date': string, 'familyID': string, 'reservedFor': string 'status': AppointmentStatus }
Auth Endpoints
-
POST /auth/login
- Authenticate a user and retrieve an access token.- Request Body:
{ "username": "string", "password": "string" }
- Response Body:
{ "token": "string" }
-
POST /auth/register-patient
- Register a patient and obtain an access token.- Request Body:
{ "username": "string", "name": "string", "email": "string", "password": "string", "dateOfBirth": "string", "gender": "string", "mobileNumber": "string", "emergencyContact": { "fullName": "string", "mobileNumber": "string" } }
- Response Body:
{ "token": "string" }
-
GET /auth/me
- Retrieve information about the currently authenticated user. -
Response Body:
{ "id": "string", "username": "string", "name": "string", "email": "string", "dateOfBirth": "string", "gender": "string", "mobileNumber": "string", "emergencyContact": { "fullName": "string", "mobileNumber": "string" } }
-
POST /patient/requestOtp
- Request to send OTP for forgetting password- Request Body:
{ email: string }
- Response Body:: N/A
-
POST /patient/verifyOtp
- Verify OTP for forgetting password- Request Body:
{ email: string, otp: string, }
- Response Body:: N/A
-
POST /patient/updatePassword
- Update patient password after forgetting password- **Request Body:** ``` { email: string, newPassword: string } ``` - **Response Body:**: N/A
Chat Endpoints
-
`POST '/chats/get-all' - Get all chats for a user
- Request Body:
{ 'username': string // Could be username of a patient, doctor, or admin }
- Response Body
{ 'id': string 'users': Array<{ 'id': string 'username': string 'name': string 'email': string 'type': UserType }> 'createdAt': string 'lastMessage': string 'hasUnreadMessages': boolean }
-
POST /chats/create-or-get
- Creates a new chat or gets one if it already exists between the users- Request Body
{ 'initiator': string 'receiver': string }
- Reponse Body:
string
-> ID of the created chat
- Request Body
-
POST /chats/get-by-id
- Gets a chat by its id- Request Body
{ 'chatId': string 'readerUsername': string }
- Reponse Body
{ 'id': string users: Array<{ 'id': string 'username': string 'name': string 'email': string 'type': UserType }> 'messages': Array<{ 'id': string 'sender': string 'senderType': UserType 'senderDetails': { 'name': string 'email': string } 'content': string 'createdAt': string }> 'createdAt': string 'lastMessage': string 'hasUnreadMessages': boolean }
- Request Body
-
POST /chats/send-message
- Sends a message- Request Body
{ 'chatId': string 'senderUsername': string 'content': string }
- Reponse Body: N/A
- Request Body
-
POST '/chats/mark-as-read'
- Marks a chat as being read- Request Body
{ 'chatId': string 'username': string }
- Reponse Body: N/A
- Request Body
Doctors Endpoints
-
PATCH '/doctors/updateDoctor/:username'
- Updates a doctor information- Request Body
{ 'name': string, 'email': string, 'dateOfBirth': string, 'hourlyRate': number, 'affiliation': string, 'educationalBackground': string, }
- Reponse Body:
{ 'id': string 'username': string 'name': string 'email': string 'dateOfBirth': Date 'hourlyRate': number 'affiliation': string 'educationalBackground': string 'speciality': string 'requestStatus': DoctorStatus }
- Request Body
-
GET /doctors/:username
- Gets doctor information by username- Request Body: N/A
- Reponse Body:
{ 'id': string 'username': string 'name': string 'email': string 'dateOfBirth': Date 'hourlyRate': number 'affiliation': string 'educationalBackground': string 'speciality': string 'requestStatus': DoctorStatus 'contractStatus': ContractStatus 'availableTimes': [Date] 'employmentContract': [string] 'documents': [string] 'wallet': number }
-
GET /doctors/pending
- Gets pending doctors requests- Request Body: N/A
- Reponse Body:
{ 'doctors': Array<{ 'id': string 'username': string 'name': string 'email': string 'dateOfBirth': Date 'hourlyRate': number 'affiliation': string 'educationalBackground': string 'speciality': string 'requestStatus': DoctorStatus }> }
-
GET /doctors/approved
- Gets approved doctors- Request Body: N/A
- Reponse Body:
{ 'doctors': Array<{ 'id': string 'username': string 'name': string 'email': string 'dateOfBirth': Date 'hourlyRate': number 'affiliation': string 'educationalBackground': string 'speciality': string 'requestStatus': DoctorStatus }> }
-
GET /doctors/approved/:id
- Gets approved doctor by id- Request Body: N/A
- Reponse Body:
{ 'id': string 'username': string 'name': string 'email': string 'dateOfBirth': Date 'hourlyRate': number 'affiliation': string 'educationalBackground': string 'speciality': string 'requestStatus': DoctorStatus 'availableTimes': [Date] 'sessionRate': number 'hasDiscount': boolean 'hourlyRateWithMarkup': number }
-
PATCH /doctors/acceptDoctorRequest/:id
- Accept a doctor by id- Request Body: N/A
- Reponse Body:
{ 'id': string 'username': string 'name': string 'email': string 'dateOfBirth': Date 'hourlyRate': number 'affiliation': string 'educationalBackground': string 'speciality': string 'requestStatus': DoctorStatus 'availableTimes': [Date] 'sessionRate': number 'hasDiscount': boolean 'hourlyRateWithMarkup': number }
-
PATCH /doctors/rejectDoctorRequest/:id
- Reject a doctor by id- Request Body: N/A
- Reponse Body:
{ 'id': string 'username': string 'name': string 'email': string 'dateOfBirth': Date 'hourlyRate': number 'affiliation': string 'educationalBackground': string 'speciality': string 'requestStatus': DoctorStatus 'availableTimes': [Date] 'sessionRate': number 'hasDiscount': boolean 'hourlyRateWithMarkup': number }
-
PATCH /doctors/addAvailableTimeSlots
- Add available time slots for doctor- Request Body:
{ 'time': Date, }
- Reponse Body:
{ 'id': string 'username': string 'name': string 'email': string 'dateOfBirth': Date 'hourlyRate': number 'affiliation': string 'educationalBackground': string 'speciality': string 'requestStatus': DoctorStatus 'availableTimes': [Date] }
- Request Body:
-
PATCH /doctors/acceptEmploymentContract
- Accept employment contract- Request Body: N/A
- Reponse Body:
{ 'id': string 'username': string 'name': string 'email': string 'dateOfBirth': Date 'hourlyRate': number 'affiliation': string 'educationalBackground': string 'speciality': string 'requestStatus': DoctorStatus 'contractStatus': ContractStatus 'availableTimes': [Date] 'employmentContract': [string] 'documents': [string] }
-
PATCH /doctors/rejectEmploymentContract
- Reject employment contract- Request Body: N/A
- Reponse Body:
{ 'id': string 'username': string 'name': string 'email': string 'dateOfBirth': Date 'hourlyRate': number 'affiliation': string 'educationalBackground': string 'speciality': string 'requestStatus': DoctorStatus 'contractStatus': ContractStatus 'availableTimes': [Date] 'employmentContract': [string] 'documents': [string] }
-
GET /doctors/wallet/:username
- Get doctor's wallet money- Request Body: N/A
- Reponse Body:
{ 'money': number }
-
POST /doctors/for-patient
- Get doctor for patient- Request Body:
{ 'patientUsername': string }
- Reponse Body:
[ { 'id': string 'username': string 'name': string } ]
- Request Body:
-
POST /auth/request-doctor'
- Request to register as a doctor- Request Body:
{ 'name': string 'email': string 'username': string 'password': string 'dateOfBirth': string 'hourlyRate': number 'affiliation': string 'educationalBackground': string 'speciality': string 'documents': File[] }
- Reponse Body:
{ 'id': string 'username': string 'name': string 'email': string 'dateOfBirth': Date 'hourlyRate': number 'affiliation': string 'educationalBackground': string 'speciality': string 'requestStatus': DoctorStatus }
- Request Body:
-
POST /patients/uploadHealthRecords/:id'
- Upload health record for a patient- Request Body:
{ HealthRecord: File[] }
- Reponse Body:
{ 'id': string, 'username': string, 'name': string, 'email': string, 'mobileNumber': string, 'dateOfBirth': Date, 'gender': Gender, 'emergencyContact': { 'name': string 'mobileNumber': string }, 'notes': string[] }
- Request Body:
-
POST /patients/deleteHealthRecord/:id'
- Delete health record for a patient- Request Body:
{ url: string // URL to delete }
- Reponse Body: N/A
- Request Body:
-
GET /patients/getMedicalHistory/:id
- Get medical history of patient- Request Body: N/A
- Reponse Body:
[string]
-
PATCH /doctors/acceptFollowupRequest/:id
- Accept a followup request by id of the request- Request Body: N/A
- Reponse Body: N/A
-
PATCH /doctors/rejectFollowupRequest/:id
- Reject a followup request by id of the request - Request Body: N/A - Reponse Body: N/A
Family Members Endpoints
-
GET /family-members/mine
- Get family members of currently logged in user- Request Body: N/A
- Reponse Body:
{ 'id': string 'name': string 'nationalId': string 'age': number 'gender': Gender 'relation': Relation 'healthPackage': { 'name'?: string 'renewalDate'?: string 'id'?: string } 'healthPackageHistory': [ { package: string; date: Date } ] //has the name not id }
-
GET /family-members/linking-me
- Get names of users linking me as family member- Request Body: N/A
- Reponse Body:
[string]
-
GET /family-members/:id
- Get family member details by id- Request Body: N/A
- Reponse Body:
{ 'familyMember': { 'id': string 'name': string 'nationalId': string 'age': number 'gender': Gender 'relation': Relation 'healthPackage': { 'name'?: string 'renewalDate'?: string 'id'?: string } 'healthPackageHistory': [ { package: string; date: Date } ] //has the name not id } 'patient': { 'id': string, 'username': string, 'name': string, 'email': string, 'mobileNumber': string, 'dateOfBirth': Date, 'gender': Gender, 'emergencyContact': { 'name': string 'mobileNumber': string }, 'notes': string[] } }
-
POST /family-members/:username:
- Add a family member to patient that has certain username- Request Body:
{ name: string, nationalId: string, age: number, gender: string, relation: string, }
- Reponse Body: N/A
- Request Body:
-
POST /family-members/link
- Link a family member- Request Body:
{ 'email'?: string, 'phonenumber'?: string, 'relation': string, }
- Reponse Body:
{ 'id': string 'name': string 'nationalId': string 'age': number 'gender': Gender 'relation': Relation 'healthPackage': { 'name'?: string 'renewalDate'?: string 'id'?: string } 'healthPackageHistory': [ { package: string; date: Date } ] //has the name not id }
- Request Body:
-
GET /family-members/mine/linked
- Get linked family members of current user - Request Body: N/A - Reponse Body:{ 'id': string 'patientId': string 'username': string 'mobileNumber': string 'email': string 'dateOfBirth': string 'name': string 'gender': string 'relation': Relation 'healthPackage': { 'name': string 'id': string } }
Health Packages Endpoints
-
POST /health-packages/for-patient
- Get health packages for patient- Request Body:
{ 'patientId': string, 'isFamilyMember': boolean, // Whether the patient is a family member or an actual patient }
- Reponse Body:
[ { 'name': string 'id': string 'pricePerYear': number 'sessionDiscount': number 'medicineDiscount': number 'familyMemberSubscribtionDiscount': number 'discountedPricePerYear': number } ]
- Request Body:
-
GET /health-packages
- Get all health packages- Request Body: N/A
- Reponse Body:
[ { 'name': string 'id': string 'pricePerYear': number 'sessionDiscount': number 'medicineDiscount': number 'familyMemberSubscribtionDiscount': number } ]
-
GET /health-packages/:id
- Get a health package by id- Request Body: N/A
- Reponse Body:
{ 'name': string 'id': string 'pricePerYear': number 'sessionDiscount': number 'medicineDiscount': number 'familyMemberSubscribtionDiscount': number }
-
PATCH /health-packages/:id
- Update a health package by id- Request Body:
{ name: string, pricePerYear: number, sessionDiscount: number, medicineDiscount: number, familyMemberSubscribtionDiscount: number, }
- Reponse Body:
string
-> ID of the updated health package
- Request Body:
-
POST /health-packages
- Create a health package- Request Body:
{ name: string, pricePerYear: number, sessionDiscount: number, medicineDiscount: number, familyMemberSubscribtionDiscount: number, }
- Reponse Body:
{ 'name': string 'id': string 'pricePerYear': number 'sessionDiscount': number 'medicineDiscount': number 'familyMemberSubscribtionDiscount': number }
- Request Body:
-
DELETE /health-packages/:id
- Delete a health package- Request Body: N/A
- Reponse Body:
string
-> ID of the deleted health package
-
GET /health-packages/isPackageHasSubscribers/:id
- Check if a health package has subscribers- Request Body: N/A
- Reponse Body:
boolean
-
PATCH /health-packages/wallet/subscriptions
- Subscribe to a health package using wallet-
Request Body:
{ // patientId or familyMemberId for the person that should be subscribed to the health package 'subscriberId': string // The person that is paying for the subscription 'payerUsername': string // Indicates whether the subscribee is a the id for FamilyMember or Patient 'isFamilyMember': boolean 'healthPackageId': string }
-
Reponse Body: N/A
-
-
PATCH /health-packages/credit-card/subscriptions
- Subscribe to a health package using credit card-
Request Body:
{ // patientId or familyMemberId for the person that should be subscribed to the health package 'subscriberId': string // The person that is paying for the subscription 'payerUsername': string // Indicates whether the subscribee is a the id for FamilyMember or Patient 'isFamilyMember': boolean 'healthPackageId': string }
-
Reponse Body: N/A
-
-
POST /health-packages/unsubscribe
- Unsubscribe to a health package using credit card- Request Body:
{ 'subscriberId': string 'payerUsername': string 'isFamilyMember': boolean }
- Reponse Body: N/A
- Request Body:
-
POST /health-packages/subscribed
- Get health package of user- Request Body:
{ 'patientId': string // patientId or familyMemberId 'isFamilyMember': boolean }
- Reponse Body:
{ healthPackage: { 'name': string 'id': string 'pricePerYear': number 'sessionDiscount': number 'medicineDiscount': number 'familyMemberSubscribtionDiscount': number 'renewalDate': string 'remainingMonths': number }, }
- Request Body:
-
POST /health-packages/patient-cancelled
- Get cancelled health packages of user- Request Body:
{ 'id': string // patientId or familyMemberId 'isFamilyMember': boolean }
- Reponse Body:
{ // Maps ID of cancelled healthPackage to Date of cancellation [id: string]: string }
- Request Body:
-
POST /health-packages/cancellation-date/:healthPackageId
- Get cancellation date for health package of user - Request Body:{ id: string; isFamilyMember: boolean }
- Reponse Body:string
-> Date of cancellation
Notifications Endpoints
-
POST /notifications/all'
- Get all notifications for a user- Request Body:
{ 'username': string, }
- Reponse Body:
{ notifications: [ { _id: string title: string description?: string } ] }
- Request Body:
-
DELETE /notifications'
- Delete a notification- Request Body:
{ username: string, notificationId: string, }
- Reponse Body: N/A
Patient Endpoints
-
GET /patients/myPatients'
- Get all patients for a user- Request Body: N/A
- Reponse Body:
[ { id: string name: string username: string email: string mobileNumber: string dateOfBirth: string gender: Gender emergencyContact: { name: string mobileNumber: string } familyMembers: string[] } ]
-
GET /patients/search?name={name}
- Search for patient by name- Request Body: N/A
- Reponse Body:
[ { id: string name: string username: string email: string mobileNumber: string dateOfBirth: string gender: Gender emergencyContact: { name: string mobileNumber: string } familyMembers: string[] } ]
-
GET /patients/filter
- Filter patients- Request Body: N/A
- Reponse Body:
[ { id: string name: string username: string email: string mobileNumber: string dateOfBirth: string gender: Gender emergencyContact: { name: string mobileNumber: string } familyMembers: string[] } ]
-
GET /patients/:id
- Get patient by id- Request Body: N/A
- Reponse Body:
{ id: string name: string username: string email: string mobileNumber: string dateOfBirth: string gender: Gender emergencyContact: { name: string mobileNumber: string } familyMembers: string[] }
-
GET /patients/username/:username
- Get patient by username- Request Body: N/A
- Reponse Body:
{ id: string name: string username: string email: string mobileNumber: string dateOfBirth: string gender: Gender emergencyContact: { name: string mobileNumber: string } familyMembers: string[] documents: string[], appointments: [ { 'id': string, 'patientID': string, 'doctorID': string, 'doctorName': string, 'doctorTimes': string[], 'date': string, 'familyID': string, 'reservedFor': string 'status': AppointmentStatus } ], prescriptions: any[], notes: string[], walletMoney: number }
-
GET /patients/wallet/:id
- Get wallet money for a patient- Request Body: N/A
- Reponse Body:
{ money: number }
-
GET /patients/viewHealthRecords/me
- Get health notes for current user- Request Body: N/A
- Reponse Body:
[string]
-> The notes
-
GET /patients/viewMedicalHistory
- Get Medical History for current user- Request Body: N/A
- Reponse Body:
[string]
-> The url of the documents
-
GET /patients/viewHealthRecords/Files/:id
- Get health notes for user by id- Request Body: N/A
- Reponse Body:
[string]
-> The notes
-
POST /appointment/createFollowUp
- Create a follow up for a user- Request Body:
{ doctorID: string, patientID: string, followUpDate: Date, appointmentID: string }
- Reponse Body: N/A
- Request Body:
-
POST /patients/deleteMedicalHistory/mine'
- Delete a file from the medical history- Request Body:
{ url: string, // Url to delete }
- Reponse Body: N/A
- Request Body:
-
POST /patients/uploadMedicalHistory/mine
- Upload medical history- Request Body:
{ medicalHistory: File, }
- Reponse Body: N/A
- Request Body:
-
POST
/appointment/requestFollowUp` - Request a follow up- Request Body:
{ appointmentID: string, date: string }
- Reponse Body: N/A
- Request Body:
-
POST
/appointment/checkFollowUp/:appointmentID`- Checks if a follow up exists- Request Body:
{ appointmentID: string, date: string }
- Reponse Body:
boolean
-> Whether the follow up exists or not
- Request Body:
Prescriptions Endpoints
-
GET /prescriptions/mine'
- Get all prescriptions of current patient- Request Body: N/A
- Reponse Body:
[ { 'id': string, 'doctor': string, 'patient': string, 'date': Date, 'isFilled': boolean, 'medicine': [ { 'name': string 'dosage': string 'quantity': number } ] } ]
-
GET prescriptions/mine/:id
- Get a single prescription by id- Request Body: N/A
- Reponse Body:
{ id: string name: string username: string email: string mobileNumber: string dateOfBirth: string gender: Gender emergencyContact: { name: string mobileNumber: string } familyMembers: string[] }
-
GET /prescriptions/:username
- Get prescriptions for patient- Request Body: N/A
- Reponse Body:
[ { 'id': string, 'doctor': string, 'patient': string, 'date': Date, 'isFilled': boolean, 'medicine': [ { 'name': string 'dosage': string 'quantity': number } ] } ]
-
POST /prescriptions
- Add a prescription to a patient- Request Body:
{ patient: string, medicine: string, date: string, }
- Reponse Body:
{ 'id': string, 'doctor': string, 'patient': string, 'date': Date, 'isFilled': boolean, 'medicine': [ { 'name': string 'dosage': string 'quantity': number } ] }
- Reponse Body:
- Request Body:
-
PUT /prescriptions/edit/:id
- Edit a prescription by id- Request Body:
{ medicine: [ { name: string, dosage: string, quantity: number, } ], date: Date, patient: string // Id of patient }
- Reponse Body: N/A
- Request Body:
We use jest
to automatically test parts of our code.
To run the tests, run the following command
> cd backend && npm run test
We also use Postman
to manually test all our api references by making sure the response is as expected. We use it as some kind of sanity-check.
Here is an example of testing one of our endpoints using Postman:
-
Make sure to follow the Installation steps first
-
Add a
.env
in thebackend
of both reposCopilot-and-Sons-Clinic
andCopilot-and-Sons-Pharmacy
with the following variables (replace the values with your own)
MONGO_URI="<Your Mongo Connection String>"
PORT=3000
BCRYPT_SALT="<A secret string to use for encrypting passwords>"
JWT_TOKEN="<A secret string to use for hashing JWT tokens>"
- Start clinic
cd Copilot-and-Sons-Clinic
npm start
- Start pharmacy in a different terminal
cd Copilot-and-Sons-Pharmacy
npm start
NOTE
If you want to use Docker Compose to start to project, you can replace the last step with
docker compose up
We welcome contributions to Copilot & Sons El7a2ny Clinic. If you want to contribute, it's as easy as:
- Fork the repo
- Create a new branch (
git checkout -b my-new-feature
) - Make changes
- Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request
- Wait for your PR to be reviewed and merged
NOTE
We welcome all contributions, but please make sure to follow our code style and linting rules. You can check the Code Style section for more details.
- Mongoose docs
- Express docs
- ReactJs docs
- NodeJs docs
- TypeScript docs
- Docker docs
- Docker Compose docs
- ESLint docs
- Prettier docs
- MUI docs
- React Router docs
- React Hook Form docs
- React Query docs
- Formik docs
- Toastify docs
- Mongoose Crash Course
- Express Crash Course
- ReactJs Crash Course
- MUI Crash Course
- React Router Crash Course
- React Hook Form Crash Course
- React Query Crash Course
The software is open source under the Apache 2.0 License
.