Help me with the following an I will tip you $2000.
1. Ensure the summary are only obtained from 2 months before today and 3 months after today. Example: Today is 5th October. Data should only be retrieved from 5th August to 5th Jan
2. Ensure no data overlap for schedule summary. Right now 31st October has 2 sets of summary on the calendar.
Give me the entire code.

Code context:
Directory: backend\app\controllers
--------------------------------------------------------------------------------

File: backend\app\controllers\staff_controller.py
--------------------------------------------------------------------------------
# app/controllers/staff_controller.py
from flask import Blueprint, request, jsonify
from flask import abort
from app.services.staff_service import StaffService

staff_bp = Blueprint('staff', __name__, url_prefix='/api')

@staff_bp.route('/login', methods=['POST'])
def login():
    data = request.get_json()

    if not data or not data.get('email') or not data.get('password'):
        return jsonify({"message": "Missing email or password"}), 400

    staff = StaffService.authenticate_staff(data['email'], data['password'])

    if not staff:
        return jsonify({"message": "Invalid email or password"}), 401
    
    staff_details = staff.to_dict()
    print(staff_details)
    return jsonify({
        "message": "Login successful",
        **staff_details
    }), 200

@staff_bp.route('/staff/<int:staff_id>', methods=['GET'])
def get_staff_by_id(staff_id):
    staff = StaffService.get_staff_by_id(staff_id)
    if not staff:
        abort(404, description="Staff not found")
    return jsonify(staff.to_dict()), 200


File: backend\app\controllers\wfh_controller.py
--------------------------------------------------------------------------------
from flask import Blueprint, request, jsonify
from app.services.wfh_request_service import WFHRequestService
from app.services.wfh_schedule_service import WFHScheduleService
from app.services.wfh_check_service import WFHCheckService
from app.models.wfh_request import WFHRequest
from datetime import datetime, timedelta, date
from app import db

wfh_bp = Blueprint('wfh', __name__, url_prefix='/api')

@wfh_bp.route('/request', methods=['POST'])
def create_wfh_request():
    print("\n===== NEW WFH REQUEST =====")
    print("Received a new WFH request")
    data = request.get_json()

    # Validate input
    print("\n----- Input Validation -----")
    required_fields = ['staff_id', 'manager_id', 'reason_for_applying', 
                       'date', 'duration', 'dept', 'position']
    for field in required_fields:
        if field not in data:
            print(f"Validation failed: Missing required field: {field}")
            return jsonify({"message": f"Missing required field: {field}"}), 400
    print("Input validation successful")

    try:
        # Get today's date for the request_date
        today = datetime.now().date()

        # Create WFHRequest
        print("\n----- Creating WFH Request -----")
        end_date = None
        if 'end_date' in data and data['end_date']:
            if(not isinstance(data['end_date'], date)):
                end_date = datetime.strptime(data['end_date'], '%Y-%m-%d').date()

        wfh_request = WFHRequestService.create_request(
            staff_id=data['staff_id'],
            manager_id=data['manager_id'],
            request_date=today,
            start_date=data['date'],
            end_date=end_date,
            reason_for_applying=data['reason_for_applying']
        )
        print(f"WFH request created successfully. Request ID: {wfh_request.request_id}")

        # Create WFH Schedules
        print("\n----- Creating WFH Schedules -----")
        if(not isinstance(data['date'], date)):
            start_date = datetime.strptime(data['date'], '%Y-%m-%d').date()
        wfh_schedules = WFHScheduleService.create_schedule(
            request_id=wfh_request.request_id,
            staff_id=data['staff_id'],
            manager_id=data['manager_id'],
            start_date=start_date,
            end_date=end_date,
            duration=data['duration'],
            dept=data['dept'],
            position=data['position']
        )
        print(f"WFH schedules created successfully. Number of schedules: {len(wfh_schedules)}")

        print("\n===== WFH REQUEST COMPLETED =====")
        print("WFH request and schedules creation completed successfully")
        print(f"Request ID: {wfh_request.request_id}")
        print(f"Number of Schedules: {len(wfh_schedules)}")
        print(f"Status: {wfh_request.status}")
        print("==================================\n")

        return jsonify({
            "message": "WFH request and schedules created successfully",
            "request_id": wfh_request.request_id,
            "schedule_count": len(wfh_schedules),
            "status": wfh_request.status
        }), 201

    except ValueError as ve:
        print(f"\n===== ERROR OCCURRED =====")
        print(f"Validation failed: {ve}")
        print("============================\n")
        db.session.rollback()
        return jsonify({"message": str(ve)}), 400
        
    except Exception as e:
        db.session.rollback()
        print("\n===== ERROR OCCURRED =====")
        print(f"An error occurred while processing the WFH request: {str(e)}")
        print("============================\n")
        db.session.rollback()
        return jsonify({"message": f"An error occurred: {str(e)}"}), 500

@wfh_bp.route('/pending-requests/<int:manager_id>', methods=['GET'])
def get_pending_requests(manager_id):
    print(f"\n===== GET PENDING REQUESTS =====")
    print(f"Retrieving pending requests for manager_id: {manager_id}")
    
    try:
        print("Calling WFHRequestService.get_pending_requests_for_manager()")
        pending_requests = WFHRequestService.get_pending_requests_for_manager(manager_id)
        
        print(f"Number of pending requests retrieved: {len(pending_requests)}")
        
        response = [request.to_dict() for request in pending_requests]
        print("Successfully converted requests to dictionary format")
        
        print("===== GET PENDING REQUESTS COMPLETED =====\n")
        return jsonify(response), 200
    
    except Exception as e:
        print(f"ERROR: An exception occurred while retrieving pending requests")
        print(f"Exception details: {str(e)}")
        print("===== GET PENDING REQUESTS FAILED =====\n")
        return jsonify({"message": f"An error occurred: {str(e)}"}), 500


@wfh_bp.route('/update-request', methods=['PATCH'])
def update_wfh_request():
    print(f"\n===== UPDATE REQUESTS =====")
    data = request.get_json()
    request_id = data['request_id']
    new_request_status = data['request_status']
    reason = data['reason']

    # Get the current date
    current_date = datetime.now().date()
    # Calculate the date 2 months ago
    two_months_ago = current_date - timedelta(days=60)

    print(f"Updating request for request_id: {request_id}")
    
    try:
        # Fetch the request
        request_obj = WFHRequest.query.get(request_id)
        if not request_obj:
            return jsonify({"message": "Request does not exist"}), 404

        if new_request_status == 'APPROVED':
            staff_id = request_obj.staff_id
            start_date = request_obj.start_date
            end_date = request_obj.end_date

            dates_to_check = []

            if end_date:
                # Recurring request
                current_date_iter = start_date
                while current_date_iter <= end_date:
                    if current_date_iter >= current_date:
                        dates_to_check.append(current_date_iter)
                    current_date_iter += timedelta(days=7)
            else:
                # Single date request
                if start_date >= current_date:
                    dates_to_check.append(start_date)
            violated_dates = []
            # Check WFH policy for each date
            for date_to_check in dates_to_check:
                result = WFHCheckService.check_department_count(staff_id, date_to_check)
                if result != 'Success':
                    violated_dates.append(date_to_check)
            
            if len(violated_dates) > 0:
                formatted_dates = ",".join([d.strftime("%d-%m-%Y") for d in violated_dates])
                return jsonify({"message": f"Cannot approve request due to policy violation on date(s) {formatted_dates}"}), 400

        print("Calling WFHRequestService.update_request()")
        response = WFHRequestService.update_request(
            request_id, new_request_status, two_months_ago, reason)
        if response == True:
            response2 = WFHScheduleService.update_schedule(request_id, new_request_status)

            if response2 == True:
                print("Successfully updated")
                print("===== UPDATE PENDING REQUESTS COMPLETED =====\n")
                return jsonify(f"Successfully updated request {request_id} as {new_request_status}"), 200
                
            else:
                return jsonify({"message": response2}), 404
                
        else:
            return jsonify({"message": response}), 404

    except Exception as e:
        db.session.rollback()
        print("\n===== ERROR OCCURRED =====")
        print(f"An error occurred while processing the WFH request: {str(e)}")
        print("============================\n")
        return jsonify({"message": f"An error occurred: {str(e)}"}), 500


