Help me with the following:
Every minute, get all leaverequests that are requested on today's date, of type "Medical" and has medicalcertificateurl as null or empty. This will send an email to the supervisor, which is supervisorid in the request, and is  the same documentid in the users collection.
Make the code as simple as possible, include print statements for debugging. Give me entire code.

Code context:
File: backend\pom.xml
--------------------------------------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.g4t4</groupId>
    <artifactId>home-cleaning-scheduler</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>Home Cleaning Scheduler</name>
    <description>A Spring Boot application for scheduling and managing home cleaning tasks</description>

    <properties>
        <java.version>21</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.google.firebase</groupId>
            <artifactId>firebase-admin</artifactId>
            <version>9.3.0</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.17.2</version>
        </dependency>
		<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.34</version> <!-- Or the latest version -->
    <scope>provided</scope>
</dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- Spring Boot Maven Plugin -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>

            <!-- Maven Compiler Plugin Configuration -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.10.1</version> <!-- or the latest stable version -->
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>


File: backend\src\main\java\com\g4t4\home_cleaning_scheduler\controller\LeaveRequestController.java
--------------------------------------------------------------------------------
// LeaveRequestController.java
package com.g4t4.home_cleaning_scheduler.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.g4t4.home_cleaning_scheduler.model.LeaveRequest;
import com.g4t4.home_cleaning_scheduler.service.LeaveRequestService;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.ExecutionException;

@RestController
@RequestMapping("/api/leave-requests")
public class LeaveRequestController {

    @Autowired
    private LeaveRequestService leaveRequestService;

