Help me with the following:
1. Change ManagerTeamView.vue such that it does this instead - For managers with role 3, the view remains the same. However, for managers with role 1, there are 2 cases. One is that they are direct supervisors of workers with role of 2. In that case, the view remains the same. However, some managers with role 1 might be managers of role 3. In this case, the card view should show in terms of the manager's team instead. For example, finance director with role of 1. He is the manager of finance managers with role of 3. The finance managers are managers of staffs with role 2. As such the card should show as finance manager 1 team, finance manager 2 team etc, and when clicked in the finance manager team, it will then show the normal view of the cards by their position, and when clicked into it, will show the individual staff like the original.
2. Change CompleteScheduleView.vue such that it is sort by department first, but when clicked into it, it will show the different teams based on reportingmanager_id. This will be hirerachical. For example, it will first show the ceo's availability, followed by the other departments. Then within the departments, it will show the department directors availability. After that, it depends whether there are further managers. If there are further managers, it will show as "manager's team" and when clicked, it will show the manager availability, followed by the staff's availability. If there are no further managers, then just show the staff directly under the director.

Give me the entire code.

Code context:
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'],duration=data['duration']
        )
        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_team_count(staff_id, date_to_check,request_obj.duration)
                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']
    duration = data['duration']
    
    try:
        result = WFHCheckService.check_team_count(staff_id, date, duration)
        return('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:
            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_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>', methods=['GET'])
def manager_schedule_detail(manager_id, date):
    try:
        date_obj = datetime.strptime(date, '%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
    
@wfh_bp.route('staff-schedule-summary/<int:reporting_manager>', methods = ['GET'])
def staff_schedule_summary(reporting_manager):
    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_staff_schedule_summary(reporting_manager, start_date, end_date)
        return jsonify(data), 200
    except Exception as e:
        print(f"Error in staff_schedule_summary: {str(e)}")
        return jsonify({"message": f"An error occurred: {str(e)}"}), 500

@wfh_bp.route('/staff-schedule-detail/<int:staff_id>/<date>', methods=['GET'])
def staff_schedule_detail(staff_id, date):
    try:
        date_obj = datetime.strptime(date, '%Y-%m-%d').date()
        data = WFHScheduleService.get_staff_schedule_detail(staff_id, date_obj)
        print("Success")
        return jsonify(data), 200
    except Exception as e:
        print(f"Error in staff_schedule_detail: {str(e)}")
        return jsonify({"message": f"An error occurred: {str(e)}"}), 500
@wfh_bp.route('/company-schedule-summary', methods=['GET'])
def company_schedule_summary():
    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_company_schedule_summary(start_date, end_date)
        return jsonify(data), 200

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

@wfh_bp.route('/company-schedule-detail/<date>', methods=['GET'])
def company_schedule_detail(date):
    try:
        date_obj = datetime.strptime(date, '%Y-%m-%d').date()
        data = WFHScheduleService.get_company_schedule_detail(date_obj)
        return jsonify(data), 200
    except Exception as e:
        print(f"Error in company_schedule_detail: {str(e)}")
        return jsonify({"message": f"An error occurred: {str(e)}"}), 500

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_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\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_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:
            # Query all schedules for the current date
            existing_schedules = WFHSchedule.query.filter(
                WFHSchedule.staff_id == staff_id,
                WFHSchedule.date == current_date,
                WFHSchedule.status != 'EXPIRED'
            ).all()
            
            # Check if any of the existing schedules are 'APPROVED' or 'PENDING'
            if any(schedule.status in ['APPROVED', 'PENDING'] for schedule in existing_schedules):
                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

            # Create a new schedule if no 'APPROVED' or 'PENDING' schedules exist
            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):
        
        manager = Staff.query.filter_by(staff_id=manager_id).first()
    
        if not manager or not manager.dept:
            # Handle the case where the manager does not exist or has no department
            return {'dates': []}  

        # Get the department of the existing manager
        department = manager.dept
        staff_list = Staff.query.filter_by(dept=department, role = 2, 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):
        manager = Staff.query.filter_by(staff_id=manager_id).first()
    
        if not manager or not manager.dept:
            # Handle the case where the manager does not exist or has no department
            return {'date': date.isoformat(), 'staff': []} 

        # Get the department of the existing manager
        department = manager.dept
        staff_list = Staff.query.filter_by(dept=department, role = 2, reporting_manager= manager_id).all()
        staff_ids = [staff.staff_id for staff in staff_list]
        if not staff_ids:
            return {'date': date.isoformat(), 'staff': []}
        print(staff_list)

        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}

    @staticmethod
    def get_staff_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_staff_schedule_detail(staff_id, date):

        manager_id = Staff.query.filter_by(staff_id=staff_id).first().reporting_manager

        if manager_id:
            print(f"The manager in charge has ID: {manager_id}")

        else:
            manager_id = staff_id
            print(f"The manager in charge has ID: {manager_id}")
        
        
        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_company_schedule_summary(start_date, end_date):
        # First, get all dates in the range
        date_list = []
        current_date = start_date
        while current_date <= end_date:
            date_list.append(current_date)
            current_date += timedelta(days=1)
        
        # Get total staff count
        total_staff = Staff.query.count()
        if total_staff == 0:
            return {'dates': []}
        
        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.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_company_schedule_detail(date):
        # Get all staffs
        staff_list = Staff.query.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,
                'dept': staff.dept,
                'status_am': 'OFFICE',
                'status_pm': 'OFFICE'
            }

        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
        }

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,
    duration VARCHAR(20) NOT NULL, -- 'FULL_DAY', 'HALF_DAY_AM', 'HALF_DAY_PM'
    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 
