Add SmartClinicManagementSystem
This commit is contained in:
40
SmartClinicManagementSystem/app/build.gradle
Normal file
40
SmartClinicManagementSystem/app/build.gradle
Normal 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()
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
}
|
||||
@@ -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 doctor’s 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.
|
||||
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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 doctor’s 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 doctor’s 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.
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
|
||||
}
|
||||
@@ -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> { ... }
|
||||
|
||||
}
|
||||
@@ -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 doctor’s 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 doctor’s 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 doctor’s name (using a LIKE query) and the patient’s ID.
|
||||
// - Return type: List<Appointment>
|
||||
// - Parameters: String doctorName, Long patientId
|
||||
|
||||
// - **filterByDoctorNameAndPatientIdAndStatus**:
|
||||
// - This method retrieves appointments based on a doctor’s name (using a LIKE query), patient’s 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.
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
|
||||
}
|
||||
@@ -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 doctor’s 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.
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
|
||||
}
|
||||
@@ -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 admin’s 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 doesn’t 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.
|
||||
|
||||
|
||||
}
|
||||
@@ -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 hasn’t 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.
|
||||
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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); }
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
|
||||
*/
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 |
@@ -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 don’t 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 site’s 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 site’s 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 it’s deferred so it doesn’t block HTML rendering.
|
||||
|
||||
20. Close the Body and HTML Tags
|
||||
|
||||
* Properly close all opened tags to complete the document structure.
|
||||
|
||||
-->
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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");
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 doctor’s 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
|
||||
*/
|
||||
@@ -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
|
||||
|
||||
*/
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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";
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
}
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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."
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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: [] };
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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.");
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// util.js
|
||||
function setRole(role) {
|
||||
localStorage.setItem("userRole", role);
|
||||
}
|
||||
|
||||
function getRole() {
|
||||
return localStorage.getItem("userRole");
|
||||
}
|
||||
|
||||
function clearRole() {
|
||||
localStorage.removeItem("userRole");
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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">×</span>
|
||||
<div id="modal-body"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="../js/patientDashboard.js" defer></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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 (`×`)
|
||||
- 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.
|
||||
|
||||
-->
|
||||
|
||||
@@ -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.
|
||||
-->
|
||||
@@ -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() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,3 +5,4 @@ include 'OnlineQuiz'
|
||||
include 'RetailManagementSystem:back-end'
|
||||
include 'RetailManagementSystem:front-end'
|
||||
include 'InventoryManagementSystem'
|
||||
include 'SmartClinicManagementSystem:app'
|
||||
Reference in New Issue
Block a user