Make the following change:
1. When user uploads an image to an existing request, it updates the same document, do not create a new document
2. When approve and reject request, it also updates the status of same document, do not create a new document
Give me the entire code.
Also, Help me create 2 simple front end interface to test out the leave request functionality of creating a request, and updating the request with a photo. The other is to accept or reject request. Give me the entire code (just do it in vanila html css js) and what data to fill up to test all the routes of leaverequest controller to ensure everything works.

Code context:
File: src\main\java\com\g4t4\home_cleaning_scheduler\model\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;

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

    private String status; // e.g., Pending, Approved, Rejected
    private String leaveType; // e.g., Medical, Annual, 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

    // Additional fields and methods if necessary
}


File: 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 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: 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.Firestore;

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

@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();
    }

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


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

import com.g4t4.home_cleaning_scheduler.model.LeaveRequest;
import com.g4t4.home_cleaning_scheduler.model.User;
import com.g4t4.home_cleaning_scheduler.repository.LeaveRequestRepository;
import com.g4t4.home_cleaning_scheduler.repository.UserRepository;
import com.google.api.core.ApiFuture;
import com.google.cloud.Timestamp;
import com.google.cloud.firestore.CollectionReference;
import com.google.cloud.firestore.Query;
import com.google.cloud.firestore.QuerySnapshot;
import com.google.cloud.storage.BlobInfo;
import com.google.cloud.storage.Acl;
import com.google.firebase.cloud.StorageClient;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

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

@Service
public class LeaveRequestService {

    private final LeaveRequestRepository leaveRequestRepository;

    @Autowired
    private UserRepository userRepository;

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

    /**
     * Submits a new leave request. Image upload is optional.
     *
     * @param leaveRequest            The leave request details provided by the user.
     * @param medicalCertificateFile  The medical certificate image file (optional).
     * @return true if the leave request is saved successfully.
     * @throws IOException if there's an error processing the image.
     */
    public boolean submitLeaveRequest(LeaveRequest leaveRequest, MultipartFile medicalCertificateFile) throws IOException {
        // Validate leave dates
        if (leaveRequest.getLeaveStartDate() == null || leaveRequest.getLeaveEndDate() == null) {
            throw new IllegalArgumentException("Leave start date and end date are required.");
        }

        if (leaveRequest.getLeaveStartDate().compareTo(leaveRequest.getLeaveEndDate()) > 0) {
            throw new IllegalArgumentException("Leave start date must be before or equal to leave end date.");
        }

        // Set dateOfApplication to current time
        leaveRequest.setDateOfApplication(Timestamp.now());

        // Set status to Pending
        leaveRequest.setStatus("Pending");

        // If leave type is Medical and medicalCertificateFile is provided, upload it
        if ("Medical".equalsIgnoreCase(leaveRequest.getLeaveType())) {
            if (medicalCertificateFile != null && !medicalCertificateFile.isEmpty()) {
                String imageUrl = uploadMedicalCertificate(medicalCertificateFile);
                leaveRequest.setMedicalCertificateUrl(imageUrl);
            }
            // Image upload is optional; proceed even if not provided
        } else {
            // For non-Medical leave types, ensure medicalCertificateUrl is null
            leaveRequest.setMedicalCertificateUrl(null);
        }

        // Generate a unique ID for the leave request
        String leaveRequestId = UUID.randomUUID().toString();
        leaveRequest.setLeaveRequestId(leaveRequestId);

        return leaveRequestRepository.saveLeaveRequest(leaveRequest);
    }