@wfh_bp.route('/reject-expired-request', methods=['POST'])
def reject_expired_request():
    """
    Check the current date against the start date in the database rows.
    If the start date is older than 2 months, update the status to 'REJECTED'.
    """
    try:
        # Get the current date
        current_date = datetime.now().date()

        # Calculate the date 2 months ago
        two_months_ago = current_date - timedelta(days=60)

        # Fetch all requests from the database
        expired = WFHRequestService.reject_expired(two_months_ago)
        return jsonify({"message": f"Updated {expired} requests to 'REJECTED'."}), 200

    except Exception as e:
        db.session.rollback()  # Rollback the session in case of an error
        return jsonify({"message": f"An error occurred: {str(e)}"}), 500
    
@wfh_bp.route('/check-wfh-count', methods=['POST'])
def check_wfh_count():

    data = request.get_json()
    staff_id = data['staff_id']
    date = data['date']
    
    try:
        result = WFHCheckService.check_department_count(staff_id, date)
        return('done')
        print('done')
    except Exception as e:
        db.session.rollback()  # Rollback the session in case of an error
        return jsonify({"message": f"An error occurred: {str(e)}"}), 500


@wfh_bp.route('/manager-schedule-summary/<int:manager_id>', methods=['GET'])
def manager_schedule_summary(manager_id):
    try:
        # Get start_date and end_date from query params, else use default range
        start_date = request.args.get('start_date')
        end_date = request.args.get('end_date')
        today = datetime.now().date()
        if start_date:
            start_date = datetime.strptime(start_date, '%Y-%m-%d').date()
        else:
            # Default to current month
            start_date = today.replace(day=1)
        
        if end_date:
            end_date = datetime.strptime(end_date, '%Y-%m-%d').date()
        else:
            # Default to end of current month
            next_month = today.replace(day=28) + timedelta(days=4)  # this will never fail
            end_date = next_month - timedelta(days=next_month.day)
        
        data = WFHScheduleService.get_manager_schedule_summary(manager_id, start_date, end_date)
        return jsonify(data), 200

    except Exception as e:
        print(f"Error in manager_schedule_summary: {str(e)}")
        return jsonify({"message": f"An error occurred: {str(e)}"}), 500

@wfh_bp.route('/manager-schedule-detail/<int:manager_id>/<date_str>', methods=['GET'])
def manager_schedule_detail(manager_id, date_str):
    try:
        date_obj = datetime.strptime(date_str, '%Y-%m-%d').date()
        data = WFHScheduleService.get_manager_schedule_detail(manager_id, date_obj)
        return jsonify(data), 200
    except Exception as e:
        print(f"Error in manager_schedule_detail: {str(e)}")
        return jsonify({"message": f"An error occurred: {str(e)}"}), 500
    
@wfh_bp.route('/personal-schedule/<int:staff_id>', methods=['GET'])
def personal_schedule(staff_id):
    try:
        # Get start_date and end_date from query params, else use default range
        start_date = request.args.get('start_date')
        end_date = request.args.get('end_date')
        today = datetime.now().date()
        if start_date:
            start_date = datetime.strptime(start_date, '%Y-%m-%d').date()
        else:
            start_date = today - timedelta(days=60)  # 2 months before today

        if end_date:
            end_date = datetime.strptime(end_date, '%Y-%m-%d').date()
        else:
            end_date = today + timedelta(days=90)  # 3 months after today

        data = WFHScheduleService.get_personal_schedule(staff_id, start_date, end_date)
        return jsonify(data), 200

    except Exception as e:
        print(f"Error in manager_schedule_summary: {str(e)}")
        return jsonify({"message": f"An error occurred: {str(e)}"}), 500


File: backend\app\controllers\__init__.py
--------------------------------------------------------------------------------


Directory: backend\app\controllers\__pycache__
--------------------------------------------------------------------------------

File: backend\app\controllers\__pycache__\staff_controller.cpython-311.pyc
--------------------------------------------------------------------------------
Non-code file (content not extracted)


File: backend\app\controllers\__pycache__\wfh_controller.cpython-311.pyc
--------------------------------------------------------------------------------
Non-code file (content not extracted)


File: backend\app\controllers\__pycache__\__init__.cpython-311.pyc
--------------------------------------------------------------------------------
Non-code file (content not extracted)


Directory: backend\app\models
--------------------------------------------------------------------------------

File: backend\app\models\staff.py
--------------------------------------------------------------------------------
from app import db

class Staff(db.Model):
    __tablename__ = 'Staff'

    staff_id = db.Column(db.Integer, primary_key=True)
    staff_fname = db.Column(db.String(255), nullable=False)
    staff_lname = db.Column(db.String(255), nullable=False)
    dept = db.Column(db.String(255), nullable=True)
    position = db.Column(db.String(255), nullable=True)
    country = db.Column(db.String(255), nullable=True)
    email = db.Column(db.String(255), nullable=False, unique=True)
    reporting_manager = db.Column(db.Integer, db.ForeignKey('Staff.staff_id'), nullable=True)
    role = db.Column(db.Integer, nullable=True)
    password = db.Column(db.String(255), nullable=True)

    def to_dict(self):
        return {
            'staff_id': self.staff_id,
            'staff_fname': self.staff_fname,
            'staff_lname': self.staff_lname,
            'dept': self.dept,
            'position': self.position,
            'country': self.country,
            'email': self.email,
            'reporting_manager': self.reporting_manager,
            'role': self.role
        }


File: backend\app\models\wfh_request.py
--------------------------------------------------------------------------------
from app import db
from sqlalchemy.sql import expression

class WFHRequest(db.Model):
    __tablename__ = 'WFHRequest'

    request_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    staff_id = db.Column(db.Integer, db.ForeignKey('Staff.staff_id'), nullable=False)
    manager_id = db.Column(db.Integer, db.ForeignKey('Staff.staff_id'), nullable=False)
    request_date = db.Column(db.Date, nullable=False)
    start_date = db.Column(db.Date, nullable=False)
    end_date = db.Column(db.Date, nullable=True)
    status = db.Column(db.String(20), nullable=False, server_default=expression.text("'PENDING'"))
    reason_for_applying = db.Column(db.Text, nullable=False)
    reason_for_rejection = db.Column(db.Text, nullable=True)

    def to_dict(self):
        end_date = None
        if self.end_date:
            end_date = self.end_date.isoformat()

        return {
            'request_id': self.request_id,
            'staff_id': self.staff_id,
            'manager_id': self.manager_id,
            'request_date': self.request_date.isoformat(),
            'start_date': self.start_date.isoformat(),
            'end_date': end_date,
            'status': self.status,
            'reason_for_applying': self.reason_for_applying,
            'reason_for_rejection': self.reason_for_rejection,
            'is_recurring': self.end_date is not None
        }


File: backend\app\models\wfh_schedule.py
--------------------------------------------------------------------------------
from app import db
from sqlalchemy.sql import expression

class WFHSchedule(db.Model):
    __tablename__ = 'WFHSchedule'

    schedule_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    request_id = db.Column(db.Integer, db.ForeignKey('WFHRequest.request_id'), nullable=False)
    staff_id = db.Column(db.Integer, db.ForeignKey('Staff.staff_id'), nullable=False)
    manager_id = db.Column(db.Integer, db.ForeignKey('Staff.staff_id'), nullable=False)
    date = db.Column(db.Date, nullable=False)
    duration = db.Column(db.String(20), nullable=False)
    status = db.Column(db.String(20), nullable=False, server_default=expression.text("'PENDING'"))
    dept = db.Column(db.String(50), nullable=False)
    position = db.Column(db.String(50), nullable=False)
    reason_for_withdrawing = db.Column(db.Text, nullable=True)
    

    def to_dict(self):
        return {
            'schedule_id': self.schedule_id,
            'request_id': self.request_id,
            'staff_id': self.staff_id,
            'manager_id': self.manager_id,
            'date': self.date.isoformat(),
            'duration': self.duration,
            'status': self.status,
            'dept': self.dept,
            'position': self.position,
            'reason_for_withdrawing': self.reason_for_withdrawing
        }


File: backend\app\models\__init__.py
--------------------------------------------------------------------------------


Directory: backend\app\models\__pycache__
--------------------------------------------------------------------------------

File: backend\app\models\__pycache__\staff.cpython-311.pyc
--------------------------------------------------------------------------------
Non-code file (content not extracted)


File: backend\app\models\__pycache__\wfh_request.cpython-311.pyc
--------------------------------------------------------------------------------
Non-code file (content not extracted)


File: backend\app\models\__pycache__\wfh_schedule.cpython-311.pyc
--------------------------------------------------------------------------------
Non-code file (content not extracted)


File: backend\app\models\__pycache__\__init__.cpython-311.pyc
--------------------------------------------------------------------------------
Non-code file (content not extracted)


Directory: backend\app\services
--------------------------------------------------------------------------------

File: backend\app\services\staff_service.py
--------------------------------------------------------------------------------
from app.models.staff import Staff
from sqlalchemy.exc import SQLAlchemyError

class StaffService:
    @staticmethod
    def authenticate_staff(email, password):
        try:
            staff = Staff.query.filter_by(email=email).first()
            if staff and staff.password == password:
                return staff
            else:
                print("Wrong email or password")
                return None
            
        except SQLAlchemyError as e:
            print(f"Database error during authentication: {str(e)}")
            raise


    @staticmethod
    def get_staff_by_id(staff_id):
        try:
            staff = Staff.query.get(staff_id)
            if staff is None:
                raise ValueError(f"No staff found with id: {staff_id}")
            return staff
        except SQLAlchemyError as e:
            print(f"Database error while fetching staff by ID: {str(e)}")
            raise
        except ValueError as e:
            print(str(e))
            raise

File: backend\app\services\wfh_check_service.py
--------------------------------------------------------------------------------
from app import db
from app.models.staff import Staff
from app.models.wfh_schedule import WFHSchedule
from app.services.staff_service import StaffService

class WFHCheckService:
    @staticmethod
    def check_department_count(staff_id, date):
        # identify the staff department
        department = StaffService.get_staff_by_id(staff_id).dept

        # find out how many people in that department
        department_count = WFHCheckService.department_count(department)

        # now, find out how many people in that department also have approved WFH on that date
        applied_count = 0

        # get all the schedules with the same date and status 'APPROVED' or 'PENDING'
        schedules = db.session.query(WFHSchedule).filter(
            WFHSchedule.date == date,
            WFHSchedule.status == 'APPROVED')
        
        for schedule in schedules:
            # get the department of the schedule's staff
            schedule_staff = StaffService.get_staff_by_id(schedule.staff_id)
            schedule_department = schedule_staff.dept

            # if department is same as the one applying, increase counter
            if schedule_department == department:
                applied_count += 1

        # Calculate the percentage of staff working from home, if including this request
        wfh_percentage = (applied_count +1) / department_count

        # If more than 50% are working from home, return an error
        if wfh_percentage > 0.5:
            print(f"Max limit for Dept: {department} on date: {date}")
            return 'Unable to apply due to max limit'
        else:
            return 'Success'

    @staticmethod
    def department_count(department):
        staff_count = db.session.query(Staff).filter_by(dept=department).count()
        return staff_count
        
    



File: backend\app\services\wfh_request_service.py
--------------------------------------------------------------------------------
from app import db
from app.models.wfh_request import WFHRequest
from datetime import datetime, timedelta, date

class WFHRequestService:
    @staticmethod
    def create_request(staff_id, manager_id, request_date, start_date, end_date, reason_for_applying):
        error_message = None
        # Check if the start date is valid (within 2 months before or 3 months after today)
        max_valid_date = datetime.now().date() + timedelta(days=90)
        min_valid_date = datetime.now().date() - timedelta(days=60)
        if(not isinstance(start_date, date)):
            start_date = datetime.strptime(start_date, "%Y-%m-%d").date()


        if start_date < min_valid_date or start_date > max_valid_date:
            raise ValueError("Start date must be between 2 months ago and 3 months from now.")
            

        # Check if the end date is valid for recurring requests
        
        if end_date:
            if(not isinstance(end_date, date)):
                print("try2")
                print(type(end_date))
                print(isinstance(end_date, date))
                end_date = datetime.strptime(end_date, "%Y-%m-%d").date()
                print("try2")

            if end_date < min_valid_date or end_date > max_valid_date:
                raise ValueError("End date must be between 2 months ago and 3 months from now.")
            # print(type(end_date))
            # print(type(start_date))
            
            if start_date >= end_date:
                raise ValueError("End date must be after start date.")

        # Check if there's an existing request for the same day
        existing_request = WFHRequest.query.filter(
            WFHRequest.staff_id == staff_id,
            WFHRequest.start_date == start_date,
            WFHRequest.status != 'EXPIRED'
        ).first()

        if existing_request and (not end_date) and existing_request.status != 'REJECTED':
            
            raise ValueError("A request for this date already exists.")

        new_request = WFHRequest(
            staff_id=staff_id,
            manager_id=manager_id,
            request_date=request_date,
            start_date=start_date,
            end_date=end_date,
            reason_for_applying=reason_for_applying
        )
        db.session.add(new_request)
        db.session.commit()
        return new_request
    
    @staticmethod
    def get_pending_requests_for_manager(manager_id):
        return WFHRequest.query.filter_by(manager_id=manager_id, status='PENDING').all()
    

    @staticmethod
    def update_request(request_id, new_request_status, two_months_ago, reason):   #new_request_status = approved / rejected
        # Fetch the request by its ID
        request = WFHRequest.query.get(request_id)
        
        # Check if the request exists
        if request:
            
            # Check if within date range
            request_date = request.start_date
            if WFHRequestService.check_date(request_date , two_months_ago):

                # Update the status field
                request.status = new_request_status

                # provide reason for reject
                if new_request_status == 'REJECTED':
                    request.reason_for_rejection = reason

            # not within date range = not suppose to approve
            else:
                return "The date is invalid to be approved"
            
            # Commit the updated record to the database
            db.session.commit()

            # happy path 
            return True
        
        else:

            # Request does not exist
            return "Request Does not Exist!"
        
    @staticmethod
    def reject_expired(two_months_ago):
        # Fetch all requests from the database
        requests = WFHRequest.query.all()
        updated_count = 0

        # Check each request
        for request in requests:

            # Check if date range is within 2 months and only update those that has not been updated
            if (WFHRequestService.check_date(request.start_date , two_months_ago) != True) and request.status!= 'REJECTED':
                request.status = 'REJECTED'  # Update status to rejected
                request.reason_for_rejection = "Rejected due to past time period" 
                updated_count += 1

        # Commit the changes to the database
        db.session.commit()
        return updated_count
    

    @staticmethod
    def check_date(request_date , two_months_ago):
        if request_date > two_months_ago:
            return True
        else:
            return False

