Skip to content

advanced-computer-lab-2023/Copilot-and-Sons-Clinic

Repository files navigation

Typing SVG

Copilot & Sons: El7a2ny Clinic

Table of Contents

  1. πŸš€ Motivation
  2. 🧱 Build Status
  3. 🎨 Code Style
  4. βš’οΈ Tech and Frameworks used
  5. πŸ”₯ Features & Screenshots
  6. πŸ’» Code Examples
  7. βš™οΈ Installation
  8. πŸ“š API Reference
  9. πŸ§ͺ Tests
  10. πŸ§‘πŸ»β€πŸ« How to Use
  11. 🀝 Contribute
  12. ©️ Credits
  13. πŸ“œ License

πŸš€ Motivation

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.

🧱 Build Status

example workflow

  • 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

🎨 Code Style

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

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

βš’οΈ Tech and Frameworks used

πŸ”₯ Features & Screenshots

User Registration πŸ“
  • Register as a patient with essential details.
  • Upload/remove medical documents (PDF, JPEG, JPG, PNG).
  • Submit a request to register as a doctor with professional details.
  • Upload required documents for doctor registration. image
User Authentication πŸ” - Login and logout securely.

image

Administrator Functions πŸ‘©β€πŸ’Ό - Add/remove another administrator. - Manage doctors and patients. - Accept or reject doctor registration requests. - View information uploaded by doctors.

image

Health Packages πŸ’Ό - Administrators can add/update/delete health packages with different price ranges.

image

Account Management πŸ”„ - Change password. - Reset forgotten password via email. - Edit/update email, hourly rate, or affiliation.

image

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.

image

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.

image

Appointment Management πŸ“… - Filter appointments by date/status. - View upcoming/past appointments. - Patient can reschedule or cancel appointments. - Doctor can reschedule appointments for patients.

image

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.

image

Wallet Management πŸ’° - Receive a refund in the wallet for canceled appointments. - View the amount in the wallet.

image

Communication πŸ“¬ - Patients and doctors can chat with each other. - Doctors and Patients can start/end video calls.

image

πŸ’» Code Examples

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')
      }}
    />
  )
}

βš™οΈ Installation

  • Make sure you have Node and Git installed

  • Make a new folder for the sub system of Clinic & Pharmacy

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

πŸ“š API Reference

Admin Endpoints
  • POST /admins - Add a new admin
    • Request body
      • username: string
      • password: string
      • email: string
    • Response: The created admin
  • 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
  • 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
      }
      
  • POST /chats/send-message - Sends a message

    • Request Body
      {
          'chatId': string
          'senderUsername': string
          'content': string
      }
      
    • Reponse Body: N/A
  • POST '/chats/mark-as-read' - Marks a chat as being read

    • Request Body
      {
          'chatId': string
          'username': string
      }
      
    • Reponse Body: N/A
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
      }
      
  • 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]
      }
      
  • 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
          }
      ]
      
  • 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
      }
      
  • 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[]
      }
      
  • POST /patients/deleteHealthRecord/:id' - Delete health record for a patient

    • Request Body:
      {
          url: string // URL to delete
      }
      
    • Reponse Body: N/A
  • 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
  • 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
      }
      
  • 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
          }
      ]
      
  • 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
  • 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
      }
      
  • 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
  • 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
          },
      }
      
  • 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
      }
      
  • 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
              }
          ]
      }
      
  • 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
  • POST /patients/deleteMedicalHistory/mine' - Delete a file from the medical history

    • Request Body:
      {
          url: string, // Url to delete
      }
      
    • Reponse Body: N/A
  • POST /patients/uploadMedicalHistory/mine - Upload medical history

    • Request Body:
      {
          medicalHistory: File,
      }
      
    • Reponse Body: N/A
  • POST /appointment/requestFollowUp` - Request a follow up

    • Request Body:
      {
        appointmentID: string,
        date: string
      }
      
    • Reponse Body: N/A
  • 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
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
                }
            ]
        }
        
  • 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

πŸ§ͺ Tests

We use jest to automatically test parts of our code. To run the tests, run the following command

> cd backend && npm run test

image

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:

image

πŸ§‘πŸ»β€πŸ« How to Use

  • Make sure to follow the Installation steps first

  • Add a .env in the backend of both repos Copilot-and-Sons-Clinic and Copilot-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

🀝 Contribute

We welcome contributions to Copilot & Sons El7a2ny Clinic. If you want to contribute, it's as easy as:

  1. Fork the repo
  2. Create a new branch (git checkout -b my-new-feature)
  3. Make changes
  4. Commit your changes (git commit -am 'Add some feature')
  5. Push to the branch (git push origin my-new-feature)
  6. Create a new Pull Request
  7. 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.

🫑 Credits

Docs

YouTube Videos

πŸ“œ License

The software is open source under the Apache 2.0 License.

Apache 2.0

About

Virtual clinic built using the MERN stack

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages