Add SmartClinicManagementSystem

This commit is contained in:
2025-11-03 18:27:23 +01:00
parent f49be0c3d3
commit f65dc6ccdd
75 changed files with 4380 additions and 1 deletions

View File

@@ -0,0 +1,40 @@
import org.springframework.boot.gradle.plugin.SpringBootPlugin
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
version = "1.0.0"
java {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
repositories {
mavenCentral()
}
dependencies {
implementation platform(SpringBootPlugin.BOM_COORDINATES)
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'io.jsonwebtoken:jjwt-api:0.12.6'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6'
runtimeOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.mysql:mysql-connector-j'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6'
testImplementation "org.junit.jupiter:junit-jupiter-api:${junitVersion}"
testImplementation "org.junit.jupiter:junit-jupiter-params:${junitVersion}"
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junitVersion}"
}
test {
useJUnitPlatform()
}

View File

@@ -0,0 +1,15 @@
package com.project.back_end;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan("com.project.back_end")
public class BackEndApplication {
public static void main(String[] args) {
SpringApplication.run(BackEndApplication.class, args);
}
}

View File

@@ -0,0 +1,90 @@
package com.project.back_end.DTO;
public class AppointmentDTO {
// 1. 'id' field:
// - Type: private Long
// - Description:
// - Represents the unique identifier for the appointment.
// - This is the primary key for identifying the appointment in the system.
// 2. 'doctorId' field:
// - Type: private Long
// - Description:
// - Represents the ID of the doctor associated with the appointment.
// - This is a simplified field, capturing only the ID of the doctor (not the full Doctor object).
// 3. 'doctorName' field:
// - Type: private String
// - Description:
// - Represents the name of the doctor associated with the appointment.
// - This is a simplified field for displaying the doctor's name.
// 4. 'patientId' field:
// - Type: private Long
// - Description:
// - Represents the ID of the patient associated with the appointment.
// - This is a simplified field, capturing only the ID of the patient (not the full Patient object).
// 5. 'patientName' field:
// - Type: private String
// - Description:
// - Represents the name of the patient associated with the appointment.
// - This is a simplified field for displaying the patient's name.
// 6. 'patientEmail' field:
// - Type: private String
// - Description:
// - Represents the email of the patient associated with the appointment.
// - This is a simplified field for displaying the patient's email.
// 7. 'patientPhone' field:
// - Type: private String
// - Description:
// - Represents the phone number of the patient associated with the appointment.
// - This is a simplified field for displaying the patient's phone number.
// 8. 'patientAddress' field:
// - Type: private String
// - Description:
// - Represents the address of the patient associated with the appointment.
// - This is a simplified field for displaying the patient's address.
// 9. 'appointmentTime' field:
// - Type: private LocalDateTime
// - Description:
// - Represents the scheduled date and time of the appointment.
// - The time when the appointment is supposed to happen, stored as a LocalDateTime object.
// 10. 'status' field:
// - Type: private int
// - Description:
// - Represents the status of the appointment.
// - Status can indicate if the appointment is "Scheduled:0", "Completed:1", or other statuses (e.g., "Canceled") as needed.
// 11. 'appointmentDate' field (Custom Getter):
// - Type: private LocalDate
// - Description:
// - A derived field representing only the date part of the appointment (without the time).
// - Extracted from the 'appointmentTime' field.
// 12. 'appointmentTimeOnly' field (Custom Getter):
// - Type: private LocalTime
// - Description:
// - A derived field representing only the time part of the appointment (without the date).
// - Extracted from the 'appointmentTime' field.
// 13. 'endTime' field (Custom Getter):
// - Type: private LocalDateTime
// - Description:
// - A derived field representing the end time of the appointment.
// - Calculated by adding 1 hour to the 'appointmentTime' field.
// 14. Constructor:
// - The constructor accepts all the relevant fields for the AppointmentDTO, including simplified fields for the doctor and patient (ID, name, etc.).
// - It also calculates custom fields: 'appointmentDate', 'appointmentTimeOnly', and 'endTime' based on the 'appointmentTime' field.
// 15. Getters:
// - Standard getter methods are provided for all fields: id, doctorId, doctorName, patientId, patientName, patientEmail, patientPhone, patientAddress, appointmentTime, status, appointmentDate, appointmentTimeOnly, and endTime.
// - These methods allow access to the values of the fields in the AppointmentDTO object.
}

View File

@@ -0,0 +1,30 @@
package com.project.back_end.DTO;
public class Login {
// 1. 'email' field:
// - Type: private String
// - Description:
// - Represents the email address used for logging into the system.
// - The email field is expected to contain a valid email address for user authentication purposes.
// 2. 'password' field:
// - Type: private String
// - Description:
// - Represents the password associated with the email address.
// - The password field is used for verifying the user's identity during login.
// - It is generally hashed before being stored and compared during authentication.
// 3. Constructor:
// - No explicit constructor is defined for this class, as it relies on the default constructor provided by Java.
// - This class can be initialized with setters or directly via reflection, as per the application's needs.
// 4. Getters and Setters:
// - Standard getter and setter methods are provided for both 'email' and 'password' fields.
// - The 'getEmail()' method allows access to the email value.
// - The 'setEmail(String email)' method sets the email value.
// - The 'getPassword()' method allows access to the password value.
// - The 'setPassword(String password)' method sets the password value.
}

View File

@@ -0,0 +1,21 @@
package com.project.back_end.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.NonNull;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(@NonNull CorsRegistry registry) {
// Allow CORS for all endpoints
registry.addMapping("/**")
.allowedOrigins("*") // Add your frontend URL here
.allowedMethods("GET", "POST", "PUT", "DELETE") // Specify allowed methods
.allowedHeaders("*"); // You can restrict headers if needed
}
}

View File

@@ -0,0 +1,27 @@
package com.project.back_end.controllers;
public class AdminController {
// 1. Set Up the Controller Class:
// - Annotate the class with `@RestController` to indicate that it's a REST controller, used to handle web requests and return JSON responses.
// - Use `@RequestMapping("${api.path}admin")` to define a base path for all endpoints in this controller.
// - This allows the use of an external property (`api.path`) for flexible configuration of endpoint paths.
// 2. Autowire Service Dependency:
// - Use constructor injection to autowire the `Service` class.
// - The service handles core logic related to admin validation and token checking.
// - This promotes cleaner code and separation of concerns between the controller and business logic layer.
// 3. Define the `adminLogin` Method:
// - Handles HTTP POST requests for admin login functionality.
// - Accepts an `Admin` object in the request body, which contains login credentials.
// - Delegates authentication logic to the `validateAdmin` method in the service layer.
// - Returns a `ResponseEntity` with a `Map` containing login status or messages.
}

View File

@@ -0,0 +1,48 @@
package com.project.back_end.controllers;
public class AppointmentController {
// 1. Set Up the Controller Class:
// - Annotate the class with `@RestController` to define it as a REST API controller.
// - Use `@RequestMapping("/appointments")` to set a base path for all appointment-related endpoints.
// - This centralizes all routes that deal with booking, updating, retrieving, and canceling appointments.
// 2. Autowire Dependencies:
// - Inject `AppointmentService` for handling the business logic specific to appointments.
// - Inject the general `Service` class, which provides shared functionality like token validation and appointment checks.
// 3. Define the `getAppointments` Method:
// - Handles HTTP GET requests to fetch appointments based on date and patient name.
// - Takes the appointment date, patient name, and token as path variables.
// - First validates the token for role `"doctor"` using the `Service`.
// - If the token is valid, returns appointments for the given patient on the specified date.
// - If the token is invalid or expired, responds with the appropriate message and status code.
// 4. Define the `bookAppointment` Method:
// - Handles HTTP POST requests to create a new appointment.
// - Accepts a validated `Appointment` object in the request body and a token as a path variable.
// - Validates the token for the `"patient"` role.
// - Uses service logic to validate the appointment data (e.g., check for doctor availability and time conflicts).
// - Returns success if booked, or appropriate error messages if the doctor ID is invalid or the slot is already taken.
// 5. Define the `updateAppointment` Method:
// - Handles HTTP PUT requests to modify an existing appointment.
// - Accepts a validated `Appointment` object and a token as input.
// - Validates the token for `"patient"` role.
// - Delegates the update logic to the `AppointmentService`.
// - Returns an appropriate success or failure response based on the update result.
// 6. Define the `cancelAppointment` Method:
// - Handles HTTP DELETE requests to cancel a specific appointment.
// - Accepts the appointment ID and a token as path variables.
// - Validates the token for `"patient"` role to ensure the user is authorized to cancel the appointment.
// - Calls `AppointmentService` to handle the cancellation process and returns the result.
}

View File

@@ -0,0 +1,61 @@
package com.project.back_end.controllers;
public class DoctorController {
// 1. Set Up the Controller Class:
// - Annotate the class with `@RestController` to define it as a REST controller that serves JSON responses.
// - Use `@RequestMapping("${api.path}doctor")` to prefix all endpoints with a configurable API path followed by "doctor".
// - This class manages doctor-related functionalities such as registration, login, updates, and availability.
// 2. Autowire Dependencies:
// - Inject `DoctorService` for handling the core logic related to doctors (e.g., CRUD operations, authentication).
// - Inject the shared `Service` class for general-purpose features like token validation and filtering.
// 3. Define the `getDoctorAvailability` Method:
// - Handles HTTP GET requests to check a specific doctors availability on a given date.
// - Requires `user` type, `doctorId`, `date`, and `token` as path variables.
// - First validates the token against the user type.
// - If the token is invalid, returns an error response; otherwise, returns the availability status for the doctor.
// 4. Define the `getDoctor` Method:
// - Handles HTTP GET requests to retrieve a list of all doctors.
// - Returns the list within a response map under the key `"doctors"` with HTTP 200 OK status.
// 5. Define the `saveDoctor` Method:
// - Handles HTTP POST requests to register a new doctor.
// - Accepts a validated `Doctor` object in the request body and a token for authorization.
// - Validates the token for the `"admin"` role before proceeding.
// - If the doctor already exists, returns a conflict response; otherwise, adds the doctor and returns a success message.
// 6. Define the `doctorLogin` Method:
// - Handles HTTP POST requests for doctor login.
// - Accepts a validated `Login` DTO containing credentials.
// - Delegates authentication to the `DoctorService` and returns login status and token information.
// 7. Define the `updateDoctor` Method:
// - Handles HTTP PUT requests to update an existing doctor's information.
// - Accepts a validated `Doctor` object and a token for authorization.
// - Token must belong to an `"admin"`.
// - If the doctor exists, updates the record and returns success; otherwise, returns not found or error messages.
// 8. Define the `deleteDoctor` Method:
// - Handles HTTP DELETE requests to remove a doctor by ID.
// - Requires both doctor ID and an admin token as path variables.
// - If the doctor exists, deletes the record and returns a success message; otherwise, responds with a not found or error message.
// 9. Define the `filter` Method:
// - Handles HTTP GET requests to filter doctors based on name, time, and specialty.
// - Accepts `name`, `time`, and `speciality` as path variables.
// - Calls the shared `Service` to perform filtering logic and returns matching doctors in the response.
}

View File

@@ -0,0 +1,52 @@
package com.project.back_end.controllers;
public class PatientController {
// 1. Set Up the Controller Class:
// - Annotate the class with `@RestController` to define it as a REST API controller for patient-related operations.
// - Use `@RequestMapping("/patient")` to prefix all endpoints with `/patient`, grouping all patient functionalities under a common route.
// 2. Autowire Dependencies:
// - Inject `PatientService` to handle patient-specific logic such as creation, retrieval, and appointments.
// - Inject the shared `Service` class for tasks like token validation and login authentication.
// 3. Define the `getPatient` Method:
// - Handles HTTP GET requests to retrieve patient details using a token.
// - Validates the token for the `"patient"` role using the shared service.
// - If the token is valid, returns patient information; otherwise, returns an appropriate error message.
// 4. Define the `createPatient` Method:
// - Handles HTTP POST requests for patient registration.
// - Accepts a validated `Patient` object in the request body.
// - First checks if the patient already exists using the shared service.
// - If validation passes, attempts to create the patient and returns success or error messages based on the outcome.
// 5. Define the `login` Method:
// - Handles HTTP POST requests for patient login.
// - Accepts a `Login` DTO containing email/username and password.
// - Delegates authentication to the `validatePatientLogin` method in the shared service.
// - Returns a response with a token or an error message depending on login success.
// 6. Define the `getPatientAppointment` Method:
// - Handles HTTP GET requests to fetch appointment details for a specific patient.
// - Requires the patient ID, token, and user role as path variables.
// - Validates the token using the shared service.
// - If valid, retrieves the patient's appointment data from `PatientService`; otherwise, returns a validation error.
// 7. Define the `filterPatientAppointment` Method:
// - Handles HTTP GET requests to filter a patient's appointments based on specific conditions.
// - Accepts filtering parameters: `condition`, `name`, and a token.
// - Token must be valid for a `"patient"` role.
// - If valid, delegates filtering logic to the shared service and returns the filtered result.
}

View File

@@ -0,0 +1,33 @@
package com.project.back_end.controllers;
public class PrescriptionController {
// 1. Set Up the Controller Class:
// - Annotate the class with `@RestController` to define it as a REST API controller.
// - Use `@RequestMapping("${api.path}prescription")` to set the base path for all prescription-related endpoints.
// - This controller manages creating and retrieving prescriptions tied to appointments.
// 2. Autowire Dependencies:
// - Inject `PrescriptionService` to handle logic related to saving and fetching prescriptions.
// - Inject the shared `Service` class for token validation and role-based access control.
// - Inject `AppointmentService` to update appointment status after a prescription is issued.
// 3. Define the `savePrescription` Method:
// - Handles HTTP POST requests to save a new prescription for a given appointment.
// - Accepts a validated `Prescription` object in the request body and a doctors token as a path variable.
// - Validates the token for the `"doctor"` role.
// - If the token is valid, updates the status of the corresponding appointment to reflect that a prescription has been added.
// - Delegates the saving logic to `PrescriptionService` and returns a response indicating success or failure.
// 4. Define the `getPrescription` Method:
// - Handles HTTP GET requests to retrieve a prescription by its associated appointment ID.
// - Accepts the appointment ID and a doctors token as path variables.
// - Validates the token for the `"doctor"` role using the shared service.
// - If the token is valid, fetches the prescription using the `PrescriptionService`.
// - Returns the prescription details or an appropriate error message if validation fails.
}

View File

@@ -0,0 +1,28 @@
package com.project.back_end.controllers;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.validation.FieldError;
import java.util.HashMap;
import java.util.Map;
@RestControllerAdvice
public class ValidationFailed {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidationException(MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
// Iterate through all the validation errors
for (FieldError error : ex.getBindingResult().getFieldErrors()) {
String errorMessage = error.getDefaultMessage();
errors.put("message", "" + errorMessage);
}
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errors);
}
}

View File

@@ -0,0 +1,37 @@
package com.project.back_end.models;
public class Admin {
// @Entity annotation:
// - Marks the class as a JPA entity, which means it represents a table in the database.
// - It is required for persistence frameworks like Hibernate to map the class to a database table.
// 1. 'id' field:
// - Type: private Long
// - Description:
// - Represents the unique identifier for the Admin entity.
// - This field is auto-generated by the database using @GeneratedValue with strategy GenerationType.IDENTITY.
// - It is the primary key of the entity, identified by @Id annotation.
// 2. 'username' field:
// - Type: private String
// - Description:
// - Represents the username of the admin.
// - Used to log into the system.
// - @NotNull validation ensures that this field cannot be null when creating or updating an Admin.
// 3. 'password' field:
// - Type: private String
// - Description:
// - Represents the password of the admin for authentication.
// - The field is marked with @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) to prevent the password from being exposed in JSON responses.
// - @NotNull validation ensures the password cannot be null when creating or updating an Admin.
// 4. Constructor(s):
// - A no-argument constructor is implicitly provided, required by JPA for entity creation.
// - A parameterized constructor can be added as needed.
// 5. Getters and Setters:
// - Standard getter and setter methods are provided for accessing and modifying the fields.
}

View File

@@ -0,0 +1,72 @@
package com.project.back_end.models;
public class Appointment {
// @Entity annotation:
// - Marks the class as a JPA entity, meaning it represents a table in the database.
// - Required for persistence frameworks (e.g., Hibernate) to map the class to a database table.
// 1. 'id' field:
// - Type: private Long
// - Description:
// - Represents the unique identifier for each appointment.
// - The @Id annotation marks it as the primary key.
// - The @GeneratedValue(strategy = GenerationType.IDENTITY) annotation auto-generates the ID value when a new record is inserted into the database.
// 2. 'doctor' field:
// - Type: private Doctor
// - Description:
// - Represents the doctor assigned to this appointment.
// - The @ManyToOne annotation defines the relationship, indicating many appointments can be linked to one doctor.
// - The @NotNull annotation ensures that an appointment must be associated with a doctor when created.
// 3. 'patient' field:
// - Type: private Patient
// - Description:
// - Represents the patient assigned to this appointment.
// - The @ManyToOne annotation defines the relationship, indicating many appointments can be linked to one patient.
// - The @NotNull annotation ensures that an appointment must be associated with a patient when created.
// 4. 'appointmentTime' field:
// - Type: private LocalDateTime
// - Description:
// - Represents the date and time when the appointment is scheduled to occur.
// - The @Future annotation ensures that the appointment time is always in the future when the appointment is created.
// - It uses LocalDateTime, which includes both the date and time for the appointment.
// 5. 'status' field:
// - Type: private int
// - Description:
// - Represents the current status of the appointment. It is an integer where:
// - 0 means the appointment is scheduled.
// - 1 means the appointment has been completed.
// - The @NotNull annotation ensures that the status field is not null.
// 6. 'getEndTime' method:
// - Type: private LocalDateTime
// - Description:
// - This method is a transient field (not persisted in the database).
// - It calculates the end time of the appointment by adding one hour to the start time (appointmentTime).
// - It is used to get an estimated appointment end time for display purposes.
// 7. 'getAppointmentDate' method:
// - Type: private LocalDate
// - Description:
// - This method extracts only the date part from the appointmentTime field.
// - It returns a LocalDate object representing just the date (without the time) of the scheduled appointment.
// 8. 'getAppointmentTimeOnly' method:
// - Type: private LocalTime
// - Description:
// - This method extracts only the time part from the appointmentTime field.
// - It returns a LocalTime object representing just the time (without the date) of the scheduled appointment.
// 9. Constructor(s):
// - A no-argument constructor is implicitly provided by JPA for entity creation.
// - A parameterized constructor can be added as needed to initialize fields.
// 10. Getters and Setters:
// - Standard getter and setter methods are provided for accessing and modifying the fields: id, doctor, patient, appointmentTime, status, etc.
}

View File

@@ -0,0 +1,65 @@
package com.project.back_end.models;
public class Doctor {
// @Entity annotation:
// - Marks the class as a JPA entity, meaning it represents a table in the database.
// - Required for persistence frameworks (e.g., Hibernate) to map the class to a database table.
// 1. 'id' field:
// - Type: private Long
// - Description:
// - Represents the unique identifier for each doctor.
// - The @Id annotation marks it as the primary key.
// - The @GeneratedValue(strategy = GenerationType.IDENTITY) annotation auto-generates the ID value when a new record is inserted into the database.
// 2. 'name' field:
// - Type: private String
// - Description:
// - Represents the doctor's name.
// - The @NotNull annotation ensures that the doctor's name is required.
// - The @Size(min = 3, max = 100) annotation ensures that the name length is between 3 and 100 characters.
// - Provides validation for correct input and user experience.
// 3. 'specialty' field:
// - Type: private String
// - Description:
// - Represents the medical specialty of the doctor.
// - The @NotNull annotation ensures that a specialty must be provided.
// - The @Size(min = 3, max = 50) annotation ensures that the specialty name is between 3 and 50 characters long.
// 4. 'email' field:
// - Type: private String
// - Description:
// - Represents the doctor's email address.
// - The @NotNull annotation ensures that an email address is required.
// - The @Email annotation validates that the email address follows a valid email format (e.g., doctor@example.com).
// 5. 'password' field:
// - Type: private String
// - Description:
// - Represents the doctor's password for login authentication.
// - The @NotNull annotation ensures that a password must be provided.
// - The @Size(min = 6) annotation ensures that the password must be at least 6 characters long.
// - The @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) annotation ensures that the password is not serialized in the response (hidden from the frontend).
// 6. 'phone' field:
// - Type: private String
// - Description:
// - Represents the doctor's phone number.
// - The @NotNull annotation ensures that a phone number must be provided.
// - The @Pattern(regexp = "^[0-9]{10}$") annotation validates that the phone number must be exactly 10 digits long.
// 7. 'availableTimes' field:
// - Type: private List<String>
// - Description:
// - Represents the available times for the doctor in a list of time slots.
// - Each time slot is represented as a string (e.g., "09:00-10:00", "10:00-11:00").
// - The @ElementCollection annotation ensures that the list of time slots is stored as a separate collection in the database.
// 8. Getters and Setters:
// - Standard getter and setter methods are provided for all fields: id, name, specialty, email, password, phone, and availableTimes.
}

View File

@@ -0,0 +1,59 @@
package com.project.back_end.models;
public class Patient {
// @Entity annotation:
// - Marks the class as a JPA entity, meaning it represents a table in the database.
// - Required for persistence frameworks (e.g., Hibernate) to map the class to a database table.
// 1. 'id' field:
// - Type: private Long
// - Description:
// - Represents the unique identifier for each patient.
// - The @Id annotation marks it as the primary key.
// - The @GeneratedValue(strategy = GenerationType.IDENTITY) annotation auto-generates the ID value when a new record is inserted into the database.
// 2. 'name' field:
// - Type: private String
// - Description:
// - Represents the patient's full name.
// - The @NotNull annotation ensures that the patient's name is required.
// - The @Size(min = 3, max = 100) annotation ensures that the name length is between 3 and 100 characters.
// - Provides validation for correct input and user experience.
// 3. 'email' field:
// - Type: private String
// - Description:
// - Represents the patient's email address.
// - The @NotNull annotation ensures that an email address must be provided.
// - The @Email annotation validates that the email address follows a valid email format (e.g., patient@example.com).
// 4. 'password' field:
// - Type: private String
// - Description:
// - Represents the patient's password for login authentication.
// - The @NotNull annotation ensures that a password must be provided.
// - The @Size(min = 6) annotation ensures that the password must be at least 6 characters long.
// 5. 'phone' field:
// - Type: private String
// - Description:
// - Represents the patient's phone number.
// - The @NotNull annotation ensures that a phone number must be provided.
// - The @Pattern(regexp = "^[0-9]{10}$") annotation validates that the phone number must be exactly 10 digits long.
// 6. 'address' field:
// - Type: private String
// - Description:
// - Represents the patient's address.
// - The @NotNull annotation ensures that the address must be provided.
// - The @Size(max = 255) annotation ensures that the address does not exceed 255 characters in length, providing validation for the address input.
// 7. Getters and Setters:
// - Standard getter and setter methods are provided for all fields: id, name, email, password, phone, and address.
// - These methods allow access and modification of the fields of the Patient class.
}

View File

@@ -0,0 +1,56 @@
package com.project.back_end.models;
public class Prescription {
// @Document annotation:
// - Marks the class as a MongoDB document (a collection in MongoDB).
// - The collection name is specified as "prescriptions" to map this class to the "prescriptions" collection in MongoDB.
// 1. 'id' field:
// - Type: private String
// - Description:
// - Represents the unique identifier for each prescription.
// - The @Id annotation marks it as the primary key in the MongoDB collection.
// - The id is of type String, which is commonly used for MongoDB's ObjectId as it stores IDs as strings in the database.
// 2. 'patientName' field:
// - Type: private String
// - Description:
// - Represents the name of the patient receiving the prescription.
// - The @NotNull annotation ensures that the patient name is required.
// - The @Size(min = 3, max = 100) annotation ensures that the name length is between 3 and 100 characters, ensuring a reasonable name length.
// 3. 'appointmentId' field:
// - Type: private Long
// - Description:
// - Represents the ID of the associated appointment where the prescription was given.
// - The @NotNull annotation ensures that the appointment ID is required for the prescription.
// 4. 'medication' field:
// - Type: private String
// - Description:
// - Represents the medication prescribed to the patient.
// - The @NotNull annotation ensures that the medication name is required.
// - The @Size(min = 3, max = 100) annotation ensures that the medication name is between 3 and 100 characters, which ensures meaningful medication names.
// 5. 'dosage' field:
// - Type: private String
// - Description:
// - Represents the dosage information for the prescribed medication.
// - The @NotNull annotation ensures that the dosage information is provided.
// 6. 'doctorNotes' field:
// - Type: private String
// - Description:
// - Represents any additional notes or instructions from the doctor regarding the prescription.
// - The @Size(max = 200) annotation ensures that the doctor's notes do not exceed 200 characters, providing a reasonable limit for additional notes.
// 7. Constructors:
// - The class includes a no-argument constructor (default constructor) and a parameterized constructor that initializes the fields: patientName, medication, dosage, doctorNotes, and appointmentId.
// 8. Getters and Setters:
// - Standard getter and setter methods are provided for all fields: id, patientName, medication, dosage, doctorNotes, and appointmentId.
// - These methods allow access and modification of the fields of the Prescription class.
}

View File

@@ -0,0 +1,30 @@
package com.project.back_end.mvc;
public class DashboardController {
// 1. Set Up the MVC Controller Class:
// - Annotate the class with `@Controller` to indicate that it serves as an MVC controller returning view names (not JSON).
// - This class handles routing to admin and doctor dashboard pages based on token validation.
// 2. Autowire the Shared Service:
// - Inject the common `Service` class, which provides the token validation logic used to authorize access to dashboards.
// 3. Define the `adminDashboard` Method:
// - Handles HTTP GET requests to `/adminDashboard/{token}`.
// - Accepts an admin's token as a path variable.
// - Validates the token using the shared service for the `"admin"` role.
// - If the token is valid (i.e., no errors returned), forwards the user to the `"admin/adminDashboard"` view.
// - If invalid, redirects to the root URL, likely the login or home page.
// 4. Define the `doctorDashboard` Method:
// - Handles HTTP GET requests to `/doctorDashboard/{token}`.
// - Accepts a doctor's token as a path variable.
// - Validates the token using the shared service for the `"doctor"` role.
// - If the token is valid, forwards the user to the `"doctor/doctorDashboard"` view.
// - If the token is invalid, redirects to the root URL.
}

View File

@@ -0,0 +1,30 @@
package com.project.back_end.repo;
public interface AdminRepository {
// 1. Extend JpaRepository:
// - The repository extends JpaRepository<Admin, Long>, which gives it basic CRUD functionality.
// - The methods such as save, delete, update, and find are inherited without the need for explicit implementation.
// - JpaRepository also includes pagination and sorting features.
// Example: public interface AdminRepository extends JpaRepository<Admin, Long> {}
// 2. Custom Query Method:
// - **findByUsername**:
// - This method allows you to find an Admin by their username.
// - Return type: Admin
// - Parameter: String username
// - It will return an Admin entity that matches the provided username.
// - If no Admin is found with the given username, it returns null.
// Example: public Admin findByUsername(String username);
// 3. Add @Repository annotation:
// - The @Repository annotation marks this interface as a Spring Data JPA repository.
// - While it is technically optional (since JpaRepository is a part of Spring Data), it's good practice to include it for clarity.
// - Spring Data JPA automatically implements the repository, providing the necessary CRUD functionality.
// Example: @Repository
// public interface AdminRepository extends JpaRepository<Admin, Long> { ... }
}

View File

@@ -0,0 +1,66 @@
package com.project.back_end.repo;
public interface AppointmentRepository {
// 1. Extend JpaRepository:
// - The repository extends JpaRepository<Appointment, Long>, which gives it basic CRUD functionality.
// - The methods such as save, delete, update, and find are inherited without the need for explicit implementation.
// - JpaRepository also includes pagination and sorting features.
// Example: public interface AppointmentRepository extends JpaRepository<Appointment, Long> {}
// 2. Custom Query Methods:
// - **findByDoctorIdAndAppointmentTimeBetween**:
// - This method retrieves a list of appointments for a specific doctor within a given time range.
// - The doctors available times are eagerly fetched to avoid lazy loading.
// - Return type: List<Appointment>
// - Parameters: Long doctorId, LocalDateTime start, LocalDateTime end
// - It uses a LEFT JOIN to fetch the doctors available times along with the appointments.
// - **findByDoctorIdAndPatient_NameContainingIgnoreCaseAndAppointmentTimeBetween**:
// - This method retrieves appointments for a specific doctor and patient name (ignoring case) within a given time range.
// - It performs a LEFT JOIN to fetch both the doctor and patient details along with the appointment times.
// - Return type: List<Appointment>
// - Parameters: Long doctorId, String patientName, LocalDateTime start, LocalDateTime end
// - **deleteAllByDoctorId**:
// - This method deletes all appointments associated with a particular doctor.
// - It is marked as @Modifying and @Transactional, which makes it a modification query, ensuring that the operation is executed within a transaction.
// - Return type: void
// - Parameters: Long doctorId
// - **findByPatientId**:
// - This method retrieves all appointments for a specific patient.
// - Return type: List<Appointment>
// - Parameters: Long patientId
// - **findByPatient_IdAndStatusOrderByAppointmentTimeAsc**:
// - This method retrieves all appointments for a specific patient with a given status, ordered by the appointment time.
// - Return type: List<Appointment>
// - Parameters: Long patientId, int status
// - **filterByDoctorNameAndPatientId**:
// - This method retrieves appointments based on a doctors name (using a LIKE query) and the patients ID.
// - Return type: List<Appointment>
// - Parameters: String doctorName, Long patientId
// - **filterByDoctorNameAndPatientIdAndStatus**:
// - This method retrieves appointments based on a doctors name (using a LIKE query), patients ID, and a specific appointment status.
// - Return type: List<Appointment>
// - Parameters: String doctorName, Long patientId, int status
// - **updateStatus**:
// - This method updates the status of a specific appointment based on its ID.
// - Return type: void
// - Parameters: int status, long id
// 3. @Modifying and @Transactional annotations:
// - The @Modifying annotation is used to indicate that the method performs a modification operation (like DELETE or UPDATE).
// - The @Transactional annotation ensures that the modification is done within a transaction, meaning that if any exception occurs, the changes will be rolled back.
// 4. @Repository annotation:
// - The @Repository annotation marks this interface as a Spring Data JPA repository.
// - Spring Data JPA automatically implements this repository, providing the necessary CRUD functionality and custom queries defined in the interface.
}

View File

@@ -0,0 +1,39 @@
package com.project.back_end.repo;
public interface DoctorRepository {
// 1. Extend JpaRepository:
// - The repository extends JpaRepository<Doctor, Long>, which gives it basic CRUD functionality.
// - This allows the repository to perform operations like save, delete, update, and find without needing to implement these methods manually.
// - JpaRepository also includes features like pagination and sorting.
// Example: public interface DoctorRepository extends JpaRepository<Doctor, Long> {}
// 2. Custom Query Methods:
// - **findByEmail**:
// - This method retrieves a Doctor by their email.
// - Return type: Doctor
// - Parameters: String email
// - **findByNameLike**:
// - This method retrieves a list of Doctors whose name contains the provided search string (case-sensitive).
// - The `CONCAT('%', :name, '%')` is used to create a pattern for partial matching.
// - Return type: List<Doctor>
// - Parameters: String name
// - **findByNameContainingIgnoreCaseAndSpecialtyIgnoreCase**:
// - This method retrieves a list of Doctors where the name contains the search string (case-insensitive) and the specialty matches exactly (case-insensitive).
// - It combines both fields for a more specific search.
// - Return type: List<Doctor>
// - Parameters: String name, String specialty
// - **findBySpecialtyIgnoreCase**:
// - This method retrieves a list of Doctors with the specified specialty, ignoring case sensitivity.
// - Return type: List<Doctor>
// - Parameters: String specialty
// 3. @Repository annotation:
// - The @Repository annotation marks this interface as a Spring Data JPA repository.
// - Spring Data JPA automatically implements this repository, providing the necessary CRUD functionality and custom queries defined in the interface.
}

View File

@@ -0,0 +1,29 @@
package com.project.back_end.repo;
public interface PatientRepository {
// 1. Extend JpaRepository:
// - The repository extends JpaRepository<Patient, Long>, which provides basic CRUD functionality.
// - This allows the repository to perform operations like save, delete, update, and find without needing to implement these methods manually.
// - JpaRepository also includes features like pagination and sorting.
// Example: public interface PatientRepository extends JpaRepository<Patient, Long> {}
// 2. Custom Query Methods:
// - **findByEmail**:
// - This method retrieves a Patient by their email address.
// - Return type: Patient
// - Parameters: String email
// - **findByEmailOrPhone**:
// - This method retrieves a Patient by either their email or phone number, allowing flexibility for the search.
// - Return type: Patient
// - Parameters: String email, String phone
// 3. @Repository annotation:
// - The @Repository annotation marks this interface as a Spring Data JPA repository.
// - Spring Data JPA automatically implements this repository, providing the necessary CRUD functionality and custom queries defined in the interface.
}

View File

@@ -0,0 +1,21 @@
package com.project.back_end.repo;
public interface PrescriptionRepository {
// 1. Extend MongoRepository:
// - The repository extends MongoRepository<Prescription, String>, which provides basic CRUD functionality for MongoDB.
// - This allows the repository to perform operations like save, delete, update, and find without needing to implement these methods manually.
// - MongoRepository is tailored for working with MongoDB, unlike JpaRepository which is used for relational databases.
// Example: public interface PrescriptionRepository extends MongoRepository<Prescription, String> {}
// 2. Custom Query Method:
// - **findByAppointmentId**:
// - This method retrieves a list of prescriptions associated with a specific appointment.
// - Return type: List<Prescription>
// - Parameters: Long appointmentId
// - MongoRepository automatically derives the query from the method name, in this case, it will find prescriptions by the appointment ID.
}

View File

@@ -0,0 +1,45 @@
package com.project.back_end.services;
public class AppointmentService {
// 1. **Add @Service Annotation**:
// - To indicate that this class is a service layer class for handling business logic.
// - The `@Service` annotation should be added before the class declaration to mark it as a Spring service component.
// - Instruction: Add `@Service` above the class definition.
// 2. **Constructor Injection for Dependencies**:
// - The `AppointmentService` class requires several dependencies like `AppointmentRepository`, `Service`, `TokenService`, `PatientRepository`, and `DoctorRepository`.
// - These dependencies should be injected through the constructor.
// - Instruction: Ensure constructor injection is used for proper dependency management in Spring.
// 3. **Add @Transactional Annotation for Methods that Modify Database**:
// - The methods that modify or update the database should be annotated with `@Transactional` to ensure atomicity and consistency of the operations.
// - Instruction: Add the `@Transactional` annotation above methods that interact with the database, especially those modifying data.
// 4. **Book Appointment Method**:
// - Responsible for saving the new appointment to the database.
// - If the save operation fails, it returns `0`; otherwise, it returns `1`.
// - Instruction: Ensure that the method handles any exceptions and returns an appropriate result code.
// 5. **Update Appointment Method**:
// - This method is used to update an existing appointment based on its ID.
// - It validates whether the patient ID matches, checks if the appointment is available for updating, and ensures that the doctor is available at the specified time.
// - If the update is successful, it saves the appointment; otherwise, it returns an appropriate error message.
// - Instruction: Ensure proper validation and error handling is included for appointment updates.
// 6. **Cancel Appointment Method**:
// - This method cancels an appointment by deleting it from the database.
// - It ensures the patient who owns the appointment is trying to cancel it and handles possible errors.
// - Instruction: Make sure that the method checks for the patient ID match before deleting the appointment.
// 7. **Get Appointments Method**:
// - This method retrieves a list of appointments for a specific doctor on a particular day, optionally filtered by the patient's name.
// - It uses `@Transactional` to ensure that database operations are consistent and handled in a single transaction.
// - Instruction: Ensure the correct use of transaction boundaries, especially when querying the database for appointments.
// 8. **Change Status Method**:
// - This method updates the status of an appointment by changing its value in the database.
// - It should be annotated with `@Transactional` to ensure the operation is executed in a single transaction.
// - Instruction: Add `@Transactional` before this method to ensure atomicity when updating appointment status.
}

View File

@@ -0,0 +1,92 @@
package com.project.back_end.services;
public class DoctorService {
// 1. **Add @Service Annotation**:
// - This class should be annotated with `@Service` to indicate that it is a service layer class.
// - The `@Service` annotation marks this class as a Spring-managed bean for business logic.
// - Instruction: Add `@Service` above the class declaration.
// 2. **Constructor Injection for Dependencies**:
// - The `DoctorService` class depends on `DoctorRepository`, `AppointmentRepository`, and `TokenService`.
// - These dependencies should be injected via the constructor for proper dependency management.
// - Instruction: Ensure constructor injection is used for injecting dependencies into the service.
// 3. **Add @Transactional Annotation for Methods that Modify or Fetch Database Data**:
// - Methods like `getDoctorAvailability`, `getDoctors`, `findDoctorByName`, `filterDoctorsBy*` should be annotated with `@Transactional`.
// - The `@Transactional` annotation ensures that database operations are consistent and wrapped in a single transaction.
// - Instruction: Add the `@Transactional` annotation above the methods that perform database operations or queries.
// 4. **getDoctorAvailability Method**:
// - Retrieves the available time slots for a specific doctor on a particular date and filters out already booked slots.
// - The method fetches all appointments for the doctor on the given date and calculates the availability by comparing against booked slots.
// - Instruction: Ensure that the time slots are properly formatted and the available slots are correctly filtered.
// 5. **saveDoctor Method**:
// - Used to save a new doctor record in the database after checking if a doctor with the same email already exists.
// - If a doctor with the same email is found, it returns `-1` to indicate conflict; `1` for success, and `0` for internal errors.
// - Instruction: Ensure that the method correctly handles conflicts and exceptions when saving a doctor.
// 6. **updateDoctor Method**:
// - Updates an existing doctor's details in the database. If the doctor doesn't exist, it returns `-1`.
// - Instruction: Make sure that the doctor exists before attempting to save the updated record and handle any errors properly.
// 7. **getDoctors Method**:
// - Fetches all doctors from the database. It is marked with `@Transactional` to ensure that the collection is properly loaded.
// - Instruction: Ensure that the collection is eagerly loaded, especially if dealing with lazy-loaded relationships (e.g., available times).
// 8. **deleteDoctor Method**:
// - Deletes a doctor from the system along with all appointments associated with that doctor.
// - It first checks if the doctor exists. If not, it returns `-1`; otherwise, it deletes the doctor and their appointments.
// - Instruction: Ensure the doctor and their appointments are deleted properly, with error handling for internal issues.
// 9. **validateDoctor Method**:
// - Validates a doctor's login by checking if the email and password match an existing doctor record.
// - It generates a token for the doctor if the login is successful, otherwise returns an error message.
// - Instruction: Make sure to handle invalid login attempts and password mismatches properly with error responses.
// 10. **findDoctorByName Method**:
// - Finds doctors based on partial name matching and returns the list of doctors with their available times.
// - This method is annotated with `@Transactional` to ensure that the database query and data retrieval are properly managed within a transaction.
// - Instruction: Ensure that available times are eagerly loaded for the doctors.
// 11. **filterDoctorsByNameSpecilityandTime Method**:
// - Filters doctors based on their name, specialty, and availability during a specific time (AM/PM).
// - The method fetches doctors matching the name and specialty criteria, then filters them based on their availability during the specified time period.
// - Instruction: Ensure proper filtering based on both the name and specialty as well as the specified time period.
// 12. **filterDoctorByTime Method**:
// - Filters a list of doctors based on whether their available times match the specified time period (AM/PM).
// - This method processes a list of doctors and their available times to return those that fit the time criteria.
// - Instruction: Ensure that the time filtering logic correctly handles both AM and PM time slots and edge cases.
// 13. **filterDoctorByNameAndTime Method**:
// - Filters doctors based on their name and the specified time period (AM/PM).
// - Fetches doctors based on partial name matching and filters the results to include only those available during the specified time period.
// - Instruction: Ensure that the method correctly filters doctors based on the given name and time of day (AM/PM).
// 14. **filterDoctorByNameAndSpecility Method**:
// - Filters doctors by name and specialty.
// - It ensures that the resulting list of doctors matches both the name (case-insensitive) and the specified specialty.
// - Instruction: Ensure that both name and specialty are considered when filtering doctors.
// 15. **filterDoctorByTimeAndSpecility Method**:
// - Filters doctors based on their specialty and availability during a specific time period (AM/PM).
// - Fetches doctors based on the specified specialty and filters them based on their available time slots for AM/PM.
// - Instruction: Ensure the time filtering is accurately applied based on the given specialty and time period (AM/PM).
// 16. **filterDoctorBySpecility Method**:
// - Filters doctors based on their specialty.
// - This method fetches all doctors matching the specified specialty and returns them.
// - Instruction: Make sure the filtering logic works for case-insensitive specialty matching.
// 17. **filterDoctorsByTime Method**:
// - Filters all doctors based on their availability during a specific time period (AM/PM).
// - The method checks all doctors' available times and returns those available during the specified time period.
// - Instruction: Ensure proper filtering logic to handle AM/PM time periods.
}

View File

@@ -0,0 +1,58 @@
package com.project.back_end.services;
public class PatientService {
// 1. **Add @Service Annotation**:
// - The `@Service` annotation is used to mark this class as a Spring service component.
// - It will be managed by Spring's container and used for business logic related to patients and appointments.
// - Instruction: Ensure that the `@Service` annotation is applied above the class declaration.
// 2. **Constructor Injection for Dependencies**:
// - The `PatientService` class has dependencies on `PatientRepository`, `AppointmentRepository`, and `TokenService`.
// - These dependencies are injected via the constructor to maintain good practices of dependency injection and testing.
// - Instruction: Ensure constructor injection is used for all the required dependencies.
// 3. **createPatient Method**:
// - Creates a new patient in the database. It saves the patient object using the `PatientRepository`.
// - If the patient is successfully saved, the method returns `1`; otherwise, it logs the error and returns `0`.
// - Instruction: Ensure that error handling is done properly and exceptions are caught and logged appropriately.
// 4. **getPatientAppointment Method**:
// - Retrieves a list of appointments for a specific patient, based on their ID.
// - The appointments are then converted into `AppointmentDTO` objects for easier consumption by the API client.
// - This method is marked as `@Transactional` to ensure database consistency during the transaction.
// - Instruction: Ensure that appointment data is properly converted into DTOs and the method handles errors gracefully.
// 5. **filterByCondition Method**:
// - Filters appointments for a patient based on the condition (e.g., "past" or "future").
// - Retrieves appointments with a specific status (0 for future, 1 for past) for the patient.
// - Converts the appointments into `AppointmentDTO` and returns them in the response.
// - Instruction: Ensure the method correctly handles "past" and "future" conditions, and that invalid conditions are caught and returned as errors.
// 6. **filterByDoctor Method**:
// - Filters appointments for a patient based on the doctor's name.
// - It retrieves appointments where the doctors name matches the given value, and the patient ID matches the provided ID.
// - Instruction: Ensure that the method correctly filters by doctor's name and patient ID and handles any errors or invalid cases.
// 7. **filterByDoctorAndCondition Method**:
// - Filters appointments based on both the doctor's name and the condition (past or future) for a specific patient.
// - This method combines filtering by doctor name and appointment status (past or future).
// - Converts the appointments into `AppointmentDTO` objects and returns them in the response.
// - Instruction: Ensure that the filter handles both doctor name and condition properly, and catches errors for invalid input.
// 8. **getPatientDetails Method**:
// - Retrieves patient details using the `tokenService` to extract the patient's email from the provided token.
// - Once the email is extracted, it fetches the corresponding patient from the `patientRepository`.
// - It returns the patient's information in the response body.
// - Instruction: Make sure that the token extraction process works correctly and patient details are fetched properly based on the extracted email.
// 9. **Handling Exceptions and Errors**:
// - The service methods handle exceptions using try-catch blocks and log any issues that occur. If an error occurs during database operations, the service responds with appropriate HTTP status codes (e.g., `500 Internal Server Error`).
// - Instruction: Ensure that error handling is consistent across the service, with proper logging and meaningful error messages returned to the client.
// 10. **Use of DTOs (Data Transfer Objects)**:
// - The service uses `AppointmentDTO` to transfer appointment-related data between layers. This ensures that sensitive or unnecessary data (e.g., password or private patient information) is not exposed in the response.
// - Instruction: Ensure that DTOs are used appropriately to limit the exposure of internal data and only send the relevant fields to the client.
}

View File

@@ -0,0 +1,34 @@
package com.project.back_end.services;
public class PrescriptionService {
// 1. **Add @Service Annotation**:
// - The `@Service` annotation marks this class as a Spring service component, allowing Spring's container to manage it.
// - This class contains the business logic related to managing prescriptions in the healthcare system.
// - Instruction: Ensure the `@Service` annotation is applied to mark this class as a Spring-managed service.
// 2. **Constructor Injection for Dependencies**:
// - The `PrescriptionService` class depends on the `PrescriptionRepository` to interact with the database.
// - It is injected through the constructor, ensuring proper dependency management and enabling testing.
// - Instruction: Constructor injection is a good practice, ensuring that all necessary dependencies are available at the time of service initialization.
// 3. **savePrescription Method**:
// - This method saves a new prescription to the database.
// - Before saving, it checks if a prescription already exists for the same appointment (using the appointment ID).
// - If a prescription exists, it returns a `400 Bad Request` with a message stating the prescription already exists.
// - If no prescription exists, it saves the new prescription and returns a `201 Created` status with a success message.
// - Instruction: Handle errors by providing appropriate status codes and messages, ensuring that multiple prescriptions for the same appointment are not saved.
// 4. **getPrescription Method**:
// - Retrieves a prescription associated with a specific appointment based on the `appointmentId`.
// - If a prescription is found, it returns it within a map wrapped in a `200 OK` status.
// - If there is an error while fetching the prescription, it logs the error and returns a `500 Internal Server Error` status with an error message.
// - Instruction: Ensure that this method handles edge cases, such as no prescriptions found for the given appointment, by returning meaningful responses.
// 5. **Exception Handling and Error Responses**:
// - Both methods (`savePrescription` and `getPrescription`) contain try-catch blocks to handle exceptions that may occur during database interaction.
// - If an error occurs, the method logs the error and returns an HTTP `500 Internal Server Error` response with a corresponding error message.
// - Instruction: Ensure that all potential exceptions are handled properly, and meaningful responses are returned to the client.
}

View File

@@ -0,0 +1,66 @@
package com.project.back_end.services;
public class Service {
// 1. **@Service Annotation**
// The @Service annotation marks this class as a service component in Spring. This allows Spring to automatically detect it through component scanning
// and manage its lifecycle, enabling it to be injected into controllers or other services using @Autowired or constructor injection.
// 2. **Constructor Injection for Dependencies**
// The constructor injects all required dependencies (TokenService, Repositories, and other Services). This approach promotes loose coupling, improves testability,
// and ensures that all required dependencies are provided at object creation time.
// 3. **validateToken Method**
// This method checks if the provided JWT token is valid for a specific user. It uses the TokenService to perform the validation.
// If the token is invalid or expired, it returns a 401 Unauthorized response with an appropriate error message. This ensures security by preventing
// unauthorized access to protected resources.
// 4. **validateAdmin Method**
// This method validates the login credentials for an admin user.
// - It first searches the admin repository using the provided username.
// - If an admin is found, it checks if the password matches.
// - If the password is correct, it generates and returns a JWT token (using the admins username) with a 200 OK status.
// - If the password is incorrect, it returns a 401 Unauthorized status with an error message.
// - If no admin is found, it also returns a 401 Unauthorized.
// - If any unexpected error occurs during the process, a 500 Internal Server Error response is returned.
// This method ensures that only valid admin users can access secured parts of the system.
// 5. **filterDoctor Method**
// This method provides filtering functionality for doctors based on name, specialty, and available time slots.
// - It supports various combinations of the three filters.
// - If none of the filters are provided, it returns all available doctors.
// This flexible filtering mechanism allows the frontend or consumers of the API to search and narrow down doctors based on user criteria.
// 6. **validateAppointment Method**
// This method validates if the requested appointment time for a doctor is available.
// - It first checks if the doctor exists in the repository.
// - Then, it retrieves the list of available time slots for the doctor on the specified date.
// - It compares the requested appointment time with the start times of these slots.
// - If a match is found, it returns 1 (valid appointment time).
// - If no matching time slot is found, it returns 0 (invalid).
// - If the doctor doesnt exist, it returns -1.
// This logic prevents overlapping or invalid appointment bookings.
// 7. **validatePatient Method**
// This method checks whether a patient with the same email or phone number already exists in the system.
// - If a match is found, it returns false (indicating the patient is not valid for new registration).
// - If no match is found, it returns true.
// This helps enforce uniqueness constraints on patient records and prevent duplicate entries.
// 8. **validatePatientLogin Method**
// This method handles login validation for patient users.
// - It looks up the patient by email.
// - If found, it checks whether the provided password matches the stored one.
// - On successful validation, it generates a JWT token and returns it with a 200 OK status.
// - If the password is incorrect or the patient doesn't exist, it returns a 401 Unauthorized with a relevant error.
// - If an exception occurs, it returns a 500 Internal Server Error.
// This method ensures only legitimate patients can log in and access their data securely.
// 9. **filterPatient Method**
// This method filters a patient's appointment history based on condition and doctor name.
// - It extracts the email from the JWT token to identify the patient.
// - Depending on which filters (condition, doctor name) are provided, it delegates the filtering logic to PatientService.
// - If no filters are provided, it retrieves all appointments for the patient.
// This flexible method supports patient-specific querying and enhances user experience on the client side.
}

View File