-- CEO with 7 directors
(1, 'Test', 'CEO', 'CEO', 'MD', 'Singapore', 'ceo@test.com', NULL, 1, 'password'),
(2, 'Sales', 'Director', 'Sales', 'Director', 'Singapore', 'salesdirector@test.com', 1, 1, 'password'),
(3, 'Consultancy', 'Director', 'Consultancy', 'Director', 'Singapore', 'consultancydirector@test.com', 1, 1, 'password'),
(4, 'Solutioning', 'Director', 'Solutioning', 'Director', 'Singapore', 'solutioningdirector@test.com', 1, 1, 'password'),
(5, 'Engineering', 'Director', 'Engineering', 'Director', 'Singapore', 'engineeringdirector@test.com', 1, 1, 'password'),
(6, 'HR', 'Director', 'HR', 'Director', 'Singapore', 'hrdirector@test.com', 1, 1, 'password'),
(7, 'Finance', 'Director', 'Finance', 'Director', 'Singapore', 'financedirector@test.com', 1, 1, 'password'),
(8, 'IT', 'Director', 'IT', 'Director', 'Singapore', 'itdirector@test.com', 1, 1, 'password'),


-- Sales with 2 managers and 4 staff
(9, 'Sales', 'Manager1', 'Sales', 'Sales Manager', 'Singapore', 'salesmanager1@test.com', 2, 3, 'password'),
(10, 'Sales', 'Manager2', 'Sales', 'Sales Manager', 'Singapore', 'salesmanager2@test.com', 2, 3, 'password'),
(11, 'Sales', 'Staff1', 'Sales', 'Account Manager', 'Singapore', 'salesstaff1@test.com', 9, 2, 'password'),
(12, 'Sales', 'Staff2', 'Sales', 'Account Manager', 'Singapore', 'salesstaff2@test.com', 9, 2, 'password'),
(13, 'Sales', 'Staff3', 'Sales', 'Account Manager', 'Singapore', 'salesstaff3@test.com', 10, 2, 'password'),
(14, 'Sales', 'Staff4', 'Sales', 'Account Manager', 'Singapore', 'salesstaff4@test.com', 10, 2, 'password'),

-- Consultancy with 2 consultants
(15, 'Consultancy', 'Staff1', 'Consultancy', 'Consultant', 'Singapore', 'consultant1@test.com', 3, 2, 'password'),
(16, 'Consultancy', 'Staff2', 'Consultancy', 'Consultant', 'Singapore', 'consultant2@test.com', 3, 2, 'password'),

-- Solutioning with 2 developers and 2 support team staff
(17, 'Solutioning', 'Developer1', 'Solutioning', 'Developer', 'Singapore', 'developer1@test.com', 4, 2, 'password'),
(18, 'Solutioning', 'Developer2', 'Solutioning', 'Developer', 'Singapore', 'developer2@test.com', 4, 2, 'password'),
(19, 'Solutioning', 'Support1', 'Solutioning', 'Support', 'Singapore', 'support1@test.com', 4, 2, 'password'),
(20, 'Solutioning', 'Support2', 'Solutioning', 'Support', 'Singapore', 'support2@test.com', 4, 2, 'password'),

-- Engineering with 2 senior engineers, 2 junior engineers, 2 call centre, 2 operation planning team
(21, 'Engineering', 'SeniorEngineer1', 'Engineering', 'Senior Engineer', 'Singapore', 'se1@test.com', 5, 2, 'password'),
(22, 'Engineering', 'SeniorEngineer2', 'Engineering', 'Senior Engineer', 'Singapore', 'se2@test.com', 5, 2, 'password'),
(23, 'Engineering', 'JuniorEngineer1', 'Engineering', 'Junior Engineer', 'Singapore', 'je1@test.com', 5, 2, 'password'),
(24, 'Engineering', 'JuniorEngineer2', 'Engineering', 'Junior Engineer', 'Singapore', 'je2@test.com', 5, 2, 'password'),
(25, 'Engineering', 'CallCentre1', 'Engineering', 'Call Centre', 'Singapore', 'cc1@test.com', 5, 2, 'password'),
(26, 'Engineering', 'CallCentre2', 'Engineering', 'Call Centre', 'Singapore', 'cc2@test.com', 5, 2, 'password'),
(27, 'Engineering', 'OperationPlanning1', 'Engineering', 'Operation Planning', 'Singapore', 'op1@test.com', 5, 2, 'password'),
(28, 'Engineering', 'OperationPlanning2', 'Engineering', 'Operation Planning', 'Singapore', 'op2@test.com', 5, 2, 'password'),

-- HR with 2 HR Team, 2 LD Team, 2 Admin Team
(29, 'HR', 'HR1', 'HR', 'HR Team', 'Singapore', 'hr1@test.com', 6, 1, 'password'),
(30, 'HR', 'HR2', 'HR', 'HR Team', 'Singapore', 'hr2@test.com', 6, 1, 'password'),
(31, 'HR', 'LD1', 'HR', 'LD Team', 'Singapore', 'ld1@test.com', 6, 2, 'password'),
(32, 'HR', 'LD2', 'HR', 'LD Team', 'Singapore', 'ld2@test.com', 6, 2, 'password'),
(33, 'HR', 'Admin1', 'HR', 'Admin Team', 'Singapore', 'hradmin1@test.com', 6, 2, 'password'),
(34, 'HR', 'Admin2', 'HR', 'Admin Team', 'Singapore', 'hradmin2@test.com', 6, 2, 'password'),

-- Finance with 2 Finance Manager, 5 Finance Executive
(35, 'Finance', 'Manager1', 'Finance', 'Finance Manager', 'Singapore', 'financemanager1@test.com', 7, 3, 'password'),
(36, 'Finance', 'Manager2', 'Finance', 'Finance Manager', 'Singapore', 'financemanager2@test.com', 7, 3, 'password'),
(37, 'Finance', 'Staff1', 'Finance', 'Finance Executive', 'Singapore', 'financestaff1@test.com', 35, 2, 'password'),
(38, 'Finance', 'Staff2', 'Finance', 'Finance Executive', 'Singapore', 'financestaff2@test.com', 35, 2, 'password'),
(39, 'Finance', 'Staff3', 'Finance', 'Finance Executive', 'Singapore', 'financestaff3@test.com', 36, 2, 'password'),
(40, 'Finance', 'Staff4', 'Finance', 'Finance Executive', 'Singapore', 'financestaff4@test.com', 36, 2, 'password'),
(41, 'Finance', 'Staff5', 'Finance', 'Finance Executive', 'Singapore', 'financestaff5@test.com', 36, 2, 'password'),

-- IT with 2 IT Team, 2 IT Support Team
(42, 'IT', 'IT1', 'IT', 'IT Team', 'Singapore', 'it1@test.com', 8, 2, 'password'),
(43, 'IT', 'IT2', 'IT', 'IT Team', 'Singapore', 'it2@test.com', 8, 2, 'password');


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



File: frontend\src\views\CompleteScheduleView.vue
--------------------------------------------------------------------------------
<template>
  <div id="app">
    <Navbar />
    <div class="dashboard-container">
      <div class="main-section">
        <div class="calendar-container">
          <FullCalendar 
            :options="calendarOptions"
          />
          <div v-if="isLoading" class="loading-overlay">
            <div class="spinner"></div>
          </div>
        </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 only rendered for small screens -->
              <button 
                v-if="isSmallScreen" 
                class="close-button" 
                @click="isRightContainerVisible = false"
              >
                ×
              </button>
            </div>
            <div class="tabs">
              <button 
                :class="['tab', activeTab === 'AM' ? 'active' : '']" 
                @click="activeTab = 'AM'"
              >
                AM
              </button>
              <button 
                :class="['tab', activeTab === 'PM' ? 'active' : '']" 
                @click="activeTab = 'PM'"
              >
                PM
              </button>
            </div>
            <div class="tab-content">
              <div v-if="activeTab === 'AM'">
                <div class="summary">
                  <div class="summary-item">
                    <span class="summary-label">In Office:</span>
                    <span class="summary-value">{{ selectedDateDetails.AM.inOffice }} / {{ selectedDateDetails.AM.total }}</span>
                  </div>
                  <div class="summary-item">
                    <span class="summary-label">WFH:</span>
                    <span class="summary-value">{{ selectedDateDetails.AM.wfh }} / {{ selectedDateDetails.AM.total }}</span>
                  </div>
                </div>
                <div class="departments">
                  <div 
                    class="department card" 
                    v-for="department in selectedDateDetails.AM.departments" 
                    :key="department.deptName"
                    @click="toggleDepartment('AM', department.deptName)"
                  >
                    <div class="department-header">
                      <h3>{{ department.deptName }}</h3>
                      <div class="badge-container">
                        <span class="badge badge-office">
                          In Office: {{ department.inOffice }}/{{ department.total }}
                        </span>
                        <span class="badge badge-wfh">
                          WFH: {{ department.wfh }}/{{ department.total }}
                        </span>
                      </div>
                    </div>
                    <div v-if="expandedDepartments['AM-' + department.deptName]" class="teams">
                      <div 
                        class="team card" 
                        v-for="team in department.teams" 
                        :key="team.teamName"
                        @click.stop="toggleTeam('AM', department.deptName, team.teamName)"
                      >
                        <div class="team-header">
                          <h4>{{ team.teamName }}</h4>
                          <div class="badge-container">
                            <span class="badge badge-office">
                              In Office: {{ team.inOffice }}/{{ team.total }}
                            </span>
                            <span class="badge badge-wfh">
                              WFH: {{ team.wfh }}/{{ team.total }}
                            </span>
                          </div>
                        </div>
                        <div class="staff-list" v-if="expandedTeams['AM-' + department.deptName + '-' + team.teamName]">
                          <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>
                      </div>
                    </div>
                  </div>
                </div>
              </div>
              <div v-else-if="activeTab === 'PM'">
                <div class="summary">
                  <div class="summary-item">
                    <span class="summary-label">In Office:</span>
                    <span class="summary-value">{{ selectedDateDetails.PM.inOffice }} / {{ selectedDateDetails.PM.total }}</span>
                  </div>
                  <div class="summary-item">
                    <span class="summary-label">WFH:</span>
                    <span class="summary-value">{{ selectedDateDetails.PM.wfh }} / {{ selectedDateDetails.PM.total }}</span>
                  </div>
                </div>
                <div class="departments">
                  <div 
                    class="department card" 
                    v-for="department in selectedDateDetails.PM.departments" 
                    :key="department.deptName"
                    @click="toggleDepartment('PM', department.deptName)"
                  >
                    <div class="department-header">
                      <h3>{{ department.deptName }}</h3>
                      <div class="badge-container">
                        <span class="badge badge-office">
                          In Office: {{ department.inOffice }}/{{ department.total }}
                        </span>
                        <span class="badge badge-wfh">
                          WFH: {{ department.wfh }}/{{ department.total }}
                        </span>
                      </div>
                    </div>
                    <div v-if="expandedDepartments['PM-' + department.deptName]" class="teams">
                      <div 
                        class="team card" 
                        v-for="team in department.teams" 
                        :key="team.teamName"
                        @click.stop="toggleTeam('PM', department.deptName, team.teamName)"
                      >
                        <div class="team-header">
                          <h4>{{ team.teamName }}</h4>
                          <div class="badge-container">
                            <span class="badge badge-office">
                              In Office: {{ team.inOffice }}/{{ team.total }}
                            </span>
                            <span class="badge badge-wfh">
                              WFH: {{ team.wfh }}/{{ team.total }}
                            </span>
                          </div>
                        </div>
                        <div class="staff-list" v-if="expandedTeams['PM-' + department.deptName + '-' + team.teamName]">
                          <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>
                      </div>
                    </div>
                  </div>
                </div>
              </div>
            </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 isLoading = ref(false);
const activeTab = ref('AM');
const expandedDepartments = ref({});
const expandedTeams = ref({});

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

onMounted(() => {
  const storedUser = localStorage.getItem('user');
  if (storedUser) {
    user.value = JSON.parse(storedUser);
    if (user.value.role === 1) {
      initiateChunkedSummaryLoading();
    }
  }
  window.addEventListener('resize', handleResize);
});

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

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