File: backend\app\services\wfh_schedule_service.py
--------------------------------------------------------------------------------
from app import db
from app.models.wfh_schedule import WFHSchedule
from app.models.wfh_request import WFHRequest
from app.models.staff import Staff
from datetime import timedelta

class WFHScheduleService:
    @staticmethod
    def create_schedule(request_id, staff_id, manager_id, start_date, end_date, duration, dept, position):
        schedules = []
        current_date = start_date
        
        while True:

            existing_schedule = WFHSchedule.query.filter(
                WFHSchedule.staff_id == staff_id,
                WFHSchedule.date == current_date,
                WFHSchedule.status != 'EXPIRED'
            ).first()
            
            if existing_schedule and existing_schedule.status != 'REJECTED':
                print(f"Schedule for {current_date} already exists")
                current_date += timedelta(days=7)  # Move to the next week
                if end_date is None or current_date > end_date:
                    break  # If no end_date or we've passed the end_date, stop creating schedule
                continue

            new_schedule = WFHSchedule(
                request_id=request_id,
                staff_id=staff_id,
                manager_id=manager_id,
                date=current_date,
                duration=duration,
                dept=dept,
                position=position
            )
            db.session.add(new_schedule)
            schedules.append(new_schedule)
            print(f"Schedule for {current_date} created successfully")

            current_date += timedelta(days=7)  # Move to the next week
            if end_date is None or current_date > end_date:
                break  # If no end_date or we've passed the end_date, stop creating schedule
        
        if len(schedules) == 0:
            print("No schedules were created. Removing request from entry")
            db.session.delete(WFHRequest.query.get(request_id))
            db.session.commit()
            raise ValueError("No schedules were created")

        
        db.session.commit()
        return schedules
    
    @staticmethod
    def update_schedule(request_id, status):
        # Fetch the existing schedules based on the request_id
        schedules = WFHSchedule.query.filter_by(request_id=request_id).all()

        if not schedules:
            raise ValueError(f"No schedules found for request_id: {request_id}")


        for schedule in schedules:
            if status == "APPROVED":
                schedule.status = "APPROVED"
            elif status == "REJECTED":
                schedule.status = "REJECTED"

        # Commit the updated schedules to the database
        db.session.commit()
        print(f"Schedules for request_id {request_id} have been updated successfully.")

        return True
    
    @staticmethod
    def get_manager_schedule_summary(manager_id, start_date, end_date):
        staff_list = Staff.query.filter_by(reporting_manager=manager_id).all()
        staff_ids = [staff.staff_id for staff in staff_list]
        total_staff = len(staff_ids)
        if total_staff == 0:
            return {'dates': []}

        date_list = []
        current_date = start_date
        while current_date <= end_date:
            date_list.append(current_date)
            current_date += timedelta(days=1)

        dates_data = []
        for d in date_list:
            date_str = d.isoformat()

            # Initialize counts
            wfh_count_am = 0
            wfh_count_pm = 0

            # Get all approved schedules for the date
            schedules = WFHSchedule.query.filter(
                WFHSchedule.staff_id.in_(staff_ids),
                WFHSchedule.date == d,
                WFHSchedule.status == 'APPROVED'
            ).all()

            for sched in schedules:
                if sched.duration == 'FULL_DAY':
                    wfh_count_am += 1
                    wfh_count_pm += 1
                elif sched.duration == 'HALF_DAY_AM':
                    wfh_count_am += 1
                elif sched.duration == 'HALF_DAY_PM':
                    wfh_count_pm += 1

            office_count_am = total_staff - wfh_count_am
            office_count_pm = total_staff - wfh_count_pm

            dates_data.append({
                'date': date_str,
                'total_staff': total_staff,
                'wfh_count_am': wfh_count_am,
                'wfh_count_pm': wfh_count_pm,
                'office_count_am': office_count_am,
                'office_count_pm': office_count_pm
            })
        return {'dates': dates_data}

    @staticmethod
    def get_manager_schedule_detail(manager_id, date):
        staff_list = Staff.query.filter_by(reporting_manager=manager_id).all()
        staff_ids = [staff.staff_id for staff in staff_list]
        if not staff_ids:
            return {'date': date.isoformat(), 'staff': []}

        staff_status = {}
        for staff in staff_list:
            staff_status[staff.staff_id] = {
                'staff_id': staff.staff_id,
                'name': f"{staff.staff_fname} {staff.staff_lname}",
                'position': staff.position,
                'status_am': 'OFFICE',
                'status_pm': 'OFFICE'
            }
        print(staff_status)

        schedules = WFHSchedule.query.filter(
            WFHSchedule.staff_id.in_(staff_ids),
            WFHSchedule.date == date,
            WFHSchedule.status == 'APPROVED'
        ).all()

        for sched in schedules:
            if sched.duration == 'FULL_DAY':
                staff_status[sched.staff_id]['status_am'] = 'WFH'
                staff_status[sched.staff_id]['status_pm'] = 'WFH'
            elif sched.duration == 'HALF_DAY_AM':
                staff_status[sched.staff_id]['status_am'] = 'WFH'
                staff_status[sched.staff_id]['status_pm'] = 'OFFICE'
            elif sched.duration == 'HALF_DAY_PM':
                staff_status[sched.staff_id]['status_am'] = 'OFFICE'
                staff_status[sched.staff_id]['status_pm'] = 'WFH'

        staff_list_status = list(staff_status.values())

        return {
            'date': date.isoformat(),
            'staff': staff_list_status
        }
    
    @staticmethod
    def get_personal_schedule(staff_id, start_date, end_date):

        date_list = []
        current_date = start_date
        while current_date <= end_date:
            date_list.append(current_date)
            current_date += timedelta(days=1)

        dates_data = []

        for d in date_list:
            date_str = d.isoformat()
            schedule = ''


            # Get all approved schedules for the date
            schedules = WFHSchedule.query.filter(
                WFHSchedule.staff_id == staff_id,
                WFHSchedule.date == d,
                WFHSchedule.status.in_(['APPROVED', 'PENDING']) 
            ).all()

            for sched in schedules:
                if sched.duration == 'FULL_DAY' and sched.status == 'APPROVED':
                    schedule += 'FullDay'
                elif sched.duration == 'HALF_DAY_AM' and sched.status == 'APPROVED':
                    schedule += 'AM'
                elif sched.duration == 'HALF_DAY_PM' and sched.status == 'APPROVED':
                    schedule += 'PM'
                elif sched.duration == 'FULL_DAY' and sched.status == 'PENDING':
                    schedule += 'FullDayPending'
                elif sched.duration == 'HALF_DAY_AM' and sched.status == 'PENDING':
                    schedule += 'AMPending'
                elif sched.duration == 'HALF_DAY_PM' and sched.status == 'PENDING':
                    schedule += 'PMPending'


            dates_data.append({
                'date': date_str,
                'schedule': schedule,
            })
        return {'dates': dates_data}

File: backend\app\services\__init__.py
--------------------------------------------------------------------------------