@@ -0,0 +1,43 @@
package com.project.back_end.services;
public class TokenService {
// 1. **@Component Annotation**
// The @Component annotation marks this class as a Spring component, meaning Spring will manage it as a bean within its application context.
// This allows the class to be injected into other Spring-managed components (like services or controllers) where it's needed.
// 2. **Constructor Injection for Dependencies**
// The constructor injects dependencies for `AdminRepository`, `DoctorRepository`, and `PatientRepository`,
// allowing the service to interact with the database and validate users based on their role (admin, doctor, or patient).
// Constructor injection ensures that the class is initialized with all required dependencies, promoting immutability and making the class testable.
// 3. **getSigningKey Method**
// This method retrieves the HMAC SHA key used to sign JWT tokens.
// It uses the `jwt.secret` value, which is provided from an external source (like application properties).
// The `Keys.hmacShaKeyFor()` method converts the secret key string into a valid `SecretKey` for signing and verification of JWTs.
// 4. **generateToken Method**
// This method generates a JWT token for a user based on their email.
// - The `subject` of the token is set to the user's email, which is used as an identifier.
// - The `issuedAt` is set to the current date and time.
// - The `expiration` is set to 7 days from the issue date, ensuring the token expires after one week.
// - The token is signed using the signing key generated by `getSigningKey()`, making it secure and tamper-proof.
// The method returns the JWT token as a string.
// 5. **extractEmail Method**
// This method extracts the user's email (subject) from the provided JWT token.
// - The token is first verified using the signing key to ensure it hasnt been tampered with.
// - After verification, the token is parsed, and the subject (which represents the email) is extracted.
// This method allows the application to retrieve the user's identity (email) from the token for further use.
// 6. **validateToken Method**
// This method validates whether a provided JWT token is valid for a specific user role (admin, doctor, or patient).
// - It first extracts the email from the token using the `extractEmail()` method.
// - Depending on the role (`admin`, `doctor`, or `patient`), it checks the corresponding repository (AdminRepository, DoctorRepository, or PatientRepository)
// to see if a user with the extracted email exists.
// - If a match is found for the specified user role, it returns true, indicating the token is valid.
// - If the role or user does not exist, it returns false, indicating the token is invalid.
// - The method gracefully handles any errors by returning false if the token is invalid or an exception occurs.
// This ensures secure access control based on the user's role and their existence in the system.
}

View File

@@ -0,0 +1,33 @@
spring.application.name=back-end
spring.datasource.url=jdbc:mysql://<mysql_host>/cms?usessl=false
spring.datasource.username=root
spring.datasource.password=<mysql_password>
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.open-in-view=false
spring.data.mongodb.uri=mongodb://root:<mongodb_password>@<mongodb_host>:27017/prescriptions?authSource=admin"
management.endpoint.health.show-details=always
management.health.db.enabled=true
api.path=/
jwt.secret=$!@#$^%$$$%####$DDCPN0234FCFDPD8670M
spring.web.resources.static-locations=classpath:/static/
# -------------------------
# Thymeleaf Configuration
# -------------------------
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML
spring.thymeleaf.cache=false
spring.thymeleaf.encoding=UTF-8

View File

@@ -0,0 +1,91 @@
/* This is the stylesheet for the addPrescription.html */
.form-container {
width: 100%;
max-width: 600px;
background: #ffffffc8;
margin: 50px auto;
padding: 30px;
border-radius: 15px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
animation: fadeIn 0.5s ease-in-out;
}
.container h2 {
text-align: center;
color: #222;
font-size: 28px;
margin-bottom: 25px;
}
.container h2 span {
color: #A62B1F;
}
label {
display: block;
margin-bottom: 6px;
font-weight: 500;
color: #333;
text-align: left;
font-size: 13px;
}
input, textarea {
width: 100%;
padding: 12px;
resize: vertical;
margin-bottom: 20px;
border: 1px solid #cccccc6e;
border-radius: 8px;
font-size: 15px;
outline: none;
transition: 0.3s;
font-family: "Verdana";
}
input:focus, textarea:focus {
border-color: #015c5d;
box-shadow: 0 0 4px #a7f3d0;
filter: brightness(1.5);
}
#patientName{
background-color: #bdbcbc80;
}
.btn-primary {
background-color: #047857;
color: #fff;
padding: 14px;
width: 100%;
border: none;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
transition: 0.3s;
}
.btn-primary:hover {
background-color: #065f46;
}
.btn-secondary {
background-color: transparent;
color: #333;
border: 1px solid #ccc;
padding: 12px;
width: 100%;
border-radius: 8px;
font-size: 15px;
margin-top: 12px;
cursor: pointer;
}
.btn-secondary:hover{
color: #fff;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(30px); }
to { opacity: 1; transform: translateY(0); }
}

View File

@@ -0,0 +1,94 @@
/*this is the style sheet for the adminDashboard. */
/*
Global Reset (`*`)
* Purpose: Ensure consistent layout across all browsers.
* Properties:
* `margin: 0` — removes default outer spacing from all elements.
* `padding: 0` — removes default inner spacing.
* `box-sizing: border-box` — ensures padding and borders are included within the total width/height of elements.
Base HTML & Body (`html, body`)
* Purpose: Establish a full-height layout and apply a standard font across the page.
* Properties:
* `height: 100%` — allows containers to stretch to the full viewport height.
* `font-family: Arial, sans-serif` — applies a clean, readable sans-serif font.
Main Layout Wrapper (`.wrapper`)
* Purpose: Create a flexible, vertical layout that fills the entire screen.
* Properties:
* `display: flex` — enables flexbox for layout control.
* `flex-direction: column` — stacks children vertically.
* `min-height: 100vh` — ensures the wrapper always covers full screen height.
Section Heading (`h2`)
* Purpose: Style the headings prominently for section separation.
* Properties:
* `font-size: 32px` — makes the heading large and noticeable.
* `font-weight: bold` — emphasizes text.
* `margin-bottom: 50px` — creates space below the heading.
* `color: #003e3e` — uses a strong teal for visual anchoring.
Main Content Section (`.main-content`)
* Purpose: Layout the main content with padding and a background image.
* Properties:
* `flex-grow: 1` — lets the content fill the available vertical space.
* `padding: 40px` — adds internal spacing around content.
* `display: flex` and `flex-direction: column` — organizes content in a vertical stack.
* `text-align: center` — centers the text.
* `background-image: url("index.png")` — sets a full-screen dashboard background.
* `background-size: cover` — ensures the image covers the whole section.
* `background-position: center` — centers the image.
* `background-repeat: no-repeat` — avoids tiling the image.
* `background-attachment: fixed` — fixes the image while scrolling.
Primary Button (`button`)
* Purpose: Design prominent, interactive buttons for main actions.
* Properties:
* `background-color: #A62B1F` — sets a bold red color.
* `color: white` — makes text stand out.
* `border: none` — removes any outline/border.
* `border-radius: 12px` — creates rounded corners.
* `padding: 10px 20px` — gives space inside the button.
* `margin-top: 20px` — adds spacing above the button.
* `width: 200px` — ensures consistent button width.
* `cursor: pointer` — shows a hand icon on hover.
* `font-size: 16px` — makes the text readable.
* On Hover:
* Retains the same red background to maintain branding.
Admin Button (`.adminBtn`)
* Purpose: Provide a secondary button style for admin-specific actions.
* Properties:
* `background-color: #f4f4f4` — uses a light grey background.
* `color: #333` — uses dark text for contrast.
* `font-weight: bold` — emphasizes text.
* `width: 110px` — smaller, compact width.
* `padding: 10px 0` — vertical padding only.
* `border: none` — clean look.
* `cursor: pointer` — indicates it's clickable.
* On Hover**:
Changes text color to `#A62B1F` for brand consistency while keeping the background same.
*/

View File

@@ -0,0 +1,65 @@
/* This is the style sheet for the doctorDashboard.html */
/*
**Table Header (`.table-header`)**
* Set font size to `24px` for clear emphasis.
* Use a dark teal color (`#015c5d`) for consistency with the theme.
* Add `30px` bottom margin and remove right margin.
**Table Styling (`table`)**
* Make the table full width (`100%`).
* Collapse borders for clean lines.
* Use a `sans-serif` font.
* Add `20px` top margin for spacing.
**Table Head (`thead`)**
* Set a very light dark background (`#33333310`) for header row.
* Use white text color (`#fff`).
**Table Cells (`td, th`)**
* Apply padding of `12px 16px` for spacing.
* Center-align the text.
* Add a subtle bottom border (`1px solid #ddd`).
**Table Row Styling**
* Alternate row colors for better readability:
* Even rows: semi-transparent light grey (`#f9f9f959`).
* Odd rows: soft white background (`#ffffffc0`).
* On hover: highlight row with a light teal overlay (`#015c5d39`).
**Prescription Button (`.prescription-btn`)**
* Set size to `30px x 30px`.
* Make it interactive with a pointer cursor.
* Add transition effects for scale and brightness on hover.
* On hover:
* Slightly enlarge (`scale(1.1)`)
* Brighten appearance
* On click: slightly shrink (`scale(0.95)`).
**No Record Message (`.noPatientRecord`)**
* Use a `16px` italic grey font to indicate no data gently.
**Today Button (`.today-btn`)**
* Add right margin (`10px`).
* Add padding (`10px`).
* Set background color to teal green (`#015c5d`).
**Date Picker (`.date-picker`)**
* Add left margin (`10px`) and top margin (`13px`).
* Round corners with `8px` radius.
* Add padding (`10px`) and set fixed height (`30px`).
---
Let me know if you'd like a consolidated style guide for the entire HospitalCRM frontend.
*/

View File

@@ -0,0 +1,53 @@
/* this is the style sheet file for index.htlm */
/*
**Global Reset (`*`)**
* Remove all default margin and padding.
* Use `border-box` for sizing to include padding and border in element size.
**Base (`html, body`)**
* Set height to 100% for full viewport layout.
* Use the `'Roboto'` sans-serif font for modern, clean typography.
**Layout Wrapper (`.wrapper`)**
* Use a vertical flexbox layout.
* Ensure the wrapper covers at least the full height of the screen (`min-height: 100vh`).
**Heading (`h2`)**
* Set a large font size (`48px`) for prominence.
* Add a bottom margin of `50px` for spacing.
* Use a dark teal color (`#003e3e`).
* Make the font bold and use a blended font stack (`'Alegreya', 'Roboto', sans-serif`) for elegant styling.
**Main Section (`.main-content`)**
* Allow the section to grow to fill space with `flex-grow: 1`.
* Add `40px` of internal padding.
* Center content both horizontally and vertically using `justify-content` and `align-items`.
* Display items in a column direction.
* Center all text.
* Apply a full-screen background image with:
* `cover` size
* `center` positioning
* no repeat
* `fixed` attachment to keep image still during scroll
**Buttons (`button`)**
* Style with a teal green background (`#015c5d`) and white text.
* Remove borders and round the corners (`12px`).
* Use `10px 20px` padding and set width to `200px`.
* Add a top margin of `10px` for spacing.
* Enable pointer cursor and readable font size (`16px`).
* On hover, change background color to bold red (`#A62B1F`).
**Dashboard Buttons (`.dashboard-btn`)**
* Use the same base green background (`#015c5d`) as the default button.
* On hover, darken the background to deep teal (`#003e3e`) for visual feedback.
*/

View File

@@ -0,0 +1,80 @@
/* This is the style sheet for the patientDashboard.html */
.card-actions {
background-color: #244557;
padding: 10px;
text-align: center;
}
.card-actions:hover{
background-color: #1c3745;
}
.ripple-overlay {
position: fixed;
width: 20px;
height: 20px;
background-color: #1c3745;
border-radius: 50%;
z-index: 9999;
transform: translate(-50%, -50%) scale(0);
transition: transform 1s ease-out, opacity 1s ease;
}
.ripple-overlay.active {
transform: translate(-50%, -50%) scale(150);
opacity: 1;
}
.modalApp {
position: fixed;
bottom: -100%;
left: 50%;
transform: translateX(-50%);
width: 50%;
background: #fff;
padding: 20px;
z-index: 10000;
transition: bottom 1.5s ease-in;
box-shadow: 0 -5px 10px rgba(0, 0, 0, 0.3);
border-radius: 10px 10px 0 0;
text-align: center;
}
.modalApp.active {
bottom: 0;
}
.modalApp h2 {
margin-top: 20px;
}
.modalApp input {
display: block;
margin: 10px auto;
padding: 10px;
width: 90%;
margin-bottom: 40px;
}
.modalApp select {
display: block;
margin: 10px auto;
padding: 10px;
width: 90%;
margin-bottom: 40px;
}
.confirm-booking {
padding: 10px 20px;
background: #1c3745;
color: #fff;
border: none;
cursor: pointer;
}
.confirm-booking:hover {
filter: brightness(1.2);
background-color: #1c3745;
}

View File

@@ -0,0 +1,382 @@
/* This is the style sheet for global change across the files. */
/* style.css */
.header {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #f4f4f4;
color: #333;
padding: 10px 20px;
border-bottom: 2px solid #ccc;
flex-wrap: wrap;
min-height: 60px;
}
/* Logo section with image + title */
.logo-link{
display: flex;
align-items: center;
gap: 10px;
}
.logo-img {
height: 40px;
width: auto;
max-width: 100%;
object-fit: contain;
}
/* Logo text / slogan */
.logo-title {
font-size: 20px;
font-weight: bold;
color: #333;
}
/* Nav links styling */
nav a {
margin-left: 20px;
margin-right: 20px;
text-decoration: none;
color: #333;
font-weight: bold;
}
nav a:hover {
color: #003e3e;
}
/* Responsive behavior */
@media (max-width: 600px) {
.header {
flex-direction: column;
align-items: flex-start;
}
nav {
margin-top: 10px;
}
nav a {
margin-left: 0;
margin-right: 10px;
}
.logo-img {
height: 30px;
}
.logo-title {
font-size: 18px;
}
}
.doctorHeader{
margin-right: 30px
}
.doctorHeader:hover{
color:#A62B1F;
}
/* Footer Section */
.footer {
background-color: #f4f4f4;
color: #fff;
padding: 40px 20px;
border-top: 2px solid #f4f4f4;
}
.footer-container {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: 30px;
max-width: 1200px;
margin: 0 auto;
}
.footer-logo {
flex: 1 1 250px;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 15px;
}
.footer-logo img {
height: 50px;
width: auto;
}
.footer-logo p {
font-size: 14px;
color: #0a0a23;
}
.footer-links {
display: flex;
flex: 2 1 500px;
justify-content: space-between;
flex-wrap: wrap;
gap: 20px;
}
.footer-column {
display: flex;
flex-direction: column;
gap: 10px;
}
.footer-column h4 {
font-size: 16px;
font-weight: bold;
margin-bottom: 8px;
color: #0a0a23;
}
.footer-column a {
text-decoration: none;
color: #0a0a23;
font-size: 14px;
}
.footer-column a:hover {
color: #003e3e;
transition: color 0.3s ease;
}
/* Mobile Responsiveness */
@media (max-width: 768px) {
.footer-container {
flex-direction: column;
align-items: flex-start;
}
.footer-links {
flex-direction: column;
gap: 20px;
}
}
.modal {
display: none;
position: fixed;
z-index: 999;
padding-top: 60px;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.6);
}
.modal-content {
background-color: white;
margin: auto;
padding: 30px;
border-radius: 10px;
width: 400px;
position: relative;
}
.modal-content h2{
font-size: 32px;
}
.close {
color: #aaa;
position: absolute;
right: 20px;
top: 15px;
font-size: 28px;
font-weight: bold;
cursor: pointer;
}
.close:hover {
color: black;
}
.dashboard-btn{
width: 100%;
}
.input-field {
padding: 10px;
border: 1px solid #ccc;
font-size: 16px;
margin-top: 10px;
margin-bottom: 20px;
width: 100%;
appearance: none; /* Remove default OS styles */
background-color: #fff;
color: #333;
cursor: pointer;
border-radius: 8px;
}
.input-field:hover {
border-color: #015c5d;
background-color: #f0fdfd;
}
.input-field:focus {
outline: none;
border-color: #015c5d;
box-shadow: 0 0 5px rgba(1, 92, 93, 0.5);
filter: brightness(1.2);
}
.select-dropdown option {
font-size: 16px;
padding: 10px;
}
#content {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(315px, 1fr));
gap: 40px;
padding: 20px;
}
.doctor-card {
display: flex;
flex-direction: column;
justify-content: space-between;
border: 1px solid #ddd;
border-radius: 10px;
width:auto;
overflow: hidden;
background-color: #fff;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
transition: transform 0.2s ease;
}
.doctor-card:hover {
transform: scale(1.02);
}
.doctor-info {
padding: 12px;
flex-grow: 1;
}
.doctor-info h3 {
margin-top: 30px;
font-size: 24px;
color: #015c5d;
}
.doctor-info p {
margin: 20px 0px;
font-size: 14px;
color: #555;
}
.card-actions {
background-color: #e28282;
padding: 10px;
text-align: center;
}
.card-actions:hover {
background-color: #d26969;
}
.card-actions button {
background: transparent;
border: none;
color: #fff;
font-weight: bold;
cursor: pointer;
}
.card-actions button:hover {
color: #fff;
border-color: #015c5d;
}
#roleSelector {
width:auto;
border-radius: 10px;
}
span {
color : #A62B1F;
}
.searchBar{
height: 30px;
padding:20px;
font-size: 16px;
margin-bottom: 30px;
border: 0.2px solid grey;
}
.searchBar:focus{
outline: none;
border-color: #015c5d;
box-shadow: 0 0 5px rgba(1, 92, 93, 0.697);
}
.filter-wrapper {
width: 350px;
display: flex;
flex-direction: row;
}
.filter-select {
margin-right: 8px;
width: 100%;
padding: 8px 12px;
border: 1px solid #ccc;
border-radius: 6px;
background-color: #fff;
color: #333;
font-size: 14px;
background-position: right 10px center;
background-size: 16px 16px;
cursor: pointer;
transition: border-color 0.2s, box-shadow 0.2s;
}
.filter-select:focus {
border-color: #015c5d;
box-shadow: 0 0 5px rgba(1, 92, 93, 0.697);
outline: none;
}
.availabilityLabel{
margin-bottom: 8px;
}
.availability-container {
margin-bottom: 1rem;
}
.checkbox-group {
display: flex;
flex-direction: column;
gap: 1rem;
margin-top: 1rem;
}
.checkbox-group label {
font-weight: 500;
cursor: pointer;
}
.checkbox-group input[type="checkbox"] {
margin-right: 0.5rem;
accent-color: #015c5db8;
}