const computeMaxDate = computed(() => {
  const date = new Date();
  date.setMonth(date.getMonth() + 3);
  date.setDate(date.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 fetchCompanyScheduleSummary(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/company-schedule-summary`, {
      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 company schedule summary:', error);
  }
}

function getDateStr(dateObj) {
  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(() => {
  let smallScreen = isSmallScreen.value;

  return {
    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';
        container.style.backgroundColor = arg.event.backgroundColor;

        if (arg.event.backgroundColor === '#FFD93D') {
          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%';

        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';

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

        badgesWrapper.appendChild(officeBadge);

        if (!smallScreen) {
          const wfhBadge = document.createElement('span');
          wfhBadge.classList.add('badge', 'badge-wfh-calendar');
          wfhBadge.textContent = `WFH: ${totalStaff - officeCount}`;
          badgesWrapper.appendChild(wfhBadge);
        }

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

        container.appendChild(labelContainer);

        return { domNodes: [container] };
      }
      return null;
    },
    eventDidMount: function(info) {
      const bgColor = info.event.backgroundColor.toUpperCase();
      const yellowHex = '#FFD93D';

      if (bgColor === yellowHex) {
        info.el.style.color = '#000000';

        const elements = info.el.querySelectorAll('*');
        elements.forEach(el => {
          el.style.color = 'inherit';
        });
      } else {
        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: '19: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;
      activeTab.value = 'AM';
      if (user.value.role === 1) {
        await displayDateDetails(info.dateStr);
      }
    }
  }
}

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

async function handleDatesSet(info) {
  if (info.view.type === 'timeGridDay') {
    const dateStr = getDateStr(info.start);
    selectedDate.value = dateStr;
    activeTab.value = 'AM';
    if (user.value.role === 1) {
      const detailData = await fetchCompanyScheduleDetail(dateStr);
      if (detailData) {
        processDetailData(detailData);
        if (!isSmallScreen.value) {
          isRightContainerVisible.value = true;
        }
      } else {
        alert('Error fetching details for the selected date.');
      }
    }
  }
}

async function displayDateDetails(dateStr) {
  const detailData = await fetchCompanyScheduleDetail(dateStr);
  if (detailData) {
    processDetailData(detailData);
    expandedDepartments.value = {}; // Reset expanded departments
    expandedTeams.value = {}; // Reset expanded teams
    isRightContainerVisible.value = true;
  } else {
    alert('Error fetching details for the selected date.');
  }
}

function processDetailData(detailData) {
  let groupedData = {
    AM: {
      inOffice: 0,
      wfh: 0,
      total: 0,
      departments: {}
    },
    PM: {
      inOffice: 0,
      wfh: 0,
      total: 0,
      departments: {}
    }
  };

  const uniqueStaffIds = new Set();

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

    if (!uniqueStaffIds.has(staff.staff_id)) {
      uniqueStaffIds.add(staff.staff_id);

      ['AM', 'PM'].forEach(slot => {
        if (!groupedData[slot].departments[dept]) {
          groupedData[slot].departments[dept] = { deptName: dept, inOffice: 0, wfh: 0, total: 0, teams: {} };
        }
        if (!groupedData[slot].departments[dept].teams[position]) {
          groupedData[slot].departments[dept].teams[position] = { teamName: position, inOffice: 0, wfh: 0, total: 0, staff: [] };
        }

        groupedData[slot].departments[dept].total += 1;
        groupedData[slot].total += 1;
        groupedData[slot].departments[dept].teams[position].total += 1;

        let statusKey = slot === 'AM' ? 'status_am' : 'status_pm';
        let staffStatus = staff[statusKey];

        if (staffStatus === 'OFFICE') {
          groupedData[slot].departments[dept].inOffice += 1;
          groupedData[slot].inOffice += 1;
          groupedData[slot].departments[dept].teams[position].inOffice += 1;
        } else {
          groupedData[slot].departments[dept].wfh += 1;
          groupedData[slot].wfh += 1;
          groupedData[slot].departments[dept].teams[position].wfh += 1;
        }

        groupedData[slot].departments[dept].teams[position].staff.push({
          staff_id: staff.staff_id,
          name: staff.name,
          status: staffStatus
        });
      });
    }
  });

  // Convert the departments object to an array
  Object.keys(groupedData.AM.departments).forEach(deptKey => {
    const dept = groupedData.AM.departments[deptKey];
    dept.teams = Object.values(dept.teams);
  });
  Object.keys(groupedData.PM.departments).forEach(deptKey => {
    const dept = groupedData.PM.departments[deptKey];
    dept.teams = Object.values(dept.teams);
  });

  groupedData.AM.departments = Object.values(groupedData.AM.departments);
  groupedData.PM.departments = Object.values(groupedData.PM.departments);

  selectedDateDetails.value = groupedData;
}

async function fetchCompanyScheduleDetail(dateStr) {
  try {
    const response = await axios.get(`${import.meta.env.VITE_API_URL}/api/company-schedule-detail/${dateStr}`);
    return response.data;
  } catch (error) {
    console.error('Error fetching company schedule detail:', error);
    return null;
  }
}

async function initiateChunkedSummaryLoading() {
  isLoading.value = true;
  const today = new Date();

  const ranges = [];

  const currentMonthStart = new Date(today.getFullYear(), today.getMonth(), 1);
  const currentMonthEnd = new Date(today.getFullYear(), today.getMonth() + 1, 0);
  ranges.push({ start: new Date(currentMonthStart), end: new Date(currentMonthEnd) });

  for (let i = 1; i <= 2; i++) {
    const start = new Date(today.getFullYear(), today.getMonth() - i, 1);
    const end = new Date(today.getFullYear(), today.getMonth() - i + 1, 0);
    ranges.push({ start, end });
  }

  for (let i = 1; i <= 3; i++) {
    const start = new Date(today.getFullYear(), today.getMonth() + i, 1);
    const end = new Date(today.getFullYear(), today.getMonth() + i + 1, 0);
    ranges.push({ start, end });
  }

  for (let i = 0; i < ranges.length; i++) {
    if (i === 0) {
      await fetchCompanyScheduleSummary(ranges[i].start, ranges[i].end);
    } else {
      setTimeout(async () => {
        await fetchCompanyScheduleSummary(ranges[i].start, ranges[i].end);
      }, i * 1000);
    }
  }
  isLoading.value = false;
}

function toggleDepartment(timeOfDay, deptName) {
  const key = `${timeOfDay}-${deptName}`;
  expandedDepartments.value[key] = !expandedDepartments.value[key];
}

function toggleTeam(timeOfDay, deptName, teamName) {
  const key = `${timeOfDay}-${deptName}-${teamName}`;
  expandedTeams.value[key] = !expandedTeams.value[key];
}
</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;
  position: relative;
}

.right-container {
  width: 30%;
  padding: 1.5rem;
  background-color: #f4f6f8;
  border-left: 1px solid #dfe3e8;
  overflow-y: auto;
  flex: none;
  min-width: 300px;
  height: 100%;
  position: relative;
  transition: transform 0.3s ease-in-out;
}

.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: 1.5rem;
}

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

.close-button {
  background: none;
  border: none;
  font-size: 1.75rem;
  cursor: pointer;
  color: #888;
}

.tabs {
  display: flex;
  margin-bottom: 1rem;
}

.tab {
  flex: 1;
  padding: 0.75rem 1rem;
  background-color: #e0e7ff;
  border: none;
  cursor: pointer;
  font-weight: 600;
  color: #141b4d;
  transition: background-color 0.3s ease;
}

.tab:not(:last-child) {
  border-right: 1px solid #c7d2fe;
}

.tab.active {
  background-color: #141b4d;
  color: #ffffff;
}

.tab:hover {
  background-color: #c7d2fe;
}

.tab-content {
  flex: 1;
}

.summary {
  display: flex;
  justify-content: space-between;
  margin-bottom: 1rem;
}

.summary-item {
  display: flex;
  align-items: center;
}

.summary-label {
  font-weight: 600;
  margin-right: 0.5rem;
  color: #2e3a59;
}

.summary-value {
  font-weight: 500;
  color: #141b4d;
}

.departments {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.department {
  background-color: #ffffff;
  border: 1px solid #dfe3e8;
  border-radius: 8px;
  padding: 1rem;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
  cursor: pointer;
}

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

.department-header h3 {
  margin: 0;
  font-size: 1rem;
  color: #2e3a59;
}

.team {
  background-color: #f9fafb;
  border: 1px solid #dfe3e8;
  border-radius: 8px;
  padding: 0.75rem;
  margin-top: 0.5rem;
  cursor: pointer;
}

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

.team-header h4 {
  margin: 0;
  font-size: 1rem;
  color: #2e3a59;
}

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

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

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

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

.staff-list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

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

.status-indicator {
  display: inline-block;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  margin-right: 0.75rem;
}

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

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

.staff-name {
  font-weight: 500;
  color: #2e3a59;
  font-size: 0.8rem;
}

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

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

.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;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background-color: rgba(255, 255, 255, 0.95);
    transform: translateX(100%);
    transition: transform 0.3s ease-in-out;
    z-index: 1000;
    overflow-y: auto;
    padding: 1.5rem;
  }

  .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;
    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;
  }

  .tabs {
    flex-direction: row;
    justify-content: space-between;
  }
}

/* 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;
}

/* Loading Overlay Styles */
.loading-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(255, 255, 255, 0.8);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 10;
}

.spinner {
  border: 8px solid #f3f3f3; /* Light grey */
  border-top: 8px solid #3498db; /* Blue */
  border-radius: 50%;
  width: 60px;
  height: 60px;
  animation: spin 2s linear infinite;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

/* Indicate that cards are clickable */
.department.card, .team.card {
  transition: background-color 0.2s ease;
}

.department.card:hover, .team.card:hover {
  background-color: #f0f4ff;
}
</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 v-if="isLoading" class="loading-overlay">
            <div class="spinner"></div>
          </div>
        </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 only rendered for small screens -->
              <button 
                v-if="isSmallScreen" 
                class="close-button" 
                @click="isRightContainerVisible = false"
              >
                ×
              </button>
            </div>
            <div class="tabs">
              <button 
                :class="['tab', activeTab === 'AM' ? 'active' : '']" 
                @click="activeTab = 'AM'"
              >
                AM
              </button>
              <button 
                :class="['tab', activeTab === 'PM' ? 'active' : '']" 
                @click="activeTab = 'PM'"
              >
                PM
              </button>
            </div>
            <div class="tab-content">
              <div v-if="activeTab === 'AM'">
                <div class="summary">
                  <div class="summary-item">
                    <span class="summary-label">In Office:</span>
                    <span class="summary-value">{{ selectedDateDetails.AM.inOffice }} / {{ selectedDateDetails.AM.total }}</span>
                  </div>
                  <div class="summary-item">
                    <span class="summary-label">WFH:</span>
                    <span class="summary-value">{{ selectedDateDetails.AM.wfh }} / {{ selectedDateDetails.AM.total }}</span>
                  </div>
                </div>
                <div class="teams">
                  <div 
                    class="team card" 
                    v-for="team in selectedDateDetails.AM.teams" 
                    :key="team.teamName"
                    @click="toggleTeam('AM', team.teamName)"
                  >
                    <div class="team-header">
                      <h3>{{ team.teamName }}</h3>
                      <div class="badge-container">
                        <span class="badge badge-office">
                          In Office: {{ team.inOffice }}/{{ team.total }}
                        </span>
                        <span class="badge badge-wfh">
                          WFH: {{ team.wfh }}/{{ team.total }}
                        </span>
                      </div>
                    </div>
                    <div class="staff-list" v-if="expandedTeams['AM-' + team.teamName]">
                      <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>
                  </div>
                </div>
              </div>
              <div v-else-if="activeTab === 'PM'">
                <div class="summary">
                  <div class="summary-item">
                    <span class="summary-label">In Office:</span>
                    <span class="summary-value">{{ selectedDateDetails.PM.inOffice }} / {{ selectedDateDetails.PM.total }}</span>
                  </div>
                  <div class="summary-item">
                    <span class="summary-label">WFH:</span>
                    <span class="summary-value">{{ selectedDateDetails.PM.wfh }} / {{ selectedDateDetails.PM.total }}</span>
                  </div>
                </div>
                <div class="teams">
                  <div 
                    class="team card" 
                    v-for="team in selectedDateDetails.PM.teams" 
                    :key="team.teamName"
                    @click="toggleTeam('PM', team.teamName)"
                  >
                    <div class="team-header">
                      <h3>{{ team.teamName }}</h3>
                      <div class="badge-container">
                        <span class="badge badge-office">
                          In Office: {{ team.inOffice }}/{{ team.total }}
                        </span>
                        <span class="badge badge-wfh">
                          WFH: {{ team.wfh }}/{{ team.total }}
                        </span>
                      </div>
                    </div>
                    <div class="staff-list" v-if="expandedTeams['PM-' + team.teamName]">
                      <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>
                  </div>
                </div>
              </div>
            </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';

// Reactive references
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 isLoading = ref(false); // Loading indicator for summary loading
const activeTab = ref('AM'); // Active tab state
const expandedTeams = ref({}); // Tracks expanded state of team cards

// Handle window resize to update isSmallScreen
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 || user.value.role===1) {
      initiateChunkedSummaryLoading();
    }
  }
  window.addEventListener('resize', handleResize);
});

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

// Compute minimum and maximum selectable dates
const computeMinDate = computed(() => {
  const date = new Date();
  date.setMonth(date.getMonth() - 2);
  return date;
});

const computeMaxDate = computed(() => {
  const date = new Date();
  date.setMonth(date.getMonth() + 3);
  return date;
});

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

// Fetch summary data for a given date range
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);
  }
}

// Format date to YYYY-MM-DD
function getDateStr(dateObj) {
  const year = dateObj.getFullYear();
  const month = ('0' + (dateObj.getMonth() + 1)).slice(-2);
  const day = ('0' + dateObj.getDate()).slice(-2);
  return `${year}-${month}-${day}`;
}

// Calendar options
const calendarOptions = computed(() => {
  let smallScreen = isSmallScreen.value;

  return {
    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}`;

        badgesWrapper.appendChild(officeBadge);

        // Conditionally add WFH badge if not small screen
        if (!smallScreen) {
          const wfhBadge = document.createElement('span');
          wfhBadge.classList.add('badge', 'badge-wfh-calendar');
          wfhBadge.textContent = `WFH: ${totalStaff - officeCount}`;
          badgesWrapper.appendChild(wfhBadge);
        }

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

        container.appendChild(labelContainer);

        return { domNodes: [container] };
      }
      return null;
    },
    eventDidMount: function(info) {
      // Adjust 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';

        // Ensure child elements inherit the color
        const elements = info.el.querySelectorAll('*');
        elements.forEach(el => {
          el.style.color = 'inherit';
        });
      } else {
        // Default text color
        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: '19:00:00',
    businessHours: {
      startTime: '09:00',
      endTime: '18:00',
      daysOfWeek: [1, 2, 3, 4, 5],
    },
  };
});