    @PostMapping(consumes = "multipart/form-data")
    public ResponseEntity<String> submitLeaveRequest(@RequestParam("leaveRequest") String leaveRequestJson,
                                                     @RequestParam(value = "medicalCertificate", required = false) MultipartFile medicalCertificate) {
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            LeaveRequest leaveRequest = objectMapper.readValue(leaveRequestJson, LeaveRequest.class);
            leaveRequestService.submitLeaveRequest(leaveRequest, medicalCertificate);
            return ResponseEntity.status(HttpStatus.CREATED).body("Leave request submitted successfully.");
        } catch (IllegalArgumentException e) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
        } catch (IOException e) {
            e.printStackTrace(); // Add this line to log the stack trace
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error processing leave request.");
        }
    }

    @PutMapping("/{leaveRequestId}/medical-certificate")
    public ResponseEntity<String> updateMedicalCertificate(
            @PathVariable String leaveRequestId,
            @RequestParam(value = "medicalCertificate") MultipartFile medicalCertificate) {
        try {            
            leaveRequestService.updateMedicalCertificate(leaveRequestId, medicalCertificate);
            return ResponseEntity.ok("Medical certificate updated successfully.");
        } catch (IllegalArgumentException e) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
        } catch (IOException e) {
            e.printStackTrace(); 
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error processing medical certificate update.");
        } catch (Exception e) {
            e.printStackTrace(); 
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Unexpected error occurred: " + e.getMessage());
        }
    }

    @PutMapping("/{leaveRequestId}/approve")
    public ResponseEntity<String> approveLeaveRequest(@PathVariable String leaveRequestId) {
        try {
            leaveRequestService.updateLeaveRequestStatus(leaveRequestId, "Approved");
            return ResponseEntity.ok("Leave request approved.");
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error approving leave request.");
        }
    }

    @PutMapping("/{leaveRequestId}/reject")
    public ResponseEntity<String> rejectLeaveRequest(@PathVariable String leaveRequestId) {
        try {
            leaveRequestService.updateLeaveRequestStatus(leaveRequestId, "Rejected");
            return ResponseEntity.ok("Leave request rejected.");
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error rejecting leave request.");
        }
    }

    @GetMapping("/supervisor/{supervisorId}")
    public ResponseEntity<List<LeaveRequest>> getLeaveRequestsBySupervisor(@PathVariable String supervisorId) {
        try {
            List<LeaveRequest> leaveRequests = leaveRequestService.getLeaveRequestsBySupervisor(supervisorId);
            return ResponseEntity.ok(leaveRequests);
        } catch (ExecutionException | InterruptedException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }

    @GetMapping("/worker/{workerId}")
    public ResponseEntity<List<LeaveRequest>> getLeaveRequestsByWorker(@PathVariable String workerId) {
        try {
            List<LeaveRequest> leaveRequests = leaveRequestService.getLeaveRequestsByWorker(workerId);
            return ResponseEntity.ok(leaveRequests);
        } catch (ExecutionException | InterruptedException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }

    @GetMapping("/approved")
    public ResponseEntity<List<LeaveRequest>> getApprovedLeaveRequests() {
        try {
            List<LeaveRequest> leaveRequests = leaveRequestService.getApprovedLeaveRequests();
            return ResponseEntity.ok(leaveRequests);
        } catch (ExecutionException | InterruptedException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }

}


File: backend\src\main\java\com\g4t4\home_cleaning_scheduler\controller\UserController.java
--------------------------------------------------------------------------------
package com.g4t4.home_cleaning_scheduler.controller;



import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import com.g4t4.home_cleaning_scheduler.model.User;
import com.g4t4.home_cleaning_scheduler.model.Worker;
import com.g4t4.home_cleaning_scheduler.service.UserService;

import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;

@RestController
@RequestMapping("/api/users")
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping
    public ResponseEntity<User> createUser(@RequestBody Map<String, Object> userMap) throws ExecutionException, InterruptedException, NoSuchAlgorithmException {
        User.Type userType = User.Type.valueOf((String) userMap.get("userType"));
        User createdUser = userService.createUser(
        userType,
        (String) userMap.get("name"),
        (String) userMap.get("email"),
        (String) userMap.get("password"),
        (Integer) userMap.get("phoneNumber"),
        (String) userMap.get("username"),
        userMap
    );
    
        return new ResponseEntity<>(createdUser, HttpStatus.CREATED);
    }

    @PostMapping("/login")
    public ResponseEntity<User> login(@RequestBody User userBody) throws ExecutionException, InterruptedException {
        String username = userBody.getUsername();
        String password = userBody.getPassword();
        User user = userService.getUserByUsernameAndPassword(username, password);
        return user != null ? ResponseEntity.ok(user) : ResponseEntity.notFound().build();
    }

    @GetMapping("/workers")
    public ResponseEntity<List<Worker>> getAllWorkers() throws ExecutionException, InterruptedException {
        List<Worker> workers = userService.getAllWorkers();
        return ResponseEntity.ok(workers);
    }

    @PutMapping
    public ResponseEntity<User> updateUser(@RequestBody Map<String, Object> userMap) throws ExecutionException, InterruptedException, NoSuchAlgorithmException {
        User.Type userType = User.Type.valueOf((String) userMap.get("userType"));
        User updatedUser = userService.updateUser(
        userType,
        (String) userMap.get("userId"),
        (String) userMap.get("name"),
        (String) userMap.get("email"),
        (String) userMap.get("password"),
        (Integer) userMap.get("phoneNumber"),
        (String) userMap.get("username"),
        userMap
    );
    
        return new ResponseEntity<>(updatedUser, HttpStatus.CREATED);
    }

    @DeleteMapping("/{userId}")
    public ResponseEntity<String> deleteUser(@PathVariable String userId) throws ExecutionException, InterruptedException {
        String user = userService.deleteUser(userId);
        return ResponseEntity.ok(user);
    }


}


File: backend\src\main\java\com\g4t4\home_cleaning_scheduler\HomeCleaningSchedulerApplication.java
--------------------------------------------------------------------------------
package com.g4t4.home_cleaning_scheduler;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class HomeCleaningSchedulerApplication {

	public static void main(String[] args) {
		SpringApplication.run(HomeCleaningSchedulerApplication.class, args);
	}

}


File: backend\src\main\java\com\g4t4\home_cleaning_scheduler\model\Admin.java
--------------------------------------------------------------------------------
package com.g4t4.home_cleaning_scheduler.model;

import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;

public class Admin extends User {
    private String employeeId;
    private List<String> listofWorkers = new ArrayList<>();
    private Boolean isRoot = false;

    public Admin(){
        
    }

    public Admin(String name, String email, String password, int phoneNumber, Type userType, String username, String employeeId, List<String> listofWorkers, Boolean isRoot) throws NoSuchAlgorithmException {
        super(name, email, password, phoneNumber, userType, username);
        this.employeeId = employeeId;
        this.listofWorkers = listofWorkers;
        this.isRoot = isRoot;
    }

    public String getEmployeeId() {
        return employeeId;
    }

    public void setEmployeeId(String employeeId) {
        this.employeeId = employeeId;
    }

    public List<String> getListofWorkers() {
        return listofWorkers;
    }

    public void setListofWorkers(List<String> listofWorkers) {
        this.listofWorkers = listofWorkers;
    }

    public Boolean getIsRoot() {
        return isRoot;
    }

    public void setIsRoot(Boolean isRoot) {
        this.isRoot = isRoot;
    }
}




File: backend\src\main\java\com\g4t4\home_cleaning_scheduler\model\FlexibleTimestampDeserializer.java
--------------------------------------------------------------------------------
package com.g4t4.home_cleaning_scheduler.model;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.cloud.Timestamp;

import java.io.IOException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;

public class FlexibleTimestampDeserializer extends JsonDeserializer<Timestamp> {
    @Override
    public Timestamp deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        JsonNode node = p.getCodec().readTree(p);
        if (node.isTextual()) {
            String text = node.asText();
            try {
                // Handle ISO-8601 format
                TemporalAccessor ta = DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(text);
                Instant instant = Instant.from(ta);
                return Timestamp.ofTimeSecondsAndNanos(instant.getEpochSecond(), instant.getNano());
            } catch (Exception e) {
                // Handle yyyy-MM-dd format
                LocalDate localDate = LocalDate.parse(text, DateTimeFormatter.ISO_LOCAL_DATE);
                Instant instant = localDate.atStartOfDay().toInstant(ZoneOffset.UTC);
                return Timestamp.ofTimeSecondsAndNanos(instant.getEpochSecond(), 0);
            }
        } else if (node.isObject() && node.has("seconds") && node.has("nanos")) {
            // Handle Firestore Timestamp format
            long seconds = node.get("seconds").asLong();
            int nanos = node.get("nanos").asInt();
            return Timestamp.ofTimeSecondsAndNanos(seconds, nanos);
        }
        throw new IOException("Unable to deserialize Timestamp");
    }
}


File: backend\src\main\java\com\g4t4\home_cleaning_scheduler\model\LeaveRequest.java
--------------------------------------------------------------------------------
// LeaveRequest.java
package com.g4t4.home_cleaning_scheduler.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.google.cloud.Timestamp;
import com.google.cloud.firestore.annotation.DocumentId;
import com.google.cloud.firestore.annotation.IgnoreExtraProperties;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@IgnoreExtraProperties
@JsonIgnoreProperties(ignoreUnknown = true)
public class LeaveRequest {
    @DocumentId
    private String leaveRequestId;
    private String workerId;
    private String workerName; // New field for worker's name
    private String reason; // New field for reason

    @JsonDeserialize(using = FlexibleTimestampDeserializer.class)
    private Timestamp dateOfApplication;