Directory: backend\app\services\__pycache__
--------------------------------------------------------------------------------

File: backend\app\services\__pycache__\staff_service.cpython-311.pyc
--------------------------------------------------------------------------------
Non-code file (content not extracted)


File: backend\app\services\__pycache__\wfh_check_service.cpython-311.pyc
--------------------------------------------------------------------------------
Non-code file (content not extracted)


File: backend\app\services\__pycache__\wfh_request_service.cpython-311.pyc
--------------------------------------------------------------------------------
Non-code file (content not extracted)


File: backend\app\services\__pycache__\wfh_schedule_service.cpython-311.pyc
--------------------------------------------------------------------------------
Non-code file (content not extracted)


File: backend\app\services\__pycache__\__init__.cpython-311.pyc
--------------------------------------------------------------------------------
Non-code file (content not extracted)


File: backend\app\__init__.py
--------------------------------------------------------------------------------
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_cors import CORS
from config import Config

# Initialize SQLAlchemy
db = SQLAlchemy()


def create_app(config_class=Config):
    # Initialize Flask app
    app = Flask(__name__)
    app.config.from_object(config_class)

    # Configure CORS
    CORS(app, resources={r"/*": {"origins": "*"}}, supports_credentials=True)

    # Initialize extensions
    db.init_app(app)

    # Import and initialize the staff controller
    from app.controllers import staff_controller, wfh_controller

    app.register_blueprint(staff_controller.staff_bp)
    app.register_blueprint(wfh_controller.wfh_bp)

    @app.route("/")
    def test():
        return "Welcome to the WFH Scheduler API."

    return app


File: backend\WFH-Schedule.sql
--------------------------------------------------------------------------------
CREATE DATABASE wfh_scheduler;
USE wfh_scheduler;
CREATE TABLE Staff (
    staff_id INT PRIMARY KEY,
    staff_fname VARCHAR(255),
    staff_lname VARCHAR(255),
    dept VARCHAR(255),
    position VARCHAR(255),
    country VARCHAR(255),
    email VARCHAR(255),
    reporting_manager INT,
    role INT,
    password VARCHAR(255),
    FOREIGN KEY (reporting_manager) REFERENCES Staff(staff_id)
);

CREATE TABLE WFHRequest (
    request_id INT PRIMARY KEY AUTO_INCREMENT,
    staff_id INT NOT NULL,
    manager_id INT NOT NULL,
    request_date DATE NOT NULL,
    start_date DATE NOT NULL,
    end_date DATE DEFAULT NULL,
    status VARCHAR(20) DEFAULT 'PENDING', -- 'PENDING', 'REJECTED'
    reason_for_applying TEXT,
    reason_for_rejection TEXT DEFAULT NULL,
    FOREIGN KEY (staff_id) REFERENCES Staff(staff_id),
    FOREIGN KEY (manager_id) REFERENCES Staff(staff_id)
);

CREATE TABLE WFHSchedule (
    schedule_id INT PRIMARY KEY AUTO_INCREMENT,
    request_id INT NOT NULL,
    staff_id INT NOT NULL,
    manager_id INT NOT NULL,
    date DATE NOT NULL,
    duration VARCHAR(20) NOT NULL, -- 'FULL_DAY', 'HALF_DAY_AM', 'HALF_DAY_PM'
    status VARCHAR(20) DEFAULT 'PENDING', -- 'PENDING', 'WITHDRAWN', 'REJECTED'
    dept VARCHAR(255) NOT NULL,
    position VARCHAR(255) NOT NULL,    
    reason_for_withdrawing TEXT DEFAULT NULL,
    FOREIGN KEY (request_id) REFERENCES WFHRequest(request_id),
    FOREIGN KEY (staff_id) REFERENCES Staff(staff_id),
    FOREIGN KEY (manager_id) REFERENCES Staff(staff_id)
);


INSERT INTO Staff (staff_id, staff_fname, staff_lname, dept, position, country, email, reporting_manager, role, password)
VALUES 
(1, 'Test', 'Director', 'Test Department', 'Director', 'Test Country', 'director@test.com', NULL, 1, 'testpassword1'),
(2, 'Test', 'Manager', 'Test Department', 'Manager', 'Test Country', 'manager@test.com', 1, 3, 'testpassword3'),
(3, 'Test', 'Staff', 'Test Department', 'Staff', 'Test Country', 'staff@test.com', 2, 2, 'testpassword2');

-- DROP TABLE `wfh_scheduler`.`WFHSchedule`;
-- DROP TABLE `wfh_scheduler`.`WFHRequest`;



File: frontend\src\views\LoginView.vue
--------------------------------------------------------------------------------
<template>
    <div class="login-container">
      <div class="login-form-container">
        <div class="app-title">WFH Scheduler</div>
        <div class="login-form">
          <form @submit.prevent="handleLogin">
            <div class="form-group">
              <label for="email">Email</label>
              <input type="email" id="email" v-model="email" required>
            </div>
            <div class="form-group">
              <label for="password">Password</label>
              <input type="password" id="password" v-model="password" required>
            </div>
            <button type="submit">Log In</button>
          </form>
          <div v-if="errorMessage" class="error-message">
            {{ errorMessage }}
          </div>
        </div>
      </div>
    </div>
  </template>
  
  <script setup>

  import { ref } from 'vue';
  import { useRouter } from 'vue-router';
  import axios from 'axios';
  
  const router = useRouter();
  const email = ref('');
  const password = ref('');
  const errorMessage = ref('');
  
  const handleLogin = async () => {
    try {
      const response = await axios.post(`${import.meta.env.VITE_API_URL}/api/login`, {
        email: email.value,
        password: password.value
      });
  
      if (response.data.staff_id) {
        // Store the user info in localStorage
        
        localStorage.setItem('user', JSON.stringify(response.data));
  
        // Redirect to dashboard
        router.push('/schedule');
      }
    } catch (error) {
      if (error.response) {
        errorMessage.value = error.response.data.message;
      } else {
        errorMessage.value = 'An error occurred. Please try again.';
      }
    }
  };
  </script>
  
  <style scoped>
  .login-container {
    height: 100vh;
    width: 100vw;
    display: flex;
    justify-content: center;
    align-items: center;
    background-image: linear-gradient(rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7)), url('@/assets/images/login-image.jpg');
    background-size: cover;
    background-position: center;
  }
  
  .login-form-container {
    background-color: rgba(255, 255, 255, 0.95);
    padding: 2rem;
    border-radius: 15px;
    width: 100%;
    max-width: 400px;
    box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
  }
  
  .app-title {
    font-size: 2.5rem;
    font-weight: bold;
    color: #141b4d;
    margin-bottom: 1.5rem;
    text-align: center;
    text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
  }
  
  .login-form h2 {
    text-align: center;
    margin-bottom: 1.5rem;
    color: #141b4d;
  }
  
  .form-group {
    margin-bottom: 1.5rem;
  }
  
  label {
    display: block;
    margin-bottom: 0.5rem;
    color: #141b4d;
    font-weight: 600;
    text-align: left;
  }
  
  input {
    width: 100%;
    padding: 0.75rem;
    border: 1px solid #bdc3c7;
    border-radius: 4px;
    font-size: 1rem;
    transition: border-color 0.3s ease;
  }
  
  input:focus {
    outline: none;
    border-color: #141b4d;
  }
  
  button {
    width: 100%;
    padding: 0.75rem;
    background-color: #141b4d;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-size: 1rem;
    font-weight: 600;
    transition: background-color 0.3s ease;
  }
  
  button:hover {
    background-color: #111d6c;
  }
  
  .error-message {
    color: #e74c3c;
    text-align: center;
    margin-top: 1rem;
  }
  </style>