// Handle date click on the calendar
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;
      activeTab.value = 'AM'; // Reset to AM tab on new selection
      if (user.value.role === 3) {
        await displayDateDetails(info.dateStr);
      }
    }
  }
}

// Handle event click on the calendar
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;
      activeTab.value = 'AM'; // Reset to AM tab on new selection
      await displayDateDetails(dateStr);
    }
  }
}

// Handle when dates are set in the calendar view
async function handleDatesSet(info) {
  if (info.view.type === 'timeGridDay') {
    const dateStr = getDateStr(info.start);
    selectedDate.value = dateStr;
    activeTab.value = 'AM'; // Reset to AM tab on dates set
    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;
        expandedTeams.value = {}; // Reset expanded teams

        // 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.');
      }
    }
  }
}

// Display date details when a date is selected
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;
    expandedTeams.value = {}; // Reset expanded teams

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

// Fetch manager schedule details for a specific 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;
  }
}

// Initiate loading of summary data in chunks
async function initiateChunkedSummaryLoading() {
  isLoading.value = true;
  const today = new Date();

  // Define the date ranges for current month and surrounding months
  const ranges = [];

  // Current month
  const currentMonthStart = new Date(today.getFullYear(), today.getMonth(), 1);
  const currentMonthEnd = new Date(today.getFullYear(), today.getMonth() + 1, 0);
  ranges.push({ start: new Date(currentMonthStart), end: new Date(currentMonthEnd) });


  // Months before current month (2 months)
  for (let i = 1; i <= 2; i++) {
    const start = new Date(today.getFullYear(), today.getMonth() - i, 1);
    const end = new Date(today.getFullYear(), today.getMonth() - i + 1, 0);
    ranges.push({ start, end });
  }

  // Months after current month (3 months)
  for (let i = 1; i <= 3; i++) {
    const start = new Date(today.getFullYear(), today.getMonth() + i, 1);
    const end = new Date(today.getFullYear(), today.getMonth() + i + 1, 0);
    ranges.push({ start, end });
  }

  // Function to load each range sequentially
  for (let i = 0; i < ranges.length; i++) {
    // For the first range (current month), load immediately
    // For others, load in the background with a slight delay
    if (i === 0) {
      await fetchManagerScheduleSummary(ranges[i].start, ranges[i].end);
    } else {
      setTimeout(async () => {
        await fetchManagerScheduleSummary(ranges[i].start, ranges[i].end);
      }, i * 1000); // 1-second interval between requests
    }
  }

  isLoading.value = false;
}