    private String status; // e.g., Pending, Approved, Rejected
    private String leaveType; // e.g., Medical, Annual, Emergency, etc.
    private String supervisorId;

    @JsonDeserialize(using = FlexibleTimestampDeserializer.class)
    private Timestamp leaveStartDate;

    @JsonDeserialize(using = FlexibleTimestampDeserializer.class)
    private Timestamp leaveEndDate;

    private String medicalCertificateUrl; // URL to the stored medical certificate image
}


File: backend\src\main\java\com\g4t4\home_cleaning_scheduler\model\User.java
--------------------------------------------------------------------------------
package com.g4t4.home_cleaning_scheduler.model;
import java.security.NoSuchAlgorithmException;

import com.google.cloud.firestore.annotation.DocumentId;


public class User {
    @DocumentId
    private String userId;
    private String username;
    private String name;
    private String email;
    private int phoneNumber;
    private String password;
    private Type userType;

    public enum Type {
        WORKER, CLIENT, ADMIN
    }
    public User() {
        
    }
    public User(String name, String email, String password, int phoneNumber, Type userType, String username)
            throws NoSuchAlgorithmException {

        this.name = name;
        this.email = email;
        this.phoneNumber = phoneNumber;
        this.username = username;
        this.password = password;
        this.userType = userType;
    }

    public User(String username, String password)
            throws NoSuchAlgorithmException {

        this.username = username;
        this.password = password;
    }
    public String getUserId() {
        return userId;
    }
    public void setUserId(String userId) {
        this.userId = userId;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public int getPhoneNumber() {
        return phoneNumber;
    }
    public void setPhoneNumber(int phoneNumber) {
        this.phoneNumber = phoneNumber;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public Type getUserType() {
        return userType;
    }
    public void setUserType(Type userType) {
        this.userType = userType;
    }



}


File: backend\src\main\java\com\g4t4\home_cleaning_scheduler\model\Worker.java
--------------------------------------------------------------------------------
package com.g4t4.home_cleaning_scheduler.model;

import java.security.NoSuchAlgorithmException;

public class Worker extends User {
    private String employeeId;
    private int hoursPerWeek;
    private String bio;
    private String supervisorId;

    public Worker() {
        
    }

    public Worker(String name, String email, String password, int phoneNumber, Type userType, String username, String employeeId, int hoursPerWeek, String bio, String supervisorId) throws NoSuchAlgorithmException {
        super(name, email, password, phoneNumber, userType, username);
        this.employeeId = employeeId;
        this.hoursPerWeek = hoursPerWeek;
        this.bio = bio;
        this.supervisorId = supervisorId;
    }

    public String getEmployeeId() {
        return employeeId;
    }

    public void setEmployeeId(String employeeId) {
        this.employeeId = employeeId;
    }

    public int getHoursPerWeek() {
        return hoursPerWeek;
    }

    public void setHoursPerWeek(int hoursPerWeek) {
        this.hoursPerWeek = hoursPerWeek;
    }

    public String getBio() {
        return bio;
    }

    public void setBio(String bio) {
        this.bio = bio;
    }

    public String getSupervisorId() {
        return supervisorId;
    }

    public void setSupervisorId(String supervisorId) {
        this.supervisorId = supervisorId;
    }

    
    




}


File: backend\src\main\java\com\g4t4\home_cleaning_scheduler\repository\AbstractFirestoreRepository.java
--------------------------------------------------------------------------------
package com.g4t4.home_cleaning_scheduler.repository;

import java.lang.reflect.ParameterizedType;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;


import org.springframework.lang.Nullable;

import com.g4t4.home_cleaning_scheduler.utils.DocumentId;
import com.google.api.core.ApiFuture;
import com.google.cloud.firestore.CollectionReference;
import com.google.cloud.firestore.DocumentReference;
import com.google.cloud.firestore.DocumentSnapshot;
import com.google.cloud.firestore.Firestore;
import com.google.cloud.firestore.QueryDocumentSnapshot;
import com.google.cloud.firestore.QuerySnapshot;
import com.google.cloud.firestore.WriteResult;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public abstract class AbstractFirestoreRepository<T> {

    private final CollectionReference collectionReference;
    private final String collectionName;
    private final Class<T> parameterizedType;

    protected final Firestore firestore;

    // Constructor
    protected AbstractFirestoreRepository(Firestore firestore, String collectionName) {
        if (firestore == null) {
            throw new IllegalArgumentException("Firestore cannot be null");
        }
        this.firestore = firestore;
        this.collectionName = collectionName;
        this.collectionReference = firestore.collection(collectionName);

        // Initialize parameterizedType by calling getParameterizedType()
        this.parameterizedType = getParameterizedType();
    }

    // Method to get the generic type parameter (T)
    private Class<T> getParameterizedType() {
        ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();
        return (Class<T>) type.getActualTypeArguments()[0];
    }

    public void updateField(String documentId, String fieldName, Object value) throws ExecutionException, InterruptedException {
        DocumentReference docRef = collectionReference.document(documentId);
        ApiFuture<WriteResult> writeResult = docRef.update(fieldName, value);
        writeResult.get(); 
    }
    

    public boolean save(T model) {
        String documentId = getDocumentId(model);
        ApiFuture<WriteResult> resultApiFuture = collectionReference.document(documentId).set(model);

        try {
            log.info("{}-{} saved at {}", collectionName, documentId, resultApiFuture.get().getUpdateTime());
            return true;
        } catch (InterruptedException | ExecutionException e) {
            log.error("Error saving {}={} {}", collectionName, documentId, e.getMessage());
        }

        return false;
    }

    public void delete(T model) {
        String documentId = getDocumentId(model);
        ApiFuture<WriteResult> resultApiFuture = collectionReference.document(documentId).delete();
    }

    public List<T> retrieveAll() {
        ApiFuture<QuerySnapshot> querySnapshotApiFuture = collectionReference.get();

        try {
            List<QueryDocumentSnapshot> queryDocumentSnapshots = querySnapshotApiFuture.get().getDocuments();

            return queryDocumentSnapshots.stream()
                    .map(queryDocumentSnapshot -> queryDocumentSnapshot.toObject(parameterizedType))
                    .collect(Collectors.toList());

        } catch (InterruptedException | ExecutionException e) {
            log.error("Exception occurred while retrieving all documents for {}", collectionName);
        }
        return Collections.<T>emptyList();
    }

    public Optional<T> get(String documentId) {
        DocumentReference documentReference = collectionReference.document(documentId);
        ApiFuture<DocumentSnapshot> documentSnapshotApiFuture = documentReference.get();

        try {
            DocumentSnapshot documentSnapshot = documentSnapshotApiFuture.get();

            if (documentSnapshot.exists()) {
                return Optional.ofNullable(documentSnapshot.toObject(parameterizedType));
            }

        } catch (InterruptedException | ExecutionException e) {
            log.error("Exception occurred retrieving: {} {}, {}", collectionName, documentId, e.getMessage());
        }

        return Optional.empty();
    }

    protected String getDocumentId(T t) {
        Object key;
        Class<?> clzz = t.getClass();
        do {
            key = getKeyFromFields(clzz, t);
            clzz = clzz.getSuperclass();
        } while (key == null && clzz != null);

        if (key == null) {
            return UUID.randomUUID().toString();
        }
        return String.valueOf(key);
    }

    private Object getKeyFromFields(Class<?> clazz, Object t) {
        return Arrays.stream(clazz.getDeclaredFields())
                .filter(field -> field.isAnnotationPresent(DocumentId.class))
                .findFirst()
                .map(field -> getValue(t, field))
                .orElse(null);
    }

    @Nullable
    private Object getValue(Object t, java.lang.reflect.Field field) {
        field.setAccessible(true);
        try {
            return field.get(t);
        } catch (IllegalAccessException e) {
            log.error("Error in getting documentId key", e);
        }
        return null;
    }

    protected CollectionReference getCollectionReference() {
        return this.collectionReference;
    }

    protected Class<T> getType() {
        return this.parameterizedType;
    }
}



File: backend\src\main\java\com\g4t4\home_cleaning_scheduler\repository\LeaveRequestRepository.java
--------------------------------------------------------------------------------
package com.g4t4.home_cleaning_scheduler.repository;

import com.g4t4.home_cleaning_scheduler.model.LeaveRequest;
import com.google.cloud.firestore.CollectionReference;
import com.google.cloud.firestore.DocumentSnapshot;
import com.google.cloud.firestore.Firestore;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.util.concurrent.ExecutionException;

@Repository
public class LeaveRequestRepository extends AbstractFirestoreRepository<LeaveRequest> {

    @Autowired
    public LeaveRequestRepository(Firestore firestore) {
        super(firestore, "leave_requests");
    }

    public boolean saveLeaveRequest(LeaveRequest leaveRequest) {
        return save(leaveRequest);
    }

    public void deleteLeaveRequest(LeaveRequest leaveRequest) {
        delete(leaveRequest);
    }

    public CollectionReference getCollectionReference() {
        return super.getCollectionReference();
    }

    public LeaveRequest findById(String leaveRequestId) throws ExecutionException, InterruptedException {
        System.out.println("Searching for LeaveRequest with ID: " + leaveRequestId);
        DocumentSnapshot document = getCollectionReference().document(leaveRequestId).get().get();
        if (document.exists()) {
            return document.toObject(LeaveRequest.class);
        } else {
            return null; 
        }
    }

    // Additional methods to get leave requests by workerId, supervisorId, etc., can be added if needed.
}


File: backend\src\main\java\com\g4t4\home_cleaning_scheduler\repository\UserRepository.java
--------------------------------------------------------------------------------
package com.g4t4.home_cleaning_scheduler.repository;

import com.g4t4.home_cleaning_scheduler.model.Admin;
import com.g4t4.home_cleaning_scheduler.model.Client;
import com.g4t4.home_cleaning_scheduler.model.User;
import com.g4t4.home_cleaning_scheduler.model.Worker;
import com.google.cloud.firestore.Firestore;
import com.google.firebase.cloud.FirestoreClient;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.concurrent.ExecutionException;

@Repository
public class UserRepository {

    private static final String COLLECTION_NAME = "users";
    @Autowired
    private Firestore firestore;
    public enum AccountType {
        WORKER, client, administrator
    }

    public User save(User user) throws ExecutionException, InterruptedException {
        
        String id;
        if (user instanceof Admin) {
            Admin admin = (Admin) user;
            id = firestore.collection(COLLECTION_NAME).add(admin).get().getId();
        } else if (user instanceof Client) {
            Client client = (Client) user;
            id = firestore.collection(COLLECTION_NAME).add(client).get().getId();
        } else if (user instanceof Worker) {
            Worker worker = (Worker) user;
            id = firestore.collection(COLLECTION_NAME).add(worker).get().getId();
        } else {
            id = firestore.collection(COLLECTION_NAME).add(user).get().getId();
        }
        user.setUserId(id);
        return user;
    }

    public String findTypeByUsernameAndPassword(String username, String password)
            throws ExecutionException, InterruptedException {
        Firestore firestore = FirestoreClient.getFirestore();

        // Create a compound query to find a user with matching username and password
        List<User> users = firestore.collection(COLLECTION_NAME)
                .whereEqualTo("username", username)
                .whereEqualTo("password", password)
                .get()
                .get()
                .toObjects(User.class);

        // Check if a user was found
        if (!users.isEmpty()) {
            User user = users.get(0);
            return user.getUserType().toString();
        }

        // Return an empty string if no matching user was found
        return "";
    }

    public Admin findAdminByUsername(String username) throws ExecutionException, InterruptedException {
        Firestore firestore = FirestoreClient.getFirestore();

        // Query for admin with matching username
        List<Admin> admins = firestore.collection(COLLECTION_NAME)
                .whereEqualTo("username", username)
                .limit(1) // Limit to 1 result since username should be unique
                .get()
                .get()
                .toObjects(Admin.class);

        // Return the admin if found, otherwise return null
        return admins.isEmpty() ? null : admins.get(0);
    }

    public Client findClientByUsername(String username) throws ExecutionException, InterruptedException {
        Firestore firestore = FirestoreClient.getFirestore();

        // Query for admin with matching username
        List<Client> clients = firestore.collection(COLLECTION_NAME)
                .whereEqualTo("username", username)
                .limit(1) // Limit to 1 result since username should be unique
                .get()
                .get()
                .toObjects(Client.class);

        // Return the admin if found, otherwise return null
        return clients.isEmpty() ? null : clients.get(0);
    }

    public Worker findWorkerByUsername(String username) throws ExecutionException, InterruptedException {
        Firestore firestore = FirestoreClient.getFirestore();

        // Query for admin with matching username
        List<Worker> workers = firestore.collection(COLLECTION_NAME)
                .whereEqualTo("username", username)
                .limit(1) // Limit to 1 result since username should be unique
                .get()
                .get()
                .toObjects(Worker.class);

        // Return the admin if found, otherwise return null
        return workers.isEmpty() ? null : workers.get(0);
    }

    public List<Worker> findAllWorkers() throws ExecutionException, InterruptedException {
        Firestore firestore = FirestoreClient.getFirestore();

        // Query for all users with account type WORKER
        List<Worker> workers = firestore.collection(COLLECTION_NAME)
                .whereEqualTo("userType", AccountType.WORKER)
                .get()
                .get()
                .toObjects(Worker.class);

        return workers;
    }

    public User update(User user, String userId) throws ExecutionException, InterruptedException {
        Firestore firestore = FirestoreClient.getFirestore();

        if (user instanceof Admin) {
            Admin admin = (Admin) user;
            firestore.collection(COLLECTION_NAME).document(userId).set(admin).get();
        } else if (user instanceof Client) {
            Client client = (Client) user;
            firestore.collection(COLLECTION_NAME).document(userId).set(client).get();
        } else if (user instanceof Worker) {
            Worker worker = (Worker) user;
            firestore.collection(COLLECTION_NAME).document(userId).set(worker).get();
        } else {
            firestore.collection(COLLECTION_NAME).document(userId).set(user).get();
        }
        user.setUserId(userId);
        return user;
    }

    public String deleteUser(String userId) throws ExecutionException, InterruptedException {
        Firestore firestore = FirestoreClient.getFirestore();
        
        firestore.collection(COLLECTION_NAME).document(userId).delete();
        return userId;
    }

}


Directory: backend\src\main\java\com\g4t4\home_cleaning_scheduler\service
--------------------------------------------------------------------------------

File: backend\src\main\java\com\g4t4\home_cleaning_scheduler\service\LeaveRequestService.java
--------------------------------------------------------------------------------
// LeaveRequestService.java
package com.g4t4.home_cleaning_scheduler.service;

import com.g4t4.home_cleaning_scheduler.model.LeaveRequest;
import com.g4t4.home_cleaning_scheduler.repository.LeaveRequestRepository;
import com.google.api.core.ApiFuture;
import com.google.cloud.Timestamp;
import com.google.cloud.firestore.CollectionReference;
import com.google.cloud.firestore.DocumentReference;
import com.google.cloud.firestore.Query;
import com.google.cloud.firestore.QuerySnapshot;
import com.google.cloud.firestore.WriteResult;
import com.google.firebase.cloud.StorageClient;

import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import com.google.cloud.storage.BlobInfo;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

@Service
public class LeaveRequestService {

    private final LeaveRequestRepository leaveRequestRepository;

    public LeaveRequestService(LeaveRequestRepository leaveRequestRepository) {
        this.leaveRequestRepository = leaveRequestRepository;
    }

    public boolean submitLeaveRequest(LeaveRequest leaveRequest, MultipartFile medicalCertificateFile) throws IOException {
        if (leaveRequest.getLeaveRequestId() == null || leaveRequest.getLeaveRequestId().isEmpty()) {
            leaveRequest.setDateOfApplication(Timestamp.now());
        }

        if (leaveRequest.getLeaveType().equalsIgnoreCase("Medical")) {
            if (medicalCertificateFile != null && !medicalCertificateFile.isEmpty()) {            
                String imageUrl = uploadMedicalCertificate(medicalCertificateFile);
                leaveRequest.setMedicalCertificateUrl(imageUrl);
            } else {
                leaveRequest.setMedicalCertificateUrl(null);
            }
        }

        leaveRequest.setStatus("Pending");

        return leaveRequestRepository.saveLeaveRequest(leaveRequest);
    }

    private String uploadMedicalCertificate(MultipartFile file) throws IOException {
        String bucketName = "clean-affcc.appspot.com";
        String folderName = "medical_certificates";

        String fileName = folderName + "/" + UUID.randomUUID().toString() + "_" + file.getOriginalFilename();

        try {
            BlobInfo blobInfo = StorageClient.getInstance().bucket(bucketName)
                    .create(fileName, file.getBytes(), file.getContentType());
            return String.format("https://storage.googleapis.com/%s/%s", bucketName, fileName);
        } catch (Exception e) {
            e.printStackTrace();
            throw new IOException("Failed to upload medical certificate: " + e.getMessage(), e);
        }
    }

    public void updateMedicalCertificate(String leaveRequestId, MultipartFile medicalCertificateFile) throws IOException,ExecutionException, InterruptedException {

        LeaveRequest leaveRequest = leaveRequestRepository.findById(leaveRequestId);
        
        if (leaveRequest == null) {
            throw new IllegalArgumentException("Leave request not found");
        }
        if (medicalCertificateFile == null || medicalCertificateFile.isEmpty()) {
            throw new IllegalArgumentException("Medical certificate image is required.");
        }
    
        String imageUrl = uploadMedicalCertificate(medicalCertificateFile);
        leaveRequest.setMedicalCertificateUrl(imageUrl);

        leaveRequestRepository.updateField(leaveRequestId, "medicalCertificateUrl", imageUrl);
    }
    

    public void updateLeaveRequestStatus(String leaveRequestId, String status) throws ExecutionException, InterruptedException {
        DocumentReference docRef = leaveRequestRepository.getCollectionReference().document(leaveRequestId);

        Map<String, Object> updates = new HashMap<>();
        updates.put("status", status);

        ApiFuture<WriteResult> writeResult = docRef.update(updates);
        writeResult.get(); // Wait for the update to complete
    }

    public List<LeaveRequest> getLeaveRequestsBySupervisor(String supervisorId) throws ExecutionException, InterruptedException {
        CollectionReference collection = leaveRequestRepository.getCollectionReference();
        Query query = collection.whereEqualTo("supervisorId", supervisorId);

        ApiFuture<QuerySnapshot> querySnapshot = query.get();

        List<LeaveRequest> leaveRequests = querySnapshot.get().getDocuments().stream()
                .map(document -> document.toObject(LeaveRequest.class))
                .collect(Collectors.toList());

        return leaveRequests;
    }

    public List<LeaveRequest> getLeaveRequestsByWorker(String workerId) throws ExecutionException, InterruptedException {
        CollectionReference collection = leaveRequestRepository.getCollectionReference();
        Query query = collection.whereEqualTo("workerId", workerId);

        ApiFuture<QuerySnapshot> querySnapshot = query.get();

        List<LeaveRequest> leaveRequests = querySnapshot.get().getDocuments().stream()
                .map(document -> document.toObject(LeaveRequest.class))
                .collect(Collectors.toList());

        return leaveRequests;
    }

    public List<LeaveRequest> getApprovedLeaveRequests() throws ExecutionException, InterruptedException {
        CollectionReference collection = leaveRequestRepository.getCollectionReference();
        Query query = collection.whereEqualTo("status", "Approved");

        ApiFuture<QuerySnapshot> querySnapshot = query.get();

        List<LeaveRequest> leaveRequests = querySnapshot.get().getDocuments().stream()
                .map(document -> document.toObject(LeaveRequest.class))
                .collect(Collectors.toList());

        return leaveRequests;
    }
 
}


File: backend\src\main\java\com\g4t4\home_cleaning_scheduler\service\UserService.java
--------------------------------------------------------------------------------
package com.g4t4.home_cleaning_scheduler.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.g4t4.home_cleaning_scheduler.model.*;
import com.g4t4.home_cleaning_scheduler.repository.UserRepository;

import java.util.Map;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    public User createUser(User.Type userType, String name, String email, String password, int phoneNumber, String username, Map<String, Object> additionalFields) throws ExecutionException, InterruptedException, NoSuchAlgorithmException {
        User user;
        switch (userType) {
            case ADMIN:
                user = new Admin(
                    name, 
                    email, 
                    password, 
                    phoneNumber, 
                    userType, 
                    username, 
                    (String) additionalFields.get("employeeId"),
                    (List<String>) additionalFields.get("listofWorkers"),
                    (Boolean) additionalFields.get("isRoot")
                );
                break;
            case CLIENT:
                user = new Client(
                    name, 
                    email, 
                    password, 
                    phoneNumber, 
                    userType, 
                    username,
                    (ArrayList<Property>) additionalFields.get("properties")
                );
                break;
            case WORKER:
                user = new Worker(
                    name, 
                    email, 
                    password, 
                    phoneNumber, 
                    userType, 
                    username,
                    (String) additionalFields.get("employeeId"),
                    (Integer) additionalFields.get("hoursPerWeek"),
                    (String) additionalFields.get("bio"),
                    (String) additionalFields.get("supervisorId")
                );
                break;
            default:
                throw new IllegalArgumentException("Invalid user type");
        }
        return userRepository.save(user);
    }

    public User updateUser(User.Type userType, String userId, String name, String email, String password, int phoneNumber, String username, Map<String, Object> additionalFields) throws ExecutionException, InterruptedException, NoSuchAlgorithmException {
        User user;
        switch (userType) {
            case ADMIN:
                user = new Admin(
                    name, 
                    email, 
                    password, 
                    phoneNumber, 
                    userType, 
                    username, 
                    (String) additionalFields.get("employeeId"),
                    (List<String>) additionalFields.get("listofWorkers"),
                    (Boolean) additionalFields.get("isRoot")
                );
                break;
            case CLIENT:
                user = new Client(
                    name, 
                    email, 
                    password, 
                    phoneNumber, 
                    userType, 
                    username,
                    (ArrayList<Property>) additionalFields.get("properties")
                );
                break;
            case WORKER:
                user = new Worker(
                    name, 
                    email, 
                    password, 
                    phoneNumber, 
                    userType, 
                    username,
                    (String) additionalFields.get("employeeId"),
                    (Integer) additionalFields.get("hoursPerWeek"),
                    (String) additionalFields.get("bio"),
                    (String) additionalFields.get("supervisorId")
                );
                break;
            default:
                throw new IllegalArgumentException("Invalid user type");
        }
        return userRepository.update(user, userId);
    }

    public String deleteUser(String userId) throws ExecutionException, InterruptedException {
        return userRepository.deleteUser(userId);
        

    }
    public User getUserByUsernameAndPassword(String username, String password) throws ExecutionException, InterruptedException {
        String userType = userRepository.findTypeByUsernameAndPassword(username, password);
        System.out.println(userType);
        
        //Get Admin/Client/Worker object based on AccountType
        if (userType.equals("ADMIN")) {
            return userRepository.findAdminByUsername(username);
        }
        if (userType.equals("CLIENT")) {
            return userRepository.findClientByUsername(username);
        }
        if (userType.equals("WORKER")) {
            return userRepository.findWorkerByUsername(username);
        }
        return null;
    }

    public List<Worker> getAllWorkers() throws ExecutionException, InterruptedException {
        return userRepository.findAllWorkers();
    }

    // public void updateUser(User user) throws ExecutionException, InterruptedException {
    //     userRepository.update(user);
    // }

}


File: backend\src\main\resources\application.properties
--------------------------------------------------------------------------------
Non-code file (content not extracted)


File: leaveapproval.html
--------------------------------------------------------------------------------
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Admin Leave Requests View</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f4f4f9;
            margin: 0;
            padding: 20px;
        }

        h1 {
            text-align: center;
            color: #333;
        }

        button {
            background-color: #007bff;
            color: #fff;
            padding: 10px 20px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            transition: background-color 0.3s;
            margin-bottom: 20px;
        }

        button:hover {
            background-color: #0056b3;
        }

        table {
            width: 100%;
            border-collapse: collapse;
            margin-top: 20px;
        }

        th, td {
            padding: 12px;
            text-align: left;
            border-bottom: 1px solid #ddd;
        }

        th {
            background-color: #007bff;
            color: white;
        }

        tr:hover {
            background-color: #f1f1f1;
        }

        img {
            max-width: 100px;
            height: auto;
        }

        .action-buttons button {
            margin-right: 5px;
        }

        .approve-button {
            background-color: #28a745; /* Green */
        }

        .approve-button:hover {
            background-color: #218838; /* Darker green */
        }

        .reject-button {
            background-color: #dc3545; /* Red */
        }

        .reject-button:hover {
            background-color: #c82333; /* Darker red */
        }
    </style>