File: frontend\src\views\ManagerTeamScheduleView.vue
--------------------------------------------------------------------------------
<template>
  <div id="app">
    <Navbar />
    <div class="dashboard-container">
      <div class="main-section">
        <div class="calendar-container">
          <FullCalendar 
            :options="calendarOptions"
          />
        </div>
        <div class="right-container" :class="{'show': isRightContainerVisible }">
          <div v-if="selectedDateDetails" class="details-container">
            <div class="details-header">
              <h2>Details for {{ selectedDate }}</h2>
              <!-- Close button is always rendered but hidden on larger screens via CSS -->
              <button class="close-button" @click="isRightContainerVisible = false">×</button>
            </div>
            <div class="time-slot" v-for="(slot, key) in selectedDateDetails" :key="key">
              <details open>
                <summary>
                  <span class="time-label">{{ key }}</span>
                  <div class="badge-container">
                    <span class="badge badge-office">
                      In Office ({{ slot.inOffice }}/{{ slot.total }})
                    </span>
                    <span class="badge badge-wfh">
                      WFH ({{ slot.wfh }}/{{ slot.total }})
                    </span>
                  </div>
                </summary>
                <div class="teams">
                  <div class="team card" v-for="team in slot.teams" :key="team.teamName">
                    <details>
                      <summary>
                        {{ team.teamName }}
                        <span class="badge badge-office">
                          In Office: {{ team.inOffice }}/{{ team.total }}
                        </span>
                        <span class="badge badge-wfh">
                          WFH: {{ team.wfh }}/{{ team.total }}
                        </span>
                      </summary>
                      <div class="staff-list">
                        <div class="staff-card" v-for="staff in team.staff" :key="staff.staff_id">
                          <span :class="['status-indicator', staff.status === 'OFFICE' ? 'green' : 'yellow']"></span>
                          <span class="staff-name">{{ staff.name }}</span>
                        </div>
                      </div>
                    </details>
                  </div>
                </div>
              </details>
            </div>
          </div>
          <div v-else class="no-selection">
            <p>Select a date on the calendar to view details.</p>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, computed, onUnmounted } from 'vue';
import Navbar from '@/components/Navbar.vue';
import FullCalendar from '@fullcalendar/vue3';
import dayGridPlugin from '@fullcalendar/daygrid';
import timeGridPlugin from '@fullcalendar/timegrid';
import interactionPlugin from '@fullcalendar/interaction';
import axios from 'axios';

const user = ref({});
const selectedDate = ref('');
const events = ref([]);
const selectedDateDetails = ref(null);
const isRightContainerVisible = ref(false);
const isSmallScreen = ref(window.innerWidth < 768); // Reactive property for screen size

const handleResize = () => {
  isSmallScreen.value = window.innerWidth < 768;
};

onMounted(() => {
  const storedUser = localStorage.getItem('user');
  if (storedUser) {
    user.value = JSON.parse(storedUser);
    if (user.value.role === 3) {
      // Fetch current month's summary first
      const currentMonthStart = new Date();
      currentMonthStart.setDate(1);
      const currentMonthEnd = new Date(currentMonthStart);
      currentMonthEnd.setMonth(currentMonthEnd.getMonth() + 1);
      currentMonthEnd.setDate(0); // last day of current month

      fetchManagerScheduleSummary(currentMonthStart, currentMonthEnd).then(() => {
        // After current month, fetch other months in the background
        fetchAdditionalMonths();
      });
    }
  }
  window.addEventListener('resize', handleResize);
});

onUnmounted(() => {
  window.removeEventListener('resize', handleResize);
});

const computeMinDate = computed(() => {
  const date = new Date();
  date.setMonth(date.getMonth() - 2);
  date.setDate(1);
  return date;
});

const computeMaxDate = computed(() => {
  const date = new Date();
  date.setMonth(date.getMonth() + 3);
  date.setDate(new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate());
  return date;
});

function getColorForPercentage(percentage) {
  percentage = parseFloat(percentage);
  if (percentage < 50) {
    return '#FF6B6B'; // Red
  } else if (percentage < 75) {
    return '#FFD93D'; // Yellow
  } else {
    return '#6BCB77'; // Green
  }
}

async function fetchManagerScheduleSummary(start, end) {
  try {
    const startDateStr = start.toISOString().split('T')[0];
    const endDateStr = end.toISOString().split('T')[0];

    const response = await axios.get(`${import.meta.env.VITE_API_URL}/api/manager-schedule-summary/${user.value.staff_id}`, {
      params: {
        start_date: startDateStr,
        end_date: endDateStr
      }
    });

    const data = response.data.dates;
    const newEvents = [];

    data.forEach(item => {
      const dateStr = item.date;
      const totalStaff = item.total_staff;
      const officeCountAm = item.office_count_am;
      const officeCountPm = item.office_count_pm;

      const percentageAm = ((officeCountAm / totalStaff) * 100).toFixed(2);
      const percentagePm = ((officeCountPm / totalStaff) * 100).toFixed(2);

      const colorAm = getColorForPercentage(percentageAm);
      const colorPm = getColorForPercentage(percentagePm);

      newEvents.push({
        title: `(AM) In Office: ${officeCountAm} / ${totalStaff}`,
        start: dateStr + 'T09:00:00',
        end: dateStr + 'T13:00:00',
        allDay: false,
        extendedProps: {
          timeOfDay: 'AM',
          officeCount: officeCountAm,
          totalStaff,
          percentage: percentageAm
        },
        backgroundColor: colorAm,
        borderColor: colorAm
      });

      newEvents.push({
        title: `(PM) In Office: ${officeCountPm} / ${totalStaff}`,
        start: dateStr + 'T14:00:00',
        end: dateStr + 'T18:00:00',
        allDay: false,
        extendedProps: {
          timeOfDay: 'PM',
          officeCount: officeCountPm,
          totalStaff,
          percentage: percentagePm
        },
        backgroundColor: colorPm,
        borderColor: colorPm
      });
    });

    events.value = [...events.value, ...newEvents];
  } catch (error) {
    console.error('Error fetching manager schedule summary:', error);
  }
}

async function fetchAdditionalMonths() {
  const fetchPromises = [];
  const currentDate = new Date();

  // Fetch 2 months before current month
  for (let i = 1; i <= 2; i++) {
    const start = new Date(currentDate.getFullYear(), currentDate.getMonth() - i, 1);
    const end = new Date(currentDate.getFullYear(), currentDate.getMonth() - i + 1, 0); // Last day of month
    fetchPromises.push(fetchManagerScheduleSummary(start, end));
  }

  // Fetch 3 months after current month
  for (let i = 1; i <= 3; i++) {
    const start = new Date(currentDate.getFullYear(), currentDate.getMonth() + i, 1);
    const end = new Date(currentDate.getFullYear(), currentDate.getMonth() + i + 1, 0); // Last day of month
    fetchPromises.push(fetchManagerScheduleSummary(start, end));
  }

  try {
    await Promise.all(fetchPromises);
    console.log('All additional months fetched successfully.');
  } catch (error) {
    console.error('Error fetching additional months:', error);
  }
}

function getDateStr(dateObj) {
  // Ensure the date is formatted correctly using local timezone
  const year = dateObj.getFullYear();
  const month = ('0' + (dateObj.getMonth() + 1)).slice(-2);
  const day = ('0' + dateObj.getDate()).slice(-2);
  return `${year}-${month}-${day}`;
}