// Toggle the expansion state of a team card
function toggleTeam(timeOfDay, teamName) {
  const key = `${timeOfDay}-${teamName}`;
  expandedTeams.value[key] = !expandedTeams.value[key];
}
</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;
  position: relative;
}

.right-container {
  width: 30%;
  padding: 1.5rem;
  background-color: #f4f6f8;
  border-left: 1px solid #dfe3e8;
  overflow-y: auto;
  flex: none;
  min-width: 300px;
  height: 100%;
  position: relative;
  transition: transform 0.3s ease-in-out;
}

.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: 1.5rem;
}

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

.close-button {
  background: none;
  border: none;
  font-size: 1.75rem;
  cursor: pointer;
  color: #888;
}

.tabs {
  display: flex;
  margin-bottom: 1rem;
}

.tab {
  flex: 1;
  padding: 0.75rem 1rem;
  background-color: #e0e7ff;
  border: none;
  cursor: pointer;
  font-weight: 600;
  color: #141b4d;
  transition: background-color 0.3s ease;
}

.tab:not(:last-child) {
  border-right: 1px solid #c7d2fe;
}

.tab.active {
  background-color: #141b4d;
  color: #ffffff;
}

.tab:hover {
  background-color: #c7d2fe;
}

.tab-content {
  flex: 1;
}