</head>
<body>
    <h1>Admin Leave Requests</h1>
    <input type="hidden" id="supervisorId" value="admin1">
    <button onclick="fetchLeaveRequests()">Fetch Leave Requests</button>

    <table id="leaveRequestsTable">
        <thead>
            <tr>
                <th>Leave Request ID</th>
                <th>Worker Name</th>
                <th>Worker ID</th>
                <th>Leave Type</th>
                <th>Status</th>
                <th>Reason</th>
                <th>Medical Certificate</th>
                <th>Actions</th>
            </tr>
        </thead>
        <tbody>
            <!-- Leave requests will be populated here -->
        </tbody>
    </table>

    <script>
        function fetchLeaveRequests() {
            const supervisorId = document.getElementById('supervisorId').value;
            fetch(`http://localhost:8081/api/leave-requests/supervisor/${supervisorId}`)
                .then(response => response.json())
                .then(data => populateTable(data))
                .catch(error => console.error('Error:', error));
        }

        function populateTable(leaveRequests) {
            const tableBody = document.getElementById('leaveRequestsTable').getElementsByTagName('tbody')[0];
            tableBody.innerHTML = ''; // Clear existing rows

            leaveRequests.forEach(request => {
                const row = tableBody.insertRow();
                row.insertCell(0).textContent = request.leaveRequestId;
                row.insertCell(1).textContent = request.workerName; // Display worker's name
                row.insertCell(2).textContent = request.workerId;
                row.insertCell(3).textContent = request.leaveType;
                row.insertCell(4).textContent = request.status;
                row.insertCell(5).textContent = request.reason; // Display reason

                const imgCell = row.insertCell(6);
                if (request.medicalCertificateUrl) {
                    const link = document.createElement('a');
                    link.href = request.medicalCertificateUrl;
                    link.target = '_blank'; // Open in a new tab
                    const img = document.createElement('img');
                    img.src = request.medicalCertificateUrl;
                    img.alt = 'Medical Certificate';
                    link.appendChild(img);
                    imgCell.appendChild(link);
                } else {
                    imgCell.textContent = 'N/A';
                }

                const actionsCell = row.insertCell(7);
                actionsCell.className = 'action-buttons';

                if (request.status === 'Pending') {
                    const approveButton = document.createElement('button');
                    approveButton.textContent = 'Approve';
                    approveButton.className = 'approve-button';
                    approveButton.onclick = () => updateRequestStatus(request.leaveRequestId, 'approve');
                    actionsCell.appendChild(approveButton);

                    const rejectButton = document.createElement('button');
                    rejectButton.textContent = 'Reject';
                    rejectButton.className = 'reject-button';
                    rejectButton.onclick = () => updateRequestStatus(request.leaveRequestId, 'reject');
                    actionsCell.appendChild(rejectButton);
                }
            });
        }

        function updateRequestStatus(leaveRequestId, action) {
            fetch(`http://localhost:8081/api/leave-requests/${leaveRequestId}/${action}`, {
                method: 'PUT'
            })
            .then(response => response.text())
            .then(data => {
                alert(data);
                fetchLeaveRequests(); // Refresh the table
            })
            .catch(error => console.error('Error:', error));
        }
    </script>