const calendarOptions = computed(() => ({
  plugins: [dayGridPlugin, timeGridPlugin, interactionPlugin],
  initialView: 'dayGridMonth',
  headerToolbar: {
    left: 'prev,next today',
    center: 'title',
    right: 'dayGridMonth,timeGridWeek,timeGridDay',
  },
  dateClick: handleDateClick,
  eventClick: handleEventClick,
  datesSet: handleDatesSet,
  selectable: true,
  validRange: {
    start: computeMinDate.value,
    end: computeMaxDate.value,
  },
  weekends: false,
  events: events.value,
  allDaySlot: false,
  eventContent: function(arg) {
    if (arg.event.extendedProps.timeOfDay) {
      const timeOfDay = arg.event.extendedProps.timeOfDay;
      const officeCount = arg.event.extendedProps.officeCount;
      const totalStaff = arg.event.extendedProps.totalStaff;
      const percentage = arg.event.extendedProps.percentage;

      const container = document.createElement('div');
      container.style.display = 'flex';
      container.style.flexDirection = 'column';
      container.style.justifyContent = 'center';
      container.style.alignItems = 'flex-start'; // Align badges beside text
      container.style.backgroundColor = arg.event.backgroundColor;

      // Assign a class based on background color
      if (arg.event.backgroundColor === '#FFD93D') { // Yellow
        container.classList.add('yellow-event');
      } else {
        container.classList.add('default-event');
      }

      container.style.borderRadius = '4px';
      container.style.margin = '1px 0';
      container.style.padding = '2px 4px';
      container.style.fontSize = '0.8em';
      container.style.width = '100%';
      container.style.height = '100%';

      // Create a wrapper for the label and badges
      const labelContainer = document.createElement('div');
      labelContainer.style.display = 'flex';
      labelContainer.style.alignItems = 'center';
      labelContainer.style.width = '100%';
      labelContainer.style.justifyContent = 'space-between';

      const label = document.createElement('span');
      label.textContent = `${timeOfDay}`;

      const badgesWrapper = document.createElement('div');
      badgesWrapper.style.display = 'flex';
      badgesWrapper.style.gap = '4px';

      // Create Office Badge
      const officeBadge = document.createElement('span');
      officeBadge.classList.add('badge', 'badge-office-calendar');
      officeBadge.textContent = `In Office: ${officeCount}`;

      // Create WFH Badge
      const wfhBadge = document.createElement('span');
      wfhBadge.classList.add('badge', 'badge-wfh-calendar');
      wfhBadge.textContent = `WFH: ${totalStaff - officeCount}`;

      badgesWrapper.appendChild(officeBadge);
      badgesWrapper.appendChild(wfhBadge);

      labelContainer.appendChild(label);
      labelContainer.appendChild(badgesWrapper);

      container.appendChild(labelContainer);

      return { domNodes: [container] };
    }
    return null;
  },
  eventDidMount: function(info) {
    // This function is called after the event has been rendered

    // Ensure the container has the correct text color based on background
    const bgColor = info.event.backgroundColor.toUpperCase();
    const yellowHex = '#FFD93D';

    if (bgColor === yellowHex) {
      // Set text color to black
      info.el.style.color = '#000000';

      // Additionally, set all child elements to inherit the color
      // This ensures badges and other texts also become black
      const elements = info.el.querySelectorAll('*');
      elements.forEach(el => {
        // Override any inline styles that set color
        el.style.color = 'inherit';
      });
    } else {
      // Default text color (you can adjust this as needed)
      info.el.style.color = '#ffffff';

      const elements = info.el.querySelectorAll('*');
      elements.forEach(el => {
        el.style.color = 'inherit';
      });
    }
  },
  eventClassNames: 'calendar-event',
  slotMinTime: '09:00:00',
  slotMaxTime: '18:00:00',
  businessHours: {
    startTime: '09:00',
    endTime: '18:00',
    daysOfWeek: [1, 2, 3, 4, 5],
  },
}));

async function handleDateClick(info) {
  if (info.view.type === 'dayGridMonth') {
    const clickedDate = new Date(info.dateStr);

    if (clickedDate < computeMinDate.value + 1) {
      alert('You cannot select a date before two months ago.');
    } else if (clickedDate > computeMaxDate.value) {
      alert('You cannot select a date beyond three months ahead.');
    } else {
      selectedDate.value = info.dateStr;
      if (user.value.role === 3) {
        await displayDateDetails(info.dateStr);
      }
    }
  }
}

async function handleEventClick(info) {
  if (user.value.role === 3) {
    const dateStr = getDateStr(info.event.start);
    const viewType = info.view.type;
    if (viewType === 'timeGridWeek' || viewType === 'timeGridDay') {
      selectedDate.value = dateStr;
      await displayDateDetails(dateStr);
    }
  }
}

async function handleDatesSet(info) {
  if (info.view.type === 'timeGridDay') {
    const dateStr = getDateStr(info.start);
    selectedDate.value = dateStr;
    if (user.value.role === 3) {
      const detailData = await fetchManagerScheduleDetail(dateStr);
      if (detailData) {
        let groupedData = {
          AM: {
            inOffice: 0,
            wfh: 0,
            total: detailData.staff.length,
            teams: {}
          },
          PM: {
            inOffice: 0,
            wfh: 0,
            total: detailData.staff.length,
            teams: {}
          }
        };

        detailData.staff.forEach(staff => {
          const position = staff.position || 'Unknown';

          // AM
          if (!groupedData.AM.teams[position]) {
            groupedData.AM.teams[position] = { teamName: position, inOffice: 0, wfh: 0, total: 0, staff: [] };
          }
          groupedData.AM.teams[position].total += 1;
          if (staff.status_am === 'OFFICE') {
            groupedData.AM.teams[position].inOffice += 1;
            groupedData.AM.inOffice += 1;
          } else {
            groupedData.AM.teams[position].wfh += 1;
            groupedData.AM.wfh += 1;
          }
          groupedData.AM.teams[position].staff.push({ staff_id: staff.staff_id, name: staff.name, status: staff.status_am });

          // PM
          if (!groupedData.PM.teams[position]) {
            groupedData.PM.teams[position] = { teamName: position, inOffice: 0, wfh: 0, total: 0, staff: [] };
          }
          groupedData.PM.teams[position].total += 1;
          if (staff.status_pm === 'OFFICE') {
            groupedData.PM.teams[position].inOffice += 1;
            groupedData.PM.inOffice += 1;
          } else {
            groupedData.PM.teams[position].wfh += 1;
            groupedData.PM.wfh += 1;
          }
          groupedData.PM.teams[position].staff.push({ staff_id: staff.staff_id, name: staff.name, status: staff.status_pm });
        });

        groupedData.AM.total = detailData.staff.length;
        groupedData.PM.total = detailData.staff.length;

        selectedDateDetails.value = groupedData;
        // Only show the card on large screens when navigating dates
        if (!isSmallScreen.value) {
          isRightContainerVisible.value = true;
        }
        // On small screens, do not automatically show the card when navigating
      } else {
        alert('Error fetching details for the selected date.');
      }
    }
  }
}