View File

@@ -0,0 +1,89 @@
/* This is the style sheet for the updateAppointment.html */
.form-container {
max-width: 600px;
background: #ffffffc8;
margin: 10px auto;
padding: 30px;
border-radius: 15px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
animation: fadeIn 0.5s ease-in-out;
}
.container h2 {
text-align: center;
color: #222;
font-size: 28px;
margin-bottom: 25px;
}
.container h2 span {
color: #A62B1F;
}
label {
display: block;
margin-bottom: 6px;
font-weight: 500;
color: #333;
text-align: left;
font-size: 13px;
}
input, textarea ,select {
width: 100%;
padding: 12px;
resize: vertical;
margin-bottom: 20px;
border: 1px solid #cccccc6e;
border-radius: 8px;
font-size: 15px;
outline: none;
transition: 0.3s;
font-family: "Verdana";
}
input:focus, textarea:focus {
border-color: #015c5d;
box-shadow: 0 0 4px #a7f3d0;
filter: brightness(1.5);
}
#patientName{
background-color: #bdbcbc80;
}
.btn-primary {
background-color: #047857;
color: #fff;
padding: 14px;
width: 100%;
border: none;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
transition: 0.3s;
}
.btn-primary:hover {
background-color: #065f46;
}
.btn-secondary {
background-color: transparent;
color: #333;
border: 1px solid #ccc;
padding: 12px;
width: 100%;
border-radius: 8px;
font-size: 15px;
margin-top: 12px;
cursor: pointer;
}
.btn-secondary:hover{
color: #fff;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(30px); }
to { opacity: 1; transform: translateY(0); }
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

View File

@@ -0,0 +1,104 @@
<!-- index.html -->
<!--
Step-by-Step Explanation of `index.html`.
This file allows users to select a role (Admin, Patient, Doctor)
and triggers modal-based login functionality.
1. Label the File
* Add a comment at the very top to identify the file as `index.html`. This helps developers stay organized.
2. Declare Document Type
* Use the declaration for HTML5 so browsers know how to render the page correctly.
3. Start the HTML Document
* Begin the HTML structure and set the document language to English to improve accessibility and SEO `lang="en"`.
4. Head Section (Metadata and Resources)
* Open the `<head>` section which contains settings and links that dont show up visually.
5. Set Character Encoding
* Specify UTF-8 encoding to support most global characters and symbols.
6. Set the Page Title
* Give the page a name (e.g., "Select Role") that appears on the browser tab.
7. Add a Favicon
* Link to an icon (like a logo) that will appear in the browser tab from the address `href="../assets/images/logo/Logo.png"`
8. Link Stylesheets
* Add a page-specific stylesheet for layout and design.
* Also include a global stylesheet for shared site-wide styles.
HINT:
`<link rel="stylesheet" href="../assets/css/index.css">`
`<link rel="stylesheet" href="../assets/css/style.css">`
9. Add JavaScript Files with `defer`
* Link utility and rendering JavaScript files (e.g., `util.js`, `render.js`).
* Include scripts for rendering components like header and footer.
* Use the `defer` attribute so they load after the HTML is parsed.
HINT:
`<script src="../js/util.js" defer></script>`
`<script src="../js/render.js" defer></script>`
`<script src="../js/components/header.js" defer></script>`
`<script src="../js/components/footer.js" defer></script>`
10. Close the Head and Open the Body
* Wrap up metadata and begin the visible page content.
11. Create a Container
* Add a main container to hold all page elements and apply layout constraints.
12. Use a Wrapper for Structure
* Inside the container, add a wrapper to group header, main, and footer sections logically.
13. Insert a Header Placeholder
* Add a `div` that will be dynamically filled by JavaScript with the sites header.
14. Define the Main Section
* Use a semantic `<main>` tag to hold the central content of the page.
15. Add a Heading for Role Selection
* Display a heading like “Select Your Role” to instruct users.
16. Add Buttons for Roles
* Create buttons for each role: Admin, Patient, and Doctor.
* Assign unique identifiers or `onclick` handlers to them as needed.
17. Insert a Footer Placeholder
* Add a `div` that will be dynamically filled by JavaScript with the sites footer.
18. Create a Modal Structure
* Add a modal overlay for popups (like login forms).
* Include a close button and an inner container for dynamic content.
19. Load Page-Specific Logic Module
* Use a `<script type="module">` tag to load the role selection logic (e.g., `index.js`).
* Ensure its deferred so it doesnt block HTML rendering.
20. Close the Body and HTML Tags
* Properly close all opened tags to complete the document structure.
-->

View File

@@ -0,0 +1,80 @@
import { savePrescription, getPrescription } from "./services/prescriptionServices.js";
document.addEventListener('DOMContentLoaded', async () => {
const savePrescriptionBtn = document.getElementById("savePrescription");
const patientNameInput = document.getElementById("patientName");
const medicinesInput = document.getElementById("medicines");
const dosageInput = document.getElementById("dosage");
const notesInput = document.getElementById("notes");
const heading = document.getElementById("heading")
const urlParams = new URLSearchParams(window.location.search);
const appointmentId = urlParams.get("appointmentId");
const mode = urlParams.get("mode");
const token = localStorage.getItem("token");
const patientName = urlParams.get("patientName")
if (heading) {
if (mode === "view") {
heading.innerHTML = `View <span>Prescription</span>`;
} else {
heading.innerHTML = `Add <span>Prescription</span>`;
}
}
// Pre-fill patient name
if (patientNameInput && patientName) {
patientNameInput.value = patientName;
}
// Fetch and pre-fill existing prescription if it exists
if (appointmentId && token) {
try {
const response = await getPrescription(appointmentId, token);
console.log("getPrescription :: ", response);
// Now, check if the prescription exists in the response and access it from the array
if (response.prescription && response.prescription.length > 0) {
const existingPrescription = response.prescription[0]; // Access first prescription object
patientNameInput.value = existingPrescription.patientName || YOU;
medicinesInput.value = existingPrescription.medication || "";
dosageInput.value = existingPrescription.dosage || "";
notesInput.value = existingPrescription.doctorNotes || "";
}
} catch (error) {
console.warn("No existing prescription found or failed to load:", error);
}
}
if (mode === 'view') {
// Make fields read-only
patientNameInput.disabled = true;
medicinesInput.disabled = true;
dosageInput.disabled = true;
notesInput.disabled = true;
savePrescriptionBtn.style.display = "none"; // Hide the save button
}
// Save prescription on button click
savePrescriptionBtn.addEventListener('click', async (e) => {
e.preventDefault();
const prescription = {
patientName: patientNameInput.value,
medication: medicinesInput.value,
dosage: dosageInput.value,
doctorNotes: notesInput.value,
appointmentId
};
const { success, message } = await savePrescription(prescription, token);
if (success) {
alert("✅ Prescription saved successfully.");
selectRole('doctor');
} else {
alert("❌ Failed to save prescription. " + message);
}
});
});

View File

@@ -0,0 +1,72 @@
/*
This script handles the admin dashboard functionality for managing doctors:
- Loads all doctor cards
- Filters doctors by name, time, or specialty
- Adds a new doctor via modal form
Attach a click listener to the "Add Doctor" button
When clicked, it opens a modal form using openModal('addDoctor')
When the DOM is fully loaded:
- Call loadDoctorCards() to fetch and display all doctors
Function: loadDoctorCards
Purpose: Fetch all doctors and display them as cards
Call getDoctors() from the service layer
Clear the current content area
For each doctor returned:
- Create a doctor card using createDoctorCard()
- Append it to the content div
Handle any fetch errors by logging them
Attach 'input' and 'change' event listeners to the search bar and filter dropdowns
On any input change, call filterDoctorsOnChange()
Function: filterDoctorsOnChange
Purpose: Filter doctors based on name, available time, and specialty
Read values from the search bar and filters
Normalize empty values to null
Call filterDoctors(name, time, specialty) from the service
If doctors are found:
- Render them using createDoctorCard()
If no doctors match the filter:
- Show a message: "No doctors found with the given filters."
Catch and display any errors with an alert
Function: renderDoctorCards
Purpose: A helper function to render a list of doctors passed to it
Clear the content area
Loop through the doctors and append each card to the content area
Function: adminAddDoctor
Purpose: Collect form data and add a new doctor to the system
Collect input values from the modal form
- Includes name, email, phone, password, specialty, and available times
Retrieve the authentication token from localStorage
- If no token is found, show an alert and stop execution
Build a doctor object with the form values
Call saveDoctor(doctor, token) from the service
If save is successful:
- Show a success message
- Close the modal and reload the page
If saving fails, show an error message
*/

View File

@@ -0,0 +1,44 @@
// appointmentRecord.js
import { getAppointments } from "./components/appointmentRow.js";
import { getAppointmentRecord } from "./services/appointmentRecordService.js";
const tableBody = document.getElementById("patientTableBody");
const filterSelect = document.getElementById("appointmentFilter");
async function loadAppointments(filter = "upcoming") {
const appointments = await getAppointmentRecord();
if (!appointments || appointments.length === 0) {
tableBody.innerHTML = `<tr><td class="noPatientRecord" colspan='5'>No appointments found.</td></tr>`;
return;
}
const today = new Date().setHours(0, 0, 0, 0);
let filteredAppointments = appointments;
if (filter === "upcoming") {
filteredAppointments = appointments.filter(app => new Date(app.date) >= today);
} else if (filter === "past") {
filteredAppointments = appointments.filter(app => new Date(app.date) < today);
}
if (filteredAppointments.length === 0) {
tableBody.innerHTML = `<tr><td class="noPatientRecord" colspan='5'>No ${filter} appointments found.</td></tr>`;
return;
}
tableBody.innerHTML = "";
filteredAppointments.forEach(appointment => {
const row = getAppointments(appointment);
tableBody.appendChild(row);
});
}
// Handle filter change
filterSelect.addEventListener("change", (e) => {
const selectedFilter = e.target.value;
loadAppointments(selectedFilter);
});
// Load upcoming appointments by default
loadAppointments("upcoming");

View File

@@ -0,0 +1,19 @@
// appointmentRow.js
export function getAppointments(appointment) {
const tr = document.createElement("tr");
tr.innerHTML = `
<td class="patient-id">${appointment.patientName}</td>
<td>${appointment.doctorName}</td>
<td>${appointment.date}</td>
<td>${appointment.time}</td>
<td><img src="../assets/images/edit/edit.png" alt="action" class="prescription-btn" data-id="${appointment.id}"></img></td>
`;
// Attach event listeners
tr.querySelector(".prescription-btn").addEventListener("click", () => {
window.location.href = `addPrescription.html?id=${patient.id}`;
});
return tr;
}

View File

@@ -0,0 +1,41 @@
/*
Import the overlay function for booking appointments from loggedPatient.js
Import the deleteDoctor API function to remove doctors (admin role) from docotrServices.js
Import function to fetch patient details (used during booking) from patientServices.js
Function to create and return a DOM element for a single doctor card
Create the main container for the doctor card
Retrieve the current user role from localStorage
Create a div to hold doctor information
Create and set the doctors name
Create and set the doctor's specialization
Create and set the doctor's email
Create and list available appointment times
Append all info elements to the doctor info container
Create a container for card action buttons
=== ADMIN ROLE ACTIONS ===
Create a delete button
Add click handler for delete button
Get the admin token from localStorage
Call API to delete the doctor
Show result and remove card if successful
Add delete button to actions container
=== PATIENT (NOT LOGGED-IN) ROLE ACTIONS ===
Create a book now button
Alert patient to log in before booking
Add button to actions container
=== LOGGED-IN PATIENT ROLE ACTIONS ===
Create a book now button
Handle booking logic for logged-in patient
Redirect if token not available
Fetch patient data with token
Show booking overlay UI with doctor and patient info
Add button to actions container
Append doctor info and action buttons to the car
Return the complete doctor card element
*/

View File

@@ -0,0 +1,104 @@
/*
Function to render the footer content into the page
Select the footer element from the DOM
Set the inner HTML of the footer element to include the footer content
This section dynamically generates the footer content for the web page, including the hospital's logo, copyright information, and various helpful links.
1. Insert Footer HTML Content
* The content is inserted into the `footer` element with the ID "footer" using `footer.innerHTML`.
* This is done dynamically via JavaScript to ensure that the footer is properly rendered across different pages.
2. Create the Footer Wrapper
* The `<footer>` tag with class `footer` wraps the entire footer content, ensuring that it is styled appropriately.
```html
<footer class="footer">
```
3. Create the Footer Container
* Inside the footer, a container div with the class `footer-container` holds the content to maintain proper alignment and spacing.
```html
<div class="footer-container">
```
4. Add the Hospital Logo and Copyright Info
* A `footer-logo` div contains the hospital's logo (an image element) and the copyright information.
- The `<img>` tag displays the logo, with an `alt` attribute for accessibility.
- The copyright text is displayed in a paragraph element.
```html
<div class="footer-logo">
<img src="../assets/images/logo/logo.png" alt="Hospital CMS Logo">
<p>© Copyright 2025. All Rights Reserved by Hospital CMS.</p>
</div>
```
5. Create the Links Section
* A `footer-links` div contains all the links grouped into three sections: Company, Support, and Legals.
* This structure helps to organize the footer content and makes it easier for users to find related links.
6. Add the 'Company' Links Column
* Inside the `footer-links` div, the first column represents company-related links.
- The section includes a header (`<h4>Company</h4>`) followed by links for "About", "Careers", and "Press".
```html
<div class="footer-column">
<h4>Company</h4>
<a href="#">About</a>
<a href="#">Careers</a>
<a href="#">Press</a>
</div>
```
7. Add the 'Support' Links Column
* The second column is dedicated to support-related links.
- It includes a header (`<h4>Support</h4>`) followed by links for "Account", "Help Center", and "Contact Us".
```html
<div class="footer-column">
<h4>Support</h4>
<a href="#">Account</a>
<a href="#">Help Center</a>
<a href="#">Contact Us</a>
</div>
```
8. Add the 'Legals' Links Column
* The third column contains legal-related links, such as "Terms & Conditions", "Privacy Policy", and "Licensing".
- The header (`<h4>Legals</h4>`) is followed by these links.
```html
<div class="footer-column">
<h4>Legals</h4>
<a href="#">Terms & Conditions</a>
<a href="#">Privacy Policy</a>
<a href="#">Licensing</a>
</div>
```
9. Close the Footer Container
* Close the `footer-container` div to ensure proper structure.
```html
</div> <!-- End of footer-container -->
```
10. Close the Footer Element
* Finally, close the `<footer>` tag to complete the footer section.
```html
</footer>
```
11. Footer Rendering Complete
* The `footer.innerHTML` code completes the dynamic rendering of the footer by injecting the structured HTML content into the `footer` element on the page.
Call the renderFooter function to populate the footer in the page
*/

View File

@@ -0,0 +1,125 @@
/*
Step-by-Step Explanation of Header Section Rendering
This code dynamically renders the header section of the page based on the user's role, session status, and available actions (such as login, logout, or role-switching).
1. Define the `renderHeader` Function
* The `renderHeader` function is responsible for rendering the entire header based on the user's session, role, and whether they are logged in.
2. Select the Header Div
* The `headerDiv` variable retrieves the HTML element with the ID `header`, where the header content will be inserted.
```javascript
const headerDiv = document.getElementById("header");
```
3. Check if the Current Page is the Root Page
* The `window.location.pathname` is checked to see if the current page is the root (`/`). If true, the user's session data (role) is removed from `localStorage`, and the header is rendered without any user-specific elements (just the logo and site title).
```javascript
if (window.location.pathname.endsWith("/")) {
localStorage.removeItem("userRole");
headerDiv.innerHTML = `
<header class="header">
<div class="logo-section">
<img src="../assets/images/logo/logo.png" alt="Hospital CRM Logo" class="logo-img">
<span class="logo-title">Hospital CMS</span>
</div>
</header>`;
return;
}
```
4. Retrieve the User's Role and Token from LocalStorage
* The `role` (user role like admin, patient, doctor) and `token` (authentication token) are retrieved from `localStorage` to determine the user's current session.
```javascript
const role = localStorage.getItem("userRole");
const token = localStorage.getItem("token");
```
5. Initialize Header Content
* The `headerContent` variable is initialized with basic header HTML (logo section), to which additional elements will be added based on the user's role.
```javascript
let headerContent = `<header class="header">
<div class="logo-section">
<img src="../assets/images/logo/logo.png" alt="Hospital CRM Logo" class="logo-img">
<span class="logo-title">Hospital CMS</span>
</div>
<nav>`;
```
6. Handle Session Expiry or Invalid Login
* If a user with a role like `loggedPatient`, `admin`, or `doctor` does not have a valid `token`, the session is considered expired or invalid. The user is logged out, and a message is shown.
```javascript
if ((role === "loggedPatient" || role === "admin" || role === "doctor") && !token) {
localStorage.removeItem("userRole");
alert("Session expired or invalid login. Please log in again.");
window.location.href = "/"; or a specific login page
return;
}
```
7. Add Role-Specific Header Content
* Depending on the user's role, different actions or buttons are rendered in the header:
- **Admin**: Can add a doctor and log out.
- **Doctor**: Has a home button and log out.
- **Patient**: Shows login and signup buttons.
- **LoggedPatient**: Has home, appointments, and logout options.
```javascript
else if (role === "admin") {
headerContent += `
<button id="addDocBtn" class="adminBtn" onclick="openModal('addDoctor')">Add Doctor</button>
<a href="#" onclick="logout()">Logout</a>`;
} else if (role === "doctor") {
headerContent += `
<button class="adminBtn" onclick="selectRole('doctor')">Home</button>
<a href="#" onclick="logout()">Logout</a>`;
} else if (role === "patient") {
headerContent += `
<button id="patientLogin" class="adminBtn">Login</button>
<button id="patientSignup" class="adminBtn">Sign Up</button>`;
} else if (role === "loggedPatient") {
headerContent += `
<button id="home" class="adminBtn" onclick="window.location.href='/pages/loggedPatientDashboard.html'">Home</button>
<button id="patientAppointments" class="adminBtn" onclick="window.location.href='/pages/patientAppointments.html'">Appointments</button>
<a href="#" onclick="logoutPatient()">Logout</a>`;
}
```
9. Close the Header Section
10. Render the Header Content
* Insert the dynamically generated `headerContent` into the `headerDiv` element.
```javascript
headerDiv.innerHTML = headerContent;
```
11. Attach Event Listeners to Header Buttons
* Call `attachHeaderButtonListeners` to add event listeners to any dynamically created buttons in the header (e.g., login, logout, home).
```javascript
attachHeaderButtonListeners();
```
### Helper Functions
13. **attachHeaderButtonListeners**: Adds event listeners to login buttons for "Doctor" and "Admin" roles. If clicked, it opens the respective login modal.
14. **logout**: Removes user session data and redirects the user to the root page.
15. **logoutPatient**: Removes the patient's session token and redirects to the patient dashboard.
16. **Render the Header**: Finally, the `renderHeader()` function is called to initialize the header rendering process when the page loads.
*/

View File

@@ -0,0 +1,101 @@
// modals.js
export function openModal(type) {
let modalContent = '';
if (type === 'addDoctor') {
modalContent = `
<h2>Add Doctor</h2>
<input type="text" id="doctorName" placeholder="Doctor Name" class="input-field">
<select id="specialization" class="input-field select-dropdown">
<option value="">Specialization</option>
<option value="cardiologist">Cardiologist</option>
<option value="dermatologist">Dermatologist</option>
<option value="neurologist">Neurologist</option>
<option value="pediatrician">Pediatrician</option>
<option value="orthopedic">Orthopedic</option>
<option value="gynecologist">Gynecologist</option>
<option value="psychiatrist">Psychiatrist</option>
<option value="dentist">Dentist</option>
<option value="ophthalmologist">Ophthalmologist</option>
<option value="ent">ENT Specialist</option>
<option value="urologist">Urologist</option>
<option value="oncologist">Oncologist</option>
<option value="gastroenterologist">Gastroenterologist</option>
<option value="general">General Physician</option>
</select>
<input type="email" id="doctorEmail" placeholder="Email" class="input-field">
<input type="password" id="doctorPassword" placeholder="Password" class="input-field">
<input type="text" id="doctorPhone" placeholder="Mobile No." class="input-field">
<div class="availability-container">
<label class="availabilityLabel">Select Availability:</label>
<div class="checkbox-group">
<label><input type="checkbox" name="availability" value="09:00-10:00"> 9:00 AM - 10:00 AM</label>
<label><input type="checkbox" name="availability" value="10:00-11:00"> 10:00 AM - 11:00 AM</label>
<label><input type="checkbox" name="availability" value="11:00-12:00"> 11:00 AM - 12:00 PM</label>
<label><input type="checkbox" name="availability" value="12:00-13:00"> 12:00 PM - 1:00 PM</label>
</div>
</div>
<button class="dashboard-btn" id="saveDoctorBtn">Save</button>
`;
} else if (type === 'patientLogin') {
modalContent = `
<h2>Patient Login</h2>
<input type="text" id="email" placeholder="Email" class="input-field">
<input type="password" id="password" placeholder="Password" class="input-field">
<button class="dashboard-btn" id="loginBtn">Login</button>
`;
}
else if (type === "patientSignup") {
modalContent = `
<h2>Patient Signup</h2>
<input type="text" id="name" placeholder="Name" class="input-field">
<input type="email" id="email" placeholder="Email" class="input-field">
<input type="password" id="password" placeholder="Password" class="input-field">
<input type="text" id="phone" placeholder="Phone" class="input-field">
<input type="text" id="address" placeholder="Address" class="input-field">
<button class="dashboard-btn" id="signupBtn">Signup</button>
`;
} else if (type === 'adminLogin') {
modalContent = `
<h2>Admin Login</h2>
<input type="text" id="username" name="username" placeholder="Username" class="input-field">
<input type="password" id="password" name="password" placeholder="Password" class="input-field">
<button class="dashboard-btn" id="adminLoginBtn" >Login</button>
`;
} else if (type === 'doctorLogin') {
modalContent = `
<h2>Doctor Login</h2>
<input type="text" id="email" placeholder="Email" class="input-field">
<input type="password" id="password" placeholder="Password" class="input-field">
<button class="dashboard-btn" id="doctorLoginBtn" >Login</button>
`;
}
document.getElementById('modal-body').innerHTML = modalContent;
document.getElementById('modal').style.display = 'block';
document.getElementById('closeModal').onclick = () => {
document.getElementById('modal').style.display = 'none';
};
if (type === "patientSignup") {
document.getElementById("signupBtn").addEventListener("click", signupPatient);
}
if (type === "patientLogin") {
document.getElementById("loginBtn").addEventListener("click", loginPatient);
}
if (type === 'addDoctor') {
document.getElementById('saveDoctorBtn').addEventListener('click', adminAddDoctor);
}
if (type === 'adminLogin') {
document.getElementById('adminLoginBtn').addEventListener('click', adminLoginHandler);
}
if (type === 'doctorLogin') {
document.getElementById('doctorLoginBtn').addEventListener('click', doctorLoginHandler);
}
}

View File

@@ -0,0 +1,17 @@
// patientRecordRow.js
export function createPatientRecordRow(patient) {
const tr = document.createElement("tr");
tr.innerHTML = `
<td class="patient-id">${patient.appointmentDate}</td>
<td>${patient.id}</td>
<td>${patient.patientId}</td>
<td><img src="../assets/images/addPrescriptionIcon/addPrescription.png" alt="addPrescriptionIcon" class="prescription-btn" data-id="${patient.id}"></img></td>
`;
// Attach event listeners
tr.querySelector(".prescription-btn").addEventListener("click", () => {
window.location.href = `/pages/addPrescription.html?mode=view&appointmentId=${patient.id}`;
});
return tr;
}

View File

@@ -0,0 +1,23 @@
// patientRows.js
export function createPatientRow(patient, appointmentId, doctorId) {
const tr = document.createElement("tr");
console.log("CreatePatientRow :: ", doctorId)
tr.innerHTML = `
<td class="patient-id">${patient.id}</td>
<td>${patient.name}</td>
<td>${patient.phone}</td>
<td>${patient.email}</td>
<td><img src="../assets/images/addPrescriptionIcon/addPrescription.png" alt="addPrescriptionIcon" class="prescription-btn" data-id="${patient.id}"></img></td>
`;
// Attach event listeners
tr.querySelector(".patient-id").addEventListener("click", () => {
window.location.href = `/pages/patientRecord.html?id=${patient.id}&doctorId=${doctorId}`;
});
tr.querySelector(".prescription-btn").addEventListener("click", () => {
window.location.href = `/pages/addPrescription.html?appointmentId=${appointmentId}&patientName=${patient.name}`;
});
return tr;
}

View File

@@ -0,0 +1,14 @@
// config.js
/**
* Configuration file for defining global constants and environment-specific settings.
*
* API_BASE_URL:
* - Base URL for all API requests made from the frontend.
* - Easily switchable for different environments (development, staging, production).
*
* Example usage:
* fetch(`${API_BASE_URL}/api/appointments`)
*/
export const API_BASE_URL = "http://localhost:8080";

View File

@@ -0,0 +1,54 @@
/*
Import getAllAppointments to fetch appointments from the backend
Import createPatientRow to generate a table row for each patient appointment
Get the table body where patient rows will be added
Initialize selectedDate with today's date in 'YYYY-MM-DD' format
Get the saved token from localStorage (used for authenticated API calls)
Initialize patientName to null (used for filtering by name)
Add an 'input' event listener to the search bar
On each keystroke:
- Trim and check the input value
- If not empty, use it as the patientName for filtering
- Else, reset patientName to "null" (as expected by backend)
- Reload the appointments list with the updated filter
Add a click listener to the "Today" button
When clicked:
- Set selectedDate to today's date
- Update the date picker UI to match
- Reload the appointments for today
Add a change event listener to the date picker
When the date changes:
- Update selectedDate with the new value
- Reload the appointments for that specific date
Function: loadAppointments
Purpose: Fetch and display appointments based on selected date and optional patient name
Step 1: Call getAllAppointments with selectedDate, patientName, and token
Step 2: Clear the table body content before rendering new rows
Step 3: If no appointments are returned:
- Display a message row: "No Appointments found for today."
Step 4: If appointments exist:
- Loop through each appointment and construct a 'patient' object with id, name, phone, and email
- Call createPatientRow to generate a table row for the appointment
- Append each row to the table body
Step 5: Catch and handle any errors during fetch:
- Show a message row: "Error loading appointments. Try again later."
When the page is fully loaded (DOMContentLoaded):
- Call renderContent() (assumes it sets up the UI layout)
- Call loadAppointments() to display today's appointments by default
*/

View File

@@ -0,0 +1,138 @@
// loggedPatient.js
import { getDoctors } from './services/doctorServices.js';
import { createDoctorCard } from './components/doctorCard.js';
import { filterDoctors } from './services/doctorServices.js';
import { bookAppointment } from './services/appointmentRecordService.js';
document.addEventListener("DOMContentLoaded", () => {
loadDoctorCards();
});
function loadDoctorCards() {
getDoctors()
.then(doctors => {
const contentDiv = document.getElementById("content");
contentDiv.innerHTML = "";
doctors.forEach(doctor => {
const card = createDoctorCard(doctor);
contentDiv.appendChild(card);
});
})
.catch(error => {
console.error("Failed to load doctors:", error);
});
}
export function showBookingOverlay(e, doctor, patient) {
const button = e.target;
const rect = button.getBoundingClientRect();
console.log(patient.name)
console.log(patient)
const ripple = document.createElement("div");
ripple.classList.add("ripple-overlay");
ripple.style.left = `${e.clientX}px`;
ripple.style.top = `${e.clientY}px`;
document.body.appendChild(ripple);
setTimeout(() => ripple.classList.add("active"), 50);
const modalApp = document.createElement("div");
modalApp.classList.add("modalApp");
modalApp.innerHTML = `
<h2>Book Appointment</h2>
<input class="input-field" type="text" value="${patient.name}" disabled />
<input class="input-field" type="text" value="${doctor.name}" disabled />
<input class="input-field" type="text" value="${doctor.specialty}" disabled/>
<input class="input-field" type="email" value="${doctor.email}" disabled/>
<input class="input-field" type="date" id="appointment-date" />
<select class="input-field" id="appointment-time">
<option value="">Select time</option>
${doctor.availableTimes.map(t => `<option value="${t}">${t}</option>`).join('')}
</select>
<button class="confirm-booking">Confirm Booking</button>
`;
document.body.appendChild(modalApp);
setTimeout(() => modalApp.classList.add("active"), 600);
modalApp.querySelector(".confirm-booking").addEventListener("click", async () => {
const date = modalApp.querySelector("#appointment-date").value;
const time = modalApp.querySelector("#appointment-time").value;
const token = localStorage.getItem("token");
const startTime = time.split('-')[0];
const appointment = {
doctor: { id: doctor.id },
patient: { id: patient.id },
appointmentTime: `${date}T${startTime}:00`,
status: 0
};
const { success, message } = await bookAppointment(appointment, token);
if (success) {
alert("Appointment Booked successfully");
ripple.remove();
modalApp.remove();
} else {
alert("❌ Failed to book an appointment :: " + message);
}
});
}
// Filter Input
document.getElementById("searchBar").addEventListener("input", filterDoctorsOnChange);
document.getElementById("filterTime").addEventListener("change", filterDoctorsOnChange);
document.getElementById("filterSpecialty").addEventListener("change", filterDoctorsOnChange);
function filterDoctorsOnChange() {
const searchBar = document.getElementById("searchBar").value.trim();
const filterTime = document.getElementById("filterTime").value;
const filterSpecialty = document.getElementById("filterSpecialty").value;
const name = searchBar.length > 0 ? searchBar : null;
const time = filterTime.length > 0 ? filterTime : null;
const specialty = filterSpecialty.length > 0 ? filterSpecialty : null;
filterDoctors(name, time, specialty)
.then(response => {
const doctors = response.doctors;
const contentDiv = document.getElementById("content");
contentDiv.innerHTML = "";
if (doctors.length > 0) {
console.log(doctors);
doctors.forEach(doctor => {
const card = createDoctorCard(doctor);
contentDiv.appendChild(card);
});
} else {
contentDiv.innerHTML = "<p>No doctors found with the given filters.</p>";
console.log("Nothing");
}
})
.catch(error => {
console.error("Failed to filter doctors:", error);
alert("❌ An error occurred while filtering doctors.");
});
}
export function renderDoctorCards(doctors) {
const contentDiv = document.getElementById("content");
contentDiv.innerHTML = "";
doctors.forEach(doctor => {
const card = createDoctorCard(doctor);
contentDiv.appendChild(card);
});
}

View File

@@ -0,0 +1,105 @@
// patientAppointment.js
import { getPatientAppointments, getPatientData, filterAppointments } from "./services/patientServices.js";
const tableBody = document.getElementById("patientTableBody");
const token = localStorage.getItem("token");
let allAppointments = [];
let filteredAppointments = [];
let patientId = null;
document.addEventListener("DOMContentLoaded", initializePage);
async function initializePage() {
try {
if (!token) throw new Error("No token found");
const patient = await getPatientData(token);
if (!patient) throw new Error("Failed to fetch patient details");
patientId = Number(patient.id);
const appointmentData = await getPatientAppointments(patientId, token, "patient") || [];
allAppointments = appointmentData.filter(app => app.patientId === patientId);
renderAppointments(allAppointments);
} catch (error) {
console.error("Error loading appointments:", error);
alert("❌ Failed to load your appointments.");
}
}
function renderAppointments(appointments) {
tableBody.innerHTML = "";
const actionTh = document.querySelector("#patientTable thead tr th:last-child");
if (actionTh) {
actionTh.style.display = "table-cell"; // Always show "Actions" column
}
if (!appointments.length) {
tableBody.innerHTML = `<tr><td colspan="5" style="text-align:center;">No Appointments Found</td></tr>`;
return;
}
appointments.forEach(appointment => {
const tr = document.createElement("tr");
tr.innerHTML = `
<td>${appointment.patientName || "You"}</td>
<td>${appointment.doctorName}</td>
<td>${appointment.appointmentDate}</td>
<td>${appointment.appointmentTimeOnly}</td>
<td>${appointment.status == 0 ? `<img src="../assets/images/edit/edit.png" alt="Edit" class="prescription-btn" data-id="${appointment.patientId}">` : "-"}</td>
`;
if (appointment.status == 0) {
const actionBtn = tr.querySelector(".prescription-btn");
actionBtn?.addEventListener("click", () => redirectToUpdatePage(appointment));
}
tableBody.appendChild(tr);
});
}
function redirectToUpdatePage(appointment) {
// Prepare the query parameters
const queryString = new URLSearchParams({
appointmentId: appointment.id,
patientId: appointment.patientId,
patientName: appointment.patientName || "You",
doctorName: appointment.doctorName,
doctorId: appointment.doctorId,
appointmentDate: appointment.appointmentDate,
appointmentTime: appointment.appointmentTimeOnly,
}).toString();
// Redirect to the update page with the query string
setTimeout(() => {
window.location.href = `/pages/updateAppointment.html?${queryString}`;
}, 100);
}
// Search and Filter Listeners
document.getElementById("searchBar").addEventListener("input", handleFilterChange);
document.getElementById("appointmentFilter").addEventListener("change", handleFilterChange);
async function handleFilterChange() {
const searchBarValue = document.getElementById("searchBar").value.trim();
const filterValue = document.getElementById("appointmentFilter").value;
const name = searchBarValue || null;
const condition = filterValue === "allAppointments" ? null : filterValue || null;
try {
const response = await filterAppointments(condition, name, token);
const appointments = response?.appointments || [];
filteredAppointments = appointments.filter(app => app.patientId === patientId);
renderAppointments(filteredAppointments);
} catch (error) {
console.error("Failed to filter appointments:", error);
alert("❌ An error occurred while filtering appointments.");
}
}

View File

@@ -0,0 +1,136 @@
// patientDashboard.js
import { getDoctors } from './services/doctorServices.js';
import { openModal } from './components/modals.js';
import { createDoctorCard } from './components/doctorCard.js';
import { filterDoctors } from './services/doctorServices.js';//call the same function to avoid duplication coz the functionality was same
import { patientSignup, patientLogin } from './services/patientServices.js';
document.addEventListener("DOMContentLoaded", () => {
loadDoctorCards();
});
document.addEventListener("DOMContentLoaded", () => {
const btn = document.getElementById("patientSignup");
if (btn) {
btn.addEventListener("click", () => openModal("patientSignup"));
}
});
document.addEventListener("DOMContentLoaded", () => {
const loginBtn = document.getElementById("patientLogin")
if (loginBtn) {
loginBtn.addEventListener("click", () => {
openModal("patientLogin")
})
}
})
function loadDoctorCards() {
getDoctors()
.then(doctors => {
const contentDiv = document.getElementById("content");
contentDiv.innerHTML = "";
doctors.forEach(doctor => {
const card = createDoctorCard(doctor);
contentDiv.appendChild(card);
});
})
.catch(error => {
console.error("Failed to load doctors:", error);
});
}
// Filter Input
document.getElementById("searchBar").addEventListener("input", filterDoctorsOnChange);
document.getElementById("filterTime").addEventListener("change", filterDoctorsOnChange);
document.getElementById("filterSpecialty").addEventListener("change", filterDoctorsOnChange);
function filterDoctorsOnChange() {
const searchBar = document.getElementById("searchBar").value.trim();
const filterTime = document.getElementById("filterTime").value;
const filterSpecialty = document.getElementById("filterSpecialty").value;
const name = searchBar.length > 0 ? searchBar : null;
const time = filterTime.length > 0 ? filterTime : null;
const specialty = filterSpecialty.length > 0 ? filterSpecialty : null;
filterDoctors(name, time, specialty)
.then(response => {
const doctors = response.doctors;
const contentDiv = document.getElementById("content");
contentDiv.innerHTML = "";
if (doctors.length > 0) {
console.log(doctors);
doctors.forEach(doctor => {
const card = createDoctorCard(doctor);
contentDiv.appendChild(card);
});
} else {
contentDiv.innerHTML = "<p>No doctors found with the given filters.</p>";
console.log("Nothing");
}
})
.catch(error => {
console.error("Failed to filter doctors:", error);
alert("❌ An error occurred while filtering doctors.");
});
}
window.signupPatient = async function () {
try {
const name = document.getElementById("name").value;
const email = document.getElementById("email").value;
const password = document.getElementById("password").value;
const phone = document.getElementById("phone").value;
const address = document.getElementById("address").value;
const data = { name, email, password, phone, address };
const { success, message } = await patientSignup(data);
if (success) {
alert(message);
document.getElementById("modal").style.display = "none";
window.location.reload();
}
else alert(message);
} catch (error) {
console.error("Signup failed:", error);
alert("❌ An error occurred while signing up.");
}
};
window.loginPatient = async function () {
try {
const email = document.getElementById("email").value;
const password = document.getElementById("password").value;
const data = {
email,
password
}
console.log("loginPatient :: ", data)
const response = await patientLogin(data);
console.log("Status Code:", response.status);
console.log("Response OK:", response.ok);
if (response.ok) {
const result = await response.json();
console.log(result);
selectRole('loggedPatient');
localStorage.setItem('token', result.token)
window.location.href = '/pages/loggedPatientDashboard.html';
} else {
alert('❌ Invalid credentials!');
}
}
catch (error) {
alert("❌ Failed to Login : ", error);
console.log("Error :: loginPatient :: ", error)
}
}

View File

@@ -0,0 +1,48 @@
// patientRecordServices.js
import { getPatientAppointments } from "./services/patientServices.js";
import { createPatientRecordRow } from './components/patientRecordRow.js';
const tableBody = document.getElementById("patientTableBody");
const token = localStorage.getItem("token");
const urlParams = new URLSearchParams(window.location.search);
const patientId = urlParams.get("id");
const doctorId = urlParams.get("doctorId");
document.addEventListener("DOMContentLoaded", initializePage);
async function initializePage() {
try {
if (!token) throw new Error("No token found");
const appointmentData = await getPatientAppointments(patientId, token, "doctor") || [];
// Filter by both patientId and doctorId
const filteredAppointments = appointmentData.filter(app =>
app.doctorId == doctorId);
console.log(filteredAppointments)
renderAppointments(filteredAppointments);
} catch (error) {
console.error("Error loading appointments:", error);
alert("❌ Failed to load your appointments.");
}
}
function renderAppointments(appointments) {
tableBody.innerHTML = "";
const actionTh = document.querySelector("#patientTable thead tr th:last-child");
if (actionTh) {
actionTh.style.display = "table-cell"; // Always show "Actions" column
}
if (!appointments.length) {
tableBody.innerHTML = `<tr><td colspan="5" style="text-align:center;">No Appointments Found</td></tr>`;
return;
}
appointments.forEach(appointment => {
const row = createPatientRecordRow(appointment);
tableBody.appendChild(row);
});
}

View File

@@ -0,0 +1,28 @@
// render.js
function selectRole(role) {
setRole(role);
const token = localStorage.getItem('token');
if (role === "admin") {
if (token) {
window.location.href = `/adminDashboard/${token}`;
}
} if (role === "patient") {
window.location.href = "/pages/patientDashboard.html";
} else if (role === "doctor") {
if (token) {
window.location.href = `/doctorDashboard/${token}`;
} else if (role === "loggedPatient") {
window.location.href = "loggedPatientDashboard.html";
}
}
}
function renderContent() {
const role = getRole();
if (!role) {
window.location.href = "/"; // if no role, send to role selection page
return;
}
}

View File

@@ -0,0 +1,62 @@
// appointmentRecordService.js
import { API_BASE_URL } from "../config/config.js";
const APPOINTMENT_API = `${API_BASE_URL}/appointments`;
//This is for the doctor to get all the patient Appointments
export async function getAllAppointments(date, patientName, token) {
const response = await fetch(`${APPOINTMENT_API}/${date}/${patientName}/${token}`);
if (!response.ok) {
throw new Error("Failed to fetch appointments");
}
return await response.json();
}
export async function bookAppointment(appointment, token) {
try {
const response = await fetch(`${APPOINTMENT_API}/${token}`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(appointment)
});
const data = await response.json();
return {
success: response.ok,
message: data.message || "Something went wrong"
};
} catch (error) {
console.error("Error while booking appointment:", error);
return {
success: false,
message: "Network error. Please try again later."
};
}
}
export async function updateAppointment(appointment, token) {
try {
const response = await fetch(`${APPOINTMENT_API}/${token}`, {
method: "PUT",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(appointment)
});
const data = await response.json();
return {
success: response.ok,
message: data.message || "Something went wrong"
};
} catch (error) {
console.error("Error while booking appointment:", error);
return {
success: false,
message: "Network error. Please try again later."
};
}
}