</body>
</html>


File: leaverequest.html
--------------------------------------------------------------------------------
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Worker Leave Requests View</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f4f4f9;
            margin: 0;
            padding: 20px;
        }

        h1 {
            text-align: center;
            color: #333;
        }

        form {
            background-color: #fff;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
            max-width: 600px;
            margin: 20px auto;
        }

        label {
            display: block;
            margin-bottom: 8px;
            color: #555;
        }

        input[type="text"],
        input[type="date"],
        select {
            width: 100%;
            padding: 10px;
            margin-bottom: 20px;
            border: 1px solid #ccc;
            border-radius: 4px;
        }

        button {
            background-color: #007bff;
            color: #fff;
            padding: 10px 20px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            transition: background-color 0.3s;
        }

        button:hover {
            background-color: #0056b3;
        }

        table {
            width: 100%;
            border-collapse: collapse;
            margin-top: 20px;
        }

        th, td {
            padding: 12px;
            text-align: left;
            border-bottom: 1px solid #ddd;
        }

        th {
            background-color: #007bff;
            color: white;
        }

        tr:hover {
            background-color: #f1f1f1;
        }

        img {
            max-width: 100px;
            height: auto;
        }
    </style>
</head>
<body>
    <h1>Worker Leave Requests</h1>
    <form id="leaveRequestForm">
        <input type="hidden" id="supervisorId" name="supervisorId" value="admin1">
        
        <label for="leaveType">Leave Type:</label>
        <select id="leaveType" name="leaveType" required>
            <option value="Medical">Medical</option>
            <option value="Annual">Annual</option>
            <option value="Emergency">Emergency</option> <!-- Added new leave type -->
        </select>

        <label for="workerId">Worker ID:</label>
        <input type="text" id="workerId" name="workerId" required>

        <label for="workerName">Worker Name:</label> <!-- New field for worker's name -->
        <input type="text" id="workerName" name="workerName" required>

        <label for="reason">Reason (Optional):</label> <!-- New optional field for reason -->
        <input type="text" id="reason" name="reason">

        <label for="leaveStartDate">Leave Start Date:</label>
        <input type="date" id="leaveStartDate" name="leaveStartDate" required>

        <label for="leaveEndDate">Leave End Date:</label>
        <input type="date" id="leaveEndDate" name="leaveEndDate" required>

        <button type="submit">Submit</button>
    </form>

    <button onclick="fetchLeaveRequests()">Fetch Leave Requests</button>

    <table id="leaveRequestsTable">
        <thead>
            <tr>
                <th>Leave Request ID</th>
                <th>Leave Type</th>
                <th>Status</th>
                <th>Medical Certificate</th>
            </tr>
        </thead>
        <tbody>
            <!-- Leave requests will be populated here -->
        </tbody>
    </table>

    <script>
        document.getElementById('leaveRequestForm').addEventListener('submit', function(event) {
            event.preventDefault();

            const formData = new FormData();
            formData.append('leaveRequest', JSON.stringify({
                workerId: document.getElementById('workerId').value,
                workerName: document.getElementById('workerName').value, // Include worker's name
                reason: document.getElementById('reason').value, // Include reason
                leaveType: document.getElementById('leaveType').value,
                supervisorId: document.getElementById('supervisorId').value,
                leaveStartDate: document.getElementById('leaveStartDate').value,
                leaveEndDate: document.getElementById('leaveEndDate').value
            }));

            fetch('http://localhost:8081/api/leave-requests', {
                method: 'POST',
                body: formData
            })
            .then(response => response.text())
            .then(data => {
                alert(data);
                fetchLeaveRequests(); // Refresh the table
            })
            .catch(error => console.error('Error:', error));
        });

        function fetchLeaveRequests() {
            const workerId = document.getElementById('workerId').value;
            fetch(`http://localhost:8081/api/leave-requests/worker/${workerId}`)
                .then(response => response.json())
                .then(data => populateTable(data))
                .catch(error => console.error('Error:', error));
        }

        function populateTable(leaveRequests) {
            const tableBody = document.getElementById('leaveRequestsTable').getElementsByTagName('tbody')[0];
            tableBody.innerHTML = ''; // Clear existing rows

            leaveRequests.forEach(request => {
                const row = tableBody.insertRow();
                row.insertCell(0).textContent = request.leaveRequestId;

                row.insertCell(1).textContent = request.leaveType;
                row.insertCell(2).textContent = request.status;

                const imgCell = row.insertCell(3);
                if (request.medicalCertificateUrl) {
                    const link = document.createElement('a');
                    link.href = request.medicalCertificateUrl;
                    link.target = '_blank'; // Open in a new tab
                    const img = document.createElement('img');
                    img.src = request.medicalCertificateUrl;
                    img.alt = 'Medical Certificate';
                    link.appendChild(img);
                    imgCell.appendChild(link);
                } else {
                    const uploadButton = document.createElement('button');
                    uploadButton.textContent = 'Upload';
                    uploadButton.onclick = () => uploadMedicalCertificate(request.leaveRequestId);
                    imgCell.appendChild(uploadButton);
                }
            });
        }

        function uploadMedicalCertificate(leaveRequestId) {
            const fileInput = document.createElement('input');
            fileInput.type = 'file';
            fileInput.accept = 'image/*';
            fileInput.onchange = () => {
                const file = fileInput.files[0];
                if (file) {
                    const formData = new FormData();
                    formData.append('medicalCertificate', file);

                    fetch(`http://localhost:8081/api/leave-requests/${leaveRequestId}/medical-certificate`, {
                        method: 'PUT',
                        body: formData
                    })
                    .then(response => response.text())
                    .then(data => {
                        alert(data);
                        fetchLeaveRequests(); // Refresh the table
                    })
                    .catch(error => console.error('Error:', error));
                }
            };
            fileInput.click();
        }
    </script>
</body>
</html>