async function displayDateDetails(dateStr) {
  const detailData = await fetchManagerScheduleDetail(dateStr);
  if (detailData) {
    let groupedData = {
      AM: {
        inOffice: 0,
        wfh: 0,
        total: detailData.staff.length,
        teams: {}
      },
      PM: {
        inOffice: 0,
        wfh: 0,
        total: detailData.staff.length,
        teams: {}
      }
    };

    detailData.staff.forEach(staff => {
      const position = staff.position || 'Unknown';

      // AM
      if (!groupedData.AM.teams[position]) {
        groupedData.AM.teams[position] = { teamName: position, inOffice: 0, wfh: 0, total: 0, staff: [] };
      }
      groupedData.AM.teams[position].total += 1;
      if (staff.status_am === 'OFFICE') {
        groupedData.AM.teams[position].inOffice += 1;
        groupedData.AM.inOffice += 1;
      } else {
        groupedData.AM.teams[position].wfh += 1;
        groupedData.AM.wfh += 1;
      }
      groupedData.AM.teams[position].staff.push({ staff_id: staff.staff_id, name: staff.name, status: staff.status_am });

      // PM
      if (!groupedData.PM.teams[position]) {
        groupedData.PM.teams[position] = { teamName: position, inOffice: 0, wfh: 0, total: 0, staff: [] };
      }
      groupedData.PM.teams[position].total += 1;
      if (staff.status_pm === 'OFFICE') {
        groupedData.PM.teams[position].inOffice += 1;
        groupedData.PM.inOffice += 1;
      } else {
        groupedData.PM.teams[position].wfh += 1;
        groupedData.PM.wfh += 1;
      }
      groupedData.PM.teams[position].staff.push({ staff_id: staff.staff_id, name: staff.name, status: staff.status_pm });
    });

    groupedData.AM.total = detailData.staff.length;
    groupedData.PM.total = detailData.staff.length;

    selectedDateDetails.value = groupedData;
    // Show the card
    isRightContainerVisible.value = true;
  } else {
    alert('Error fetching details for the selected date.');
  }
}

async function fetchManagerScheduleDetail(dateStr) {
  try {
    const response = await axios.get(`${import.meta.env.VITE_API_URL}/api/manager-schedule-detail/${user.value.staff_id}/${dateStr}`);
    return response.data;
  } catch (error) {
    console.error('Error fetching manager schedule detail:', error);
    return null;
  }
}
</script>

<style scoped>
.dashboard-container {
  display: flex;
  flex-direction: column;
  height: 100vh;
  overflow: hidden;
}

.main-section {
  display: flex;
  height: 100%;
  overflow: hidden;
}

.calendar-container {
  width: 70%;
  padding: 1rem;
  flex: 1;
  min-width: 300px;
  overflow-y: auto;
}

.right-container {
  width: 30%;
  padding: 1rem;
  background-color: #ffffff;
  border-left: 1px solid #dfe3e8;
  overflow-y: auto;
  flex: none;
  min-width: 300px;
  height: 100%;
  position: relative;
}

.right-container.show {
  display: block;
}

.details-container {
  display: flex;
  flex-direction: column;
}

.details-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 1rem;
}

.details-header h2 {
  margin: 0;
  font-size: 1.5rem;
  color: #2e3a59;
}

/* Hide the close button on larger screens */
.close-button {
  background: none;
  border: none;
  font-size: 1.5rem;
  cursor: pointer;
  color: #888;
  display: none; /* Hidden by default */
}

/* Display the close button only on small screens */
@media (max-width: 768px) {
  .close-button {
    display: block;
  }
}

.time-slot {
  margin-bottom: 1em;
}

.time-label {
  font-weight: bold;
  margin-right: 0.5em;
}

.badge-container {
  display: flex;
  gap: 0.5em;
}

.badge {
  display: inline-block;
  padding: 0.25em 0.75em;
  border-radius: 12px;
  font-size: 0.85em;
  color: #ffffff; /* Default text color */
}

.badge-office {
  background-color: #6BCB77; /* Green */
}

.badge-wfh {
  background-color: #FFD93D; /* Yellow */
  color: #000000; /* Black text for better visibility */
}

.badge-office-calendar {
  background-color: #6BCB77; /* Green */
  color: #ffffff;
  font-size: 0.7em;
  padding: 2px 4px;
  border-radius: 8px;
}

.badge-wfh-calendar {
  background-color: #FFD93D; /* Yellow */
  color: #000000; /* Black text for better visibility */
  font-size: 0.7em;
  padding: 2px 4px;
  border-radius: 8px;
}

.teams {
  margin-left: 0.5em;
}

.team {
  margin-bottom: 0.5em;
  border: 1px solid #dfe3e8;
  border-radius: 8px;
  padding: 0.5em;
  background-color: #f9fafb;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}

.team details summary {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-weight: bold;
  cursor: pointer;
  color: #2e3a59;
}

.team details[open] summary {
  border-bottom: 1px solid #dfe3e8;
  margin-bottom: 0.5em;
}

.team .badge {
  margin-left: 0.5em;
}

.staff-list {
  display: flex;
  flex-direction: column;
  margin-top: 0.5em;
}

.staff-card {
  display: flex;
  align-items: center;
  border: 1px solid #dfe3e8;
  border-radius: 6px;
  padding: 0.5em;
  margin-bottom: 0.5em;
  background-color: #ffffff;
}

.staff-card .status-indicator {
  display: inline-block;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  margin-right: 0.5em;
}

.staff-card .status-indicator.green {
  background-color: #6BCB77;
}

.staff-card .status-indicator.yellow {
  background-color: #FFD93D;
}

.staff-card .staff-name {
  font-weight: 500;
  color: #2e3a59;
}

.right-container details {
  margin: 0.5em 0;
}

.right-container summary {
  font-weight: bold;
  cursor: pointer;
  color: #2e3a59;
}

.fc {
  width: 100%;
  height: 100%;
}

.no-selection {
  text-align: center;
  color: #888;
  margin-top: 2rem;
}

.fc-event-main {
  padding: 2px;
}

.fc-daygrid-event {
  white-space: normal !important;
}

.calendar-event {
  /* Additional styling if needed */
}

.fc-daygrid-event-harness {
  margin-bottom: 2px !important;
}

/* Adjust the calendar height */
.fc .fc-view-harness {
  min-height: 0;
}

/* Hide the scrollbar but allow scrolling */
.calendar-container::-webkit-scrollbar {
  width: 0;
  height: 0;
}

.right-container::-webkit-scrollbar {
  width: 8px;
}

.right-container::-webkit-scrollbar-thumb {
  background-color: rgba(0, 0, 0, 0.2);
  border-radius: 4px;
}

.right-container::-webkit-scrollbar-track {
  background-color: transparent;
}

/* Media query for responsive layout */
@media (max-width: 768px) {
  .calendar-container {
    width: 100%;
  }

  .right-container {
    position: fixed; /* Change to fixed for overlay */
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background-color: rgba(255, 255, 255, 0.95); /* Semi-transparent background */
    transform: translateX(100%);
    transition: transform 0.3s ease-in-out;
    z-index: 1000;
    overflow-y: auto;
    padding: 1rem;
  }

  .right-container.show {
    transform: translateX(0);
  }

  /* Ensure text within badges remains black on yellow backgrounds */
  .badge-wfh, .badge-wfh-calendar {
    color: #000000 !important;
  }
}

@media (max-width: 768px) {
  .main-section {
    flex-direction: column;
  }

  .right-container {
    width: 100%;
    min-width: unset;
    height: auto;
    position: fixed; /* Ensure it overlays */
    right: 0;
    top: 0;
    box-shadow: none;
    background-color: #ffffff;
  }

  .calendar-container {
    width: 100%;
    height: 100%;
  }

  .details-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
}

/* New Styles for Conditional Text Colors */
.yellow-event {
  color: #000000 !important;
}

.default-event {
  color: #ffffff !important;
}

.yellow-event .badge,
.default-event .badge {
  color: inherit !important;
}
</style>