View File

@@ -0,0 +1,53 @@
/*
Import the base API URL from the config file
Define a constant DOCTOR_API to hold the full endpoint for doctor-related actions
Function: getDoctors
Purpose: Fetch the list of all doctors from the API
Use fetch() to send a GET request to the DOCTOR_API endpoint
Convert the response to JSON
Return the 'doctors' array from the response
If there's an error (e.g., network issue), log it and return an empty array
Function: deleteDoctor
Purpose: Delete a specific doctor using their ID and an authentication token
Use fetch() with the DELETE method
- The URL includes the doctor ID and token as path parameters
Convert the response to JSON
Return an object with:
- success: true if deletion was successful
- message: message from the server
If an error occurs, log it and return a default failure response
Function: saveDoctor
Purpose: Save (create) a new doctor using a POST request
Use fetch() with the POST method
- URL includes the token in the path
- Set headers to specify JSON content type
- Convert the doctor object to JSON in the request body
Parse the JSON response and return:
- success: whether the request succeeded
- message: from the server
Catch and log errors
- Return a failure response if an error occurs
Function: filterDoctors
Purpose: Fetch doctors based on filtering criteria (name, time, and specialty)
Use fetch() with the GET method
- Include the name, time, and specialty as URL path parameters
Check if the response is OK
- If yes, parse and return the doctor data
- If no, log the error and return an object with an empty 'doctors' array
Catch any other errors, alert the user, and return a default empty result
*/