.summary {
  display: flex;
  justify-content: space-between;
  margin-bottom: 1rem;
}

.summary-item {
  display: flex;
  align-items: center;
}

.summary-label {
  font-weight: 600;
  margin-right: 0.5rem;
  color: #2e3a59;
}

.summary-value {
  font-weight: 500;
  color: #141b4d;
}

.teams {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.team {
  background-color: #ffffff;
  border: 1px solid #dfe3e8;
  border-radius: 8px;
  padding: 1rem;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
  cursor: pointer; /* Indicate that the card is clickable */
}

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

.team-header h3 {
  margin: 0;
  font-size: 1rem;
  color: #2e3a59;
}

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

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

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

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

.staff-list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.staff-card {
  display: flex;
  align-items: center;
  border: 1px solid #dfe3e8;
  border-radius: 6px;
  padding: 0.5rem 0.75rem;
  background-color: #f9fafb;
}

.status-indicator {
  display: inline-block;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  margin-right: 0.75rem;
}

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

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

.staff-name {
  font-weight: 500;
  color: #2e3a59;
  font-size: 0.8rem;
}

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

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

.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: 1.5rem;
  }

  .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;
  }

  .tabs {
    flex-direction: row;
    justify-content: space-between;
  }
}

/* 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;
}

/* Loading Overlay Styles */
.loading-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(255, 255, 255, 0.8);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 10;
}

.spinner {
  border: 8px solid #f3f3f3; /* Light grey */
  border-top: 8px solid #3498db; /* Blue */
  border-radius: 50%;
  width: 60px;
  height: 60px;
  animation: spin 2s linear infinite;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

/* Indicate that team cards are clickable */
.team.card {
  transition: background-color 0.2s ease;
}

.team.card:hover {
  background-color: #f0f4ff;
}
</style>