    /**
     * Uploads a medical certificate image and updates the corresponding leave request.
     *
     * @param leaveRequestId         The ID of the leave request to update.
     * @param medicalCertificateFile The medical certificate image file.
     * @return true if the update is successful.
     * @throws IOException              if there's an error processing the image.
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public boolean uploadMedicalCertificate(String leaveRequestId, MultipartFile medicalCertificateFile) throws IOException, ExecutionException, InterruptedException {
        if (medicalCertificateFile == null || medicalCertificateFile.isEmpty()) {
            throw new IllegalArgumentException("Medical certificate image is required.");
        }

        // Retrieve existing leave request
        Optional<LeaveRequest> optionalLeaveRequest = leaveRequestRepository.get(leaveRequestId);
        if (!optionalLeaveRequest.isPresent()) {
            throw new IllegalArgumentException("Leave request not found.");
        }

        LeaveRequest leaveRequest = optionalLeaveRequest.get();

        // Check if leave type is Medical
        if (!"Medical".equalsIgnoreCase(leaveRequest.getLeaveType())) {
            throw new IllegalArgumentException("Cannot upload medical certificate to a non-medical leave request.");
        }

        // Upload the image
        String imageUrl = uploadMedicalCertificate(medicalCertificateFile);
        leaveRequest.setMedicalCertificateUrl(imageUrl);

        // Update the leave request
        return leaveRequestRepository.saveLeaveRequest(leaveRequest);
    }

    /**
     * Updates the status of a leave request.
     *
     * @param leaveRequestId The ID of the leave request.
     * @param adminId        The ID of the admin updating the request.
     * @param status         The new status ("Approved" or "Rejected").
     * @return true if the update is successful.
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public boolean updateLeaveRequestStatus(String leaveRequestId, String adminId, String status) throws ExecutionException, InterruptedException {
        // Retrieve the leave request
        Optional<LeaveRequest> optionalLeaveRequest = leaveRequestRepository.get(leaveRequestId);
        if (!optionalLeaveRequest.isPresent()) {
            throw new IllegalArgumentException("Leave request not found.");
        }
        LeaveRequest leaveRequest = optionalLeaveRequest.get();

        // Check if the admin is the supervisor of the worker
        if (!leaveRequest.getSupervisorId().equals(adminId)) {
            throw new IllegalArgumentException("Admin is not authorized to update this leave request.");
        }

        leaveRequest.setStatus(status);
        return leaveRequestRepository.saveLeaveRequest(leaveRequest);
    }

    /**
     * Approves a leave request.
     *
     * @param leaveRequestId The ID of the leave request.
     * @param adminId        The ID of the admin approving the request.
     * @return true if the approval is successful.
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public boolean approveLeaveRequest(String leaveRequestId, String adminId) throws ExecutionException, InterruptedException {
        return updateLeaveRequestStatus(leaveRequestId, adminId, "Approved");
    }

    /**
     * Rejects a leave request.
     *
     * @param leaveRequestId The ID of the leave request.
     * @param adminId        The ID of the admin rejecting the request.
     * @return true if the rejection is successful.
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public boolean rejectLeaveRequest(String leaveRequestId, String adminId) throws ExecutionException, InterruptedException {
        return updateLeaveRequestStatus(leaveRequestId, adminId, "Rejected");
    }

    /**
     * Uploads the medical certificate image to Firebase Storage with public access.
     *
     * @param file The image file to upload.
     * @return The public URL of the uploaded image.
     * @throws IOException if an error occurs during upload.
     */
    private String uploadMedicalCertificate(MultipartFile file) throws IOException {
        String bucketName = "clean-affcc.appspot.com"; // Replace with your Firebase bucket name
        String folderName = "medical_certificates";

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

        // Build the blob info with public read ACL
        BlobInfo blobInfo = BlobInfo.newBuilder(bucketName, fileName)
                .setContentType(file.getContentType())
                .setAcl(
                        List.of(Acl.of(Acl.User.ofAllUsers(), Acl.Role.READER))
                )
                .build();

        // Upload to Cloud Storage
        StorageClient.getInstance().bucket(bucketName).create(blobInfo.getName(), file.getBytes(), blobInfo.getContentType());

        // Return the public URL to the uploaded file
        return String.format("https://storage.googleapis.com/%s/%s", bucketName, fileName);
    }

    /**
     * Retrieves all leave requests supervised by a specific supervisor.
     *
     * @param supervisorId The ID of the supervisor.
     * @return A list of matching leave requests.
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public List<LeaveRequest> getLeaveRequestsBySupervisor(String supervisorId) throws ExecutionException, InterruptedException {
        CollectionReference collection = leaveRequestRepository.getCollectionReference();
        Query query = collection.whereEqualTo("supervisorId", supervisorId);

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

        return querySnapshot.get().getDocuments().stream()
                .map(document -> document.toObject(LeaveRequest.class))
                .collect(Collectors.toList());
    }

    /**
     * Retrieves all leave requests submitted by a specific worker.
     *
     * @param workerId The ID of the worker.
     * @return A list of matching leave requests.
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public List<LeaveRequest> getLeaveRequestsByWorker(String workerId) throws ExecutionException, InterruptedException {
        CollectionReference collection = leaveRequestRepository.getCollectionReference();
        Query query = collection.whereEqualTo("workerId", workerId);

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

        return querySnapshot.get().getDocuments().stream()
                .map(document -> document.toObject(LeaveRequest.class))
                .collect(Collectors.toList());
    }

    /**
     * Retrieves all approved leave requests.
     *
     * @return A list of approved leave requests.
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public List<LeaveRequest> getApprovedLeaveRequests() throws ExecutionException, InterruptedException {
        CollectionReference collection = leaveRequestRepository.getCollectionReference();
        Query query = collection.whereEqualTo("status", "Approved");

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

        return querySnapshot.get().getDocuments().stream()
                .map(document -> document.toObject(LeaveRequest.class))
                .collect(Collectors.toList());
    }

    /**
     * Scheduled task that runs daily at midnight to notify admins about pending medical certificates.
     *
     * @throws ExecutionException
     * @throws InterruptedException
     */
    @Scheduled(cron = "0 0 0 * * *") // Runs daily at midnight
    public void notifyAdminsForPendingMedicalCertificates() throws ExecutionException, InterruptedException {
        CollectionReference collection = leaveRequestRepository.getCollectionReference();
        Query query = collection
                .whereEqualTo("leaveType", "Medical")
                .whereEqualTo("status", "Pending")
                .whereEqualTo("medicalCertificateUrl", null);

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

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

        for (LeaveRequest leaveRequest : leaveRequests) {
            // Get supervisor/admin information
            String supervisorId = leaveRequest.getSupervisorId();
            User supervisor = userRepository.findUserById(supervisorId);

            if (supervisor != null) {
                // Send notification to supervisor
                // Implement your notification logic here (e.g., send email)
                System.out.println("Sending notification to admin: " + supervisor.getEmail());
                // Placeholder for notification logic
            }
        }
    }
}