View File

@@ -0,0 +1,58 @@
/*
Import the openModal function to handle showing login popups/modals
Import the base API URL from the config file
Define constants for the admin and doctor login API endpoints using the base URL
Use the window.onload event to ensure DOM elements are available after page load
Inside this function:
- Select the "adminLogin" and "doctorLogin" buttons using getElementById
- If the admin login button exists:
- Add a click event listener that calls openModal('adminLogin') to show the admin login modal
- If the doctor login button exists:
- Add a click event listener that calls openModal('doctorLogin') to show the doctor login modal
Define a function named adminLoginHandler on the global window object
This function will be triggered when the admin submits their login credentials
Step 1: Get the entered username and password from the input fields
Step 2: Create an admin object with these credentials
Step 3: Use fetch() to send a POST request to the ADMIN_API endpoint
- Set method to POST
- Add headers with 'Content-Type: application/json'
- Convert the admin object to JSON and send in the body
Step 4: If the response is successful:
- Parse the JSON response to get the token
- Store the token in localStorage
- Call selectRole('admin') to proceed with admin-specific behavior
Step 5: If login fails or credentials are invalid:
- Show an alert with an error message
Step 6: Wrap everything in a try-catch to handle network or server errors
- Show a generic error message if something goes wrong
Define a function named doctorLoginHandler on the global window object
This function will be triggered when a doctor submits their login credentials
Step 1: Get the entered email and password from the input fields
Step 2: Create a doctor object with these credentials
Step 3: Use fetch() to send a POST request to the DOCTOR_API endpoint
- Include headers and request body similar to admin login
Step 4: If login is successful:
- Parse the JSON response to get the token
- Store the token in localStorage
- Call selectRole('doctor') to proceed with doctor-specific behavior
Step 5: If login fails:
- Show an alert for invalid credentials
Step 6: Wrap in a try-catch block to handle errors gracefully
- Log the error to the console
- Show a generic error message
*/

View File

@@ -0,0 +1,97 @@
// patientServices
import { API_BASE_URL } from "../config/config.js";
const PATIENT_API = API_BASE_URL + '/patient'
//For creating a patient in db
export async function patientSignup(data) {
try {
const response = await fetch(`${PATIENT_API}`,
{
method: "POST",
headers: {
"Content-type": "application/json"
},
body: JSON.stringify(data)
}
);
const result = await response.json();
if (!response.ok) {
throw new Error(result.message);
}
return { success: response.ok, message: result.message }
}
catch (error) {
console.error("Error :: patientSignup :: ", error)
return { success: false, message: error.message }
}
}
//For logging in patient
export async function patientLogin(data) {
console.log("patientLogin :: ", data)
return await fetch(`${PATIENT_API}/login`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(data)
});
}
// For getting patient data (name ,id , etc ). Used in booking appointments
export async function getPatientData(token) {
try {
const response = await fetch(`${PATIENT_API}/${token}`);
const data = await response.json();
if (response.ok) return data.patient;
return null;
} catch (error) {
console.error("Error fetching patient details:", error);
return null;
}
}
// the Backend API for fetching the patient record(visible in Doctor Dashboard) and Appointments (visible in Patient Dashboard) are same based on user(patient/doctor).
export async function getPatientAppointments(id, token, user) {
try {
const response = await fetch(`${PATIENT_API}/${id}/${user}/${token}`);
const data = await response.json();
console.log(data.appointments)
if (response.ok) {
return data.appointments;
}
return null;
}
catch (error) {
console.error("Error fetching patient details:", error);
return null;
}
}
export async function filterAppointments(condition, name, token) {
try {
const response = await fetch(`${PATIENT_API}/filter/${condition}/${name}/${token}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
if (response.ok) {
const data = await response.json();
return data;
} else {
console.error("Failed to fetch doctors:", response.statusText);
return { appointments: [] };
}
} catch (error) {
console.error("Error:", error);
alert("Something went wrong!");
return { appointments: [] };
}
}

View File

@@ -0,0 +1,46 @@
// prescriptionServices.js
import { API_BASE_URL } from '../config/config.js'
const PRESCRITION_API = API_BASE_URL + "/prescription"
export async function savePrescription(prescription, token) {
try {
const response = await fetch(`${PRESCRITION_API}/${token}`, {
method: "POST",
headers: {
"Content-type": "application/json"
},
body: JSON.stringify(prescription)
});
const result = await response.json();
return { success: response.ok, message: result.message }
}
catch (error) {
console.error("Error :: savePrescription :: ", error)
return { success: false, message: result.message }
}
}
export async function getPrescription(appointmentId, token) {
try {
const response = await fetch(`${PRESCRITION_API}/${appointmentId}/${token}`, {
method: "GET",
headers: {
"Content-Type": "application/json"
}
});
if (!response.ok) {
const errorData = await response.json();
console.error("Failed to fetch prescription:", errorData);
throw new Error(errorData.message || "Unable to fetch prescription");
}
const result = await response.json();
console.log(result)
console.log(result)
return result; // This should be your prescription object
} catch (error) {
console.error("Error :: getPrescription ::", error);
throw error;
}
}

View File

@@ -0,0 +1,83 @@
// updateAppointment.js
import { updateAppointment } from "../js/services/appointmentRecordService.js";
import { getDoctors } from "../js/services/doctorServices.js";
document.addEventListener("DOMContentLoaded", initializePage);
async function initializePage() {
const token = localStorage.getItem("token"); // Assuming token is stored in localStorage
// Get appointmentId and patientId from the URL query parameters
const urlParams = new URLSearchParams(window.location.search);
const appointmentId = urlParams.get("appointmentId");
const patientId = urlParams.get("patientId");
const doctorId = urlParams.get("doctorId");
const patientName = urlParams.get("patientName");
const doctorName = urlParams.get("doctorName");
const appointmentDate = urlParams.get("appointmentDate");
const appointmentTime = urlParams.get("appointmentTime");
console.log(doctorId)
if (!token || !patientId) {
alert("Missing session data, redirecting to appointments page.");
window.location.href = "/pages/patientAppointments.html";
return;
}
// get doctor to display only the available time of doctor
getDoctors()
.then(doctors => {
// Find the doctor by the ID from the URL
const doctor = doctors.find(d => d.id == doctorId);
if (!doctor) {
alert("Doctor not found.");
return;
}
// Fill the form with the appointment data passed in the URL
document.getElementById("patientName").value = patientName || "You";
document.getElementById("doctorName").value = doctorName;
document.getElementById("appointmentDate").value = appointmentDate;
document.getElementById("appointmentTime").value = appointmentTime;
const timeSelect = document.getElementById("appointmentTime");
doctor.availableTimes.forEach(time => {
const option = document.createElement("option");
option.value = time;
option.textContent = time;
timeSelect.appendChild(option);
});
// Handle form submission for updating the appointment
document.getElementById("updateAppointmentForm").addEventListener("submit", async (e) => {
e.preventDefault(); // Prevent default form submission
const date = document.getElementById("appointmentDate").value;
const time = document.getElementById("appointmentTime").value;
const startTime = time.split('-')[0];
if (!date || !time) {
alert("Please select both date and time.");
return;
}
const updatedAppointment = {
id: appointmentId,
doctor: { id: doctor.id },
patient: { id: patientId },
appointmentTime: `${date}T${startTime}:00`,
status: 0
};
const updateResponse = await updateAppointment(updatedAppointment, token);
if (updateResponse.success) {
alert("Appointment updated successfully!");
window.location.href = "/pages/patientAppointments.html"; // Redirect back to the appointments page
} else {
alert("❌ Failed to update appointment: " + updateResponse.message);
}
});
})
.catch(error => {
console.error("Error fetching doctors:", error);
alert("❌ Failed to load doctor data.");
});
}

View File

@@ -0,0 +1,13 @@
// util.js
function setRole(role) {
localStorage.setItem("userRole", role);
}
function getRole() {
return localStorage.getItem("userRole");
}
function clearRole() {
localStorage.removeItem("userRole");
}

View File

@@ -0,0 +1,52 @@
<!-- addPrescription.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" type="image/svg+xml" href="../assets/images/logo/logo.png" />
<title>DoctorDashboard</title>
<link rel="stylesheet" href="../assets/css/adminDashboard.css">
<link rel="stylesheet" href="../assets/css/doctorDashboard.css">
<link rel="stylesheet" href="../assets/css/addPrescription.css">
<link rel="stylesheet" href="../assets/css/style.css">
<script src="../js/render.js" defer></script>
<script src="../js/util.js" defer></script>
<script src="../js/components/header.js" defer></script>
<script src="../js/components/footer.js" defer></script>
<script type="module" src = "../js/addPrescription.js"></script>
</head>
<body onload="renderContent()">
<div class="container">
<div class="wrapper">
<div id="header"></div>
<div class="main-content">
<div class="form-container">
<h2 id="heading">Add <span>Prescription</span></h2>
<form>
<label for="patientName">Patient Name</label>
<input type="text" id="patientName" disabled>
<label for="medicines">Medicine Names</label>
<input type="text" id="medicines" placeholder="e.g. Paracetamol, Vitamin C" required>
<label for="dosage">Dosage Instructions</label>
<textarea id="dosage" rows="3"
placeholder="e.g - Paracetamol"></textarea>
<label for="notes">Additional Notes</label>
<textarea id="notes" rows="3"
placeholder="Any precautions or observations (optional)"></textarea>
<button type="submit" class="btn-primary" id="savePrescription">Add Prescription</button>
<button type="button" onclick="selectRole('doctor')" class="btn-secondary">Cancel</button>
</form>
</div>
</div>
<div id="footer"></div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,53 @@
<!-- loggedPatientDashboard.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" type="image/svg+xml" href="../assets/images/logo/logo.png" />
<title>LoggedPatientDashboard</title>
<link rel = "stylesheet" href = "../assets/css/adminDashboard.css">
<link rel = "stylesheet" href = "../assets/css/style.css">
<link rel = "stylesheet" href = "../assets/css/patientDashboard.css">
<script src = "../js/render.js" defer></script>
<script src = "../js/util.js" defer></script>
<script src = "../js/components/header.js" defer></script>
<script src = "../js/components/footer.js" defer></script>
<script type = "module" src = "../js/loggedPatient.js" defer></script>
</head>
<body onload="renderContent()">
<div class="container">
<div class="wrapper">
<div id="header"></div>
<main class="main-content">
<input type="text" id="searchBar" class="searchBar" placeholder="Search Bar for custom output" />
<div class="filter-wrapper">
<select class="filter-select" id="filterTime">
<option value="">Sort by Time</option>
<option value="AM">AM</option>
<option value="PM">PM</option>
</select>
<select class="filter-select" id="filterSpecialty">
<option value="">Filter by Specialty</option>
<option value="Cardiologist">Cardiologist</option>
<option value="Dermatologist">Dermatologist</option>
<option value="Neurologist">Neurologist</option>
<option value="Pediatrician">Pediatrician</option>
<option value="Orthopedic">Orthopedic</option>
<option value="Gynecologist">Gynecologist</option>
<option value="Psychiatrist">Psychiatrist</option>
<option value="Dentist">Dentist</option>
<option value="Ophthalmologist">Ophthalmologist</option>
<option value="Ent">ENT Specialist</option>
<option value="Urologist">Urologist</option>
<option value="Oncologist">Oncologist</option>
<option value="Gastroenterologist">Gastroenterologist</option>
<option value="General">General Physician</option>
</select>
</div>
<div id="content"></div>
</main>
<div id="footer"></div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,52 @@
<!-- patientAppointment.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" type="image/svg+xml" href="../assets/images/logo/logo.png" />
<title>Patient Appointments</title>
<link rel="stylesheet" href="../assets/css/adminDashboard.css">
<link rel="stylesheet" href="../assets/css/doctorDashboard.css">
<link rel="stylesheet" href="../assets/css/style.css">
<script src="../js/render.js" defer></script>
<script src="../js/util.js" defer></script>
<script src="../js/components/header.js" defer></script>
<script src="../js/components/footer.js" defer></script>
<script type="module" src="../js/patientAppointment.js" defer></script>
</head>
<body onload="renderContent()">
<div class="container">
<div class="wrapper">
<div id="header"></div>
<div class="main-content">
<h2>Patient <span>Appointment</span></h2>
<input type="text" id="searchBar" class="searchBar" placeholder="Search Bar for custom output" />
<div class="filter-wrapper">
<select class="filter-select" id="appointmentFilter">
<option value="allAppointments">All Appointments</option>
<option value="future">Upcoming Appointments</option>
<option value="past">Past Appointments</option>
</select>
</div>
<table id="patientTable">
<thead class="table-header">
<tr>
<th>Patient Name</th>
<th>Doctor Name</th>
<th>Date</th>
<th>Time</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="patientTableBody">
<!-- rows will be inserted here dynamically -->
</tbody>
</table>
</div>
<div id="footer"></div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,63 @@
<!-- patientDashboard.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" type="image/svg+xml" href="../assets/images/logo/logo.png" />
<title>PatientDashboard</title>
<link rel="stylesheet" href="../assets/css/adminDashboard.css">
<link rel="stylesheet" href="../assets/css/style.css">
<link rel="stylesheet" href="../assets/css/patientDashboard.css">
<script src="../js/render.js" defer></script>
<script src="../js/util.js" defer></script>
<script src="../js/components/header.js" defer></script>
<script src="../js/components/footer.js" defer></script>
<script type="module" src="../js/components/modals.js"></script>
</head>
<body onload="renderContent()">
<div class="container">
<div class="wrapper">
<div id="header"></div>
<main class="main-content">
<input type="text" id="searchBar" class="searchBar" placeholder="Search Bar for custom output" />
<div class="filter-wrapper">
<select class="filter-select" id="filterTime">
<option value="">Sort by Time</option>
<option value="AM">AM</option>
<option value="PM">PM</option>
</select>
<select class="filter-select" id="filterSpecialty">
<option value="">Filter by Specialty</option>
<option value="Cardiologist">Cardiologist</option>
<option value="Dermatologist">Dermatologist</option>
<option value="Neurologist">Neurologist</option>
<option value="Pediatrician">Pediatrician</option>
<option value="Orthopedic">Orthopedic</option>
<option value="Gynecologist">Gynecologist</option>
<option value="Psychiatrist">Psychiatrist</option>
<option value="Dentist">Dentist</option>
<option value="Ophthalmologist">Ophthalmologist</option>
<option value="Ent">ENT Specialist</option>
<option value="Urologist">Urologist</option>
<option value="Oncologist">Oncologist</option>
<option value="Gastroenterologist">Gastroenterologist</option>
<option value="General">General Physician</option>
</select>
</div>
<div id="content"></div>
</main>
<div id="footer"></div>
</div>
</div>
<div id="modal" class="modal">
<div class="modal-content">
<span class="close" id="closeModal">&times;</span>
<div id="modal-body"></div>
</div>
</div>
<script type="module" src="../js/patientDashboard.js" defer></script>
</body>
</html>

View File

@@ -0,0 +1,42 @@
<!-- patientRecord.html -->
<!-- for doctor to see patient details -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" type="image/svg+xml" href="../assets/images/logo/logo.png" />
<title>DoctorDashboard</title>
<link rel = "stylesheet" href = "../assets/css/adminDashboard.css">
<link rel = "stylesheet" href = "../assets/css/doctorDashboard.css">
<link rel = "stylesheet" href = "../assets/css/style.css">
<script src = "../js/render.js" defer></script>
<script src = "../js/util.js" defer></script>
<script src = "../js/components/header.js" defer></script>
<script src = "../js/components/footer.js" defer></script>
<script type = "module" src = "../js/components/patientRecordRow.js" defer></script>
<script type = "module" src = "../js/patientRecordServices.js" defer></script>
</head>
<body onload="renderContent()">
<div class="container">
<div class="wrapper">
<div id="header"></div>
<div class="main-content">
<h2>Patient <span>Record</span></h2>
<table id="patientTable">
<thead class="table-header">
<tr>
<th>Date</th>
<th>Appointment ID</th>
<th>Patient ID</th>
<th>Prescription</th>
</tr>
</thead>
<tbody id="patientTableBody"></tbody>
</table>
</div>
<div id="footer"></div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,54 @@
<!-- updateAppointment.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" type="image/svg+xml" href="../assets/images/logo/logo.png" />
<title>DoctorDashboard</title>
<link rel="stylesheet" href="../assets/css/adminDashboard.css">
<link rel="stylesheet" href="../assets/css/doctorDashboard.css">
<link rel="stylesheet" href="../assets/css/updateAppointment.css">
<link rel="stylesheet" href="../assets/css/style.css">
<script src="../js/render.js" defer></script>
<script src="../js/util.js" defer></script>
<script src="../js/components/header.js" defer></script>
<script src="../js/components/footer.js" defer></script>
<script type="module" src="../js/updateAppointment.js" defer></script>
</head>
<body onload="renderContent()">
<div class="container">
<div class="wrapper">
<div id="header"></div>
<div class="main-content">
<div class="form-container">
<h2>Update <span>Appointment</span></h2>
<form id="updateAppointmentForm">
<label for="patientName">Patient Name</label>
<input type="text" id="patientName" disabled>
<label for="doctorName">Doctor Name</label>
<input type="text" id="doctorName" disabled>
<label for="appointmentDate">Date</label>
<input type="date" id="appointmentDate" required>
<label for="appointmentTime">Time</label>
<select id="appointmentTime" required>
</select>
<button type="submit" class="btn-primary" id="updateAppointment">Update Appointment</button>
<button type="button" onclick="window.location.href='/pages/patientAppointments.html'"
class="btn-secondary">Cancel</button>
</form>
</div>
</div>
<div id="footer"></div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,109 @@
<!-- adminDashboard.html -->
<!--
Step-by-Step Explanation of `adminDashboard.html`
This file creates the Admin Dashboard interface for managing doctor-related data
with search, filter, and modal functionality.
1. Declare the Document Type
* Use `<!DOCTYPE html>` to ensure HTML5 standards for consistent rendering across browsers.
2. Begin the HTML Document with Thymeleaf Namespace
* Add the `<html>` tag with `xmlns:th="http://www.thymeleaf.org"` to allow Thymeleaf-specific attributes in Spring-based apps.
3. Head Section Metadata and Resources
* Open the `<head>` section for meta info and links to assets and scripts.
4. Set Character Encoding
* Use UTF-8 to support all characters and prevent encoding issues.
5. Set the Favicon
* Add a favicon for browser tabs using Thymeleaf pathing:
`<link rel="icon" type="image/png" th:href="@{/assets/images/logo/logo.png}" />`.
6. Set the Page Title
* Give the page a title, e.g., `"Admin Dashboard"`, which will show in the browser tab.
7. Link CSS Stylesheets
* Include dashboard-specific styles and global site-wide styles:
- `adminDashboard.css` for layout and design of this page.
- `style.css` for shared UI components.
8. Link JavaScript Files (with `defer`)
* Use Thymeleaf's `th:src` to include JS components and utilities:
- `render.js` (rendering content)
- `util.js` (utility functions)
- `header.js` and `footer.js` (for dynamic header/footer)
* Use `defer` so scripts execute after the HTML is fully parsed.
9. Close the Head and Begin the Body
* Start the visible part of the webpage. `onload="renderContent()"` is used to call a function when the page is ready.
10. Create the Container
* Add a `div` with class `container` for layout control.
11. Add a Wrapper Inside the Container
* Wrap page elements with a `wrapper` div to maintain consistent spacing and organization.
12. Insert a Header Placeholder
* Add `<div id="header"></div>` for injecting the header via JavaScript.
13. Define the Main Section
* Use `<main class="main-content">` for the central dashboard area.
14. Add a Search Bar
* Add an input field with `id="searchBar"` for admin to search content dynamically.
15. Add Filter Dropdowns
* Use two `<select>` elements:
- One for time (AM/PM).
- One for filtering by medical specialty (Cardiologist, Dentist, etc.).
16. Add Content Display Area
* Insert a `<div id="content"></div>` for dynamic injection of doctor cards or data.
17. Add a Footer Placeholder
* Use `<div id="footer"></div>` for a JavaScript-rendered footer.
18. Add Modal Structure
* Include a modal with:
- A close button (`&times;`)
- A content area (`#modal-body`) for displaying popup information or forms.
19. Include Page-Specific JavaScript Modules
* Load JavaScript specific to the admin dashboard:
- `adminDashboard.js` for dashboard logic.
- `doctorCard.js` for rendering doctor information cards.
* Use `type="module"` and `defer` for modern and non-blocking loading.
HINT:
`<script type="module" src="../../static/js/adminDashboard.js" defer></script>`
`<script type="module" src="../../static/js/doctorDashboard.js" defer></script>`
20. Close the Body and HTML Tags
* Finish the structure by properly closing all tags.
-->

View File

@@ -0,0 +1,101 @@
<!-- doctorDashboard.html -->
<!--
Step-by-Step Explanation of `doctorDashboard.html`
This file defines the Doctor Dashboard interface, allowing doctors to view and manage their patient appointments, search records, and access patient prescriptions.
1. Label the File
* Add a comment at the top to identify the file as `doctorDashboard.html`.
2. Declare the Document Type
* Use `<!DOCTYPE html>` to define the document as an HTML5 file for consistent behavior across modern browsers.
3. Begin the HTML Document with Thymeleaf Namespace
* The `<html>` tag includes `xmlns:th="http://www.thymeleaf.org"` to enable Thymeleaf-specific attributes used in Spring Boot applications.
4. Head Section Metadata and Assets
* Start the `<head>` section to include essential meta information, styles, and scripts.
5. Set Character Encoding
* Use `<meta charset="UTF-8">` to support all text characters and prevent encoding issues.
6. Set the Favicon
* Add a favicon using Thymeleaf pathing:
`<link rel="icon" type="image/png" th:href="@{/assets/images/logo/logo.png}" />`
7. Set the Page Title
* Title the page `"Doctor Dashboard"` to show it in the browser tab.
8. Link CSS Stylesheets
* Include necessary CSS files:
- `adminDashboard.css` (for shared layout styles with admin interface)
- `doctorDashboard.css` (for doctor-specific styles)
- `style.css` (for general UI components)
9. Link JavaScript Files (with `defer`)
* Load scripts using Thymeleaf's `th:src` and `defer` attribute to prevent blocking rendering:
- `render.js` Handles common rendering logic.
- `util.js` Provides utility functions.
- `header.js` and `footer.js` Dynamically inject header/footer.
- `doctorDashboard.js` Contains dashboard logic for doctor functionality, loaded as a module.
10. Close the Head and Begin the Body
* Begin the visible part of the webpage using `<body onload="renderContent()">`, which calls a JavaScript function after loading.
11. Create the Main Container
* Use `<div class="container">` to wrap all content and structure the page.
12. Add a Wrapper for Page Content
* Nest elements inside a `wrapper` div for structured layout control.
13. Insert a Header Placeholder
* Add `<div id="header"></div>` where the dynamic header will be injected.
14. Define the Main Content Area
* Use `<div class="main-content">` for the central section of the dashboard.
15. Add a Search Bar
* Include an input field with `id="searchBar"` for doctors to search patients or data:
```html
<input type="text" id="searchBar" class="searchBar" placeholder="Search Bar for custom output" />
```
16. Add Filter Controls
* Include a filter section:
- A button (`#todayButton`) to view today's appointments.
- A date picker (`#datePicker`) to select specific dates.
17. Add a Patient Table
* Display a table to list patient data:
- Headers: Patient ID, Name, Phone No., Email, and Prescription.
- Body: `<tbody id="patientTableBody"></tbody>` for dynamic content insertion.
18. Insert a Footer Placeholder
* Use `<div id="footer"></div>` to load a common footer via JavaScript.
19. Close the Wrapper and Container
* Close `wrapper` and `container` divs to maintain structural integrity.
20. End the Body and HTML
* Properly close the `<body>` and `<html>` tags to complete the document.
-->

View File

@@ -0,0 +1,13 @@
package com.project.back_end;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class BackEndApplicationTests {
@Test
void contextLoads() {
}
}

View File

@@ -4,4 +4,5 @@ include 'Portfolio'
include 'OnlineQuiz'
include 'RetailManagementSystem:back-end'
include 'RetailManagementSystem:front-end'
include 'InventoryManagementSystem'
include 'InventoryManagementSystem'
include 'SmartClinicManagementSystem:app'