app.js

28.11 KB
13/07/2025 05:41
JS
app.js
document.addEventListener('DOMContentLoaded', () => {

  // --- STATE MANAGEMENT ---
  const state = {
    currentView: 'dashboard', // 'dashboard', 'employees', 'recruitment', 'tasks', 'employeeProfile', 'leave'
    currentEmployeeId: null,
    modal: {
      isOpen: false,
      type: null,
    },
    mockData: {
      employees: [
        {id: 1, name: 'สมชาย ใจดี', position: 'Software Developer', department: 'Technology', avatar: 'สจ', startDate: '2022-01-15', email: 'somchai.j@example.com', phone: '081-234-5678'},
        {id: 2, name: 'สมหญิง จริงใจ', position: 'HR Manager', department: 'Human Resources', avatar: 'สจ', startDate: '2021-06-01', email: 'somyhing.j@example.com', phone: '082-345-6789'},
        {id: 3, name: 'มานี มีนา', position: 'Marketing', department: 'Marketing', avatar: 'มน', startDate: '2022-08-20', email: 'manee.m@example.com', phone: '083-456-7890'},
        {id: 4, name: 'ปิติ ยินดี', position: 'Data Analyst', department: 'Technology', avatar: 'ปย', startDate: '2023-02-10', email: 'piti.y@example.com', phone: '084-567-8901'},
        {id: 5, name: 'เอบีซี ดีอีเอฟ', position: 'Sales', department: 'Sales', avatar: 'อด', startDate: '2022-11-05', email: 'abc.d@example.com', phone: '085-678-9012'},
        {id: 6, name: 'สุดา พาเพลิน', position: 'Accountant', department: 'Finance', avatar: 'สพ', startDate: '2020-03-30', email: 'suda.p@example.com', phone: '086-789-0123'},
        {id: 7, name: 'ดวงดี มีโชค', position: 'Software Developer', department: 'Technology', avatar: 'ดม', startDate: '2023-07-19', email: 'duangdee.m@example.com', phone: '087-890-1234'},
      ],
      leaveRequests: [
        {id: 1, employeeId: 1, employeeName: 'สมชาย ใจดี', type: 'ลาป่วย (Sick Leave)', startDate: '2023-11-20', endDate: '2023-11-21', status: 'pending'},
        {id: 2, employeeId: 3, employeeName: 'มานี มีนา', type: 'ลากิจ (Personal Leave)', startDate: '2023-11-25', endDate: '2023-11-25', status: 'pending'},
        {id: 3, employeeId: 4, employeeName: 'ปิติ ยินดี', type: 'ลาพักร้อน (Annual Leave)', startDate: '2023-12-01', endDate: '2023-12-05', status: 'approved'},
        {id: 4, employeeId: 6, employeeName: 'สุดา พาเพลิน', type: 'ลาป่วย (Sick Leave)', startDate: '2023-11-15', endDate: '2023-11-15', status: 'rejected'},
      ],
      candidates: [
        {id: 1, name: 'วีระ กล้าหาญ', position: 'Software Developer', status: 'interview', date: '2023-10-15'},
        {id: 2, name: 'ใจดี มีสุข', position: 'Project Manager', status: 'hired', date: '2023-10-20'},
        {id: 3, name: 'เมตตา กรุณา', position: 'UX/UI Designer', status: 'new', date: '2023-11-01'},
        {id: 4, name: 'กล้าณรงค์ วงศ์ดี', position: 'Data Analyst', status: 'new', date: '2023-11-05'},
        {id: 5, name: 'ทักษิณ ชินวัตร', position: 'Sales', status: 'interview', date: '2023-11-10'},
      ],
      tasks: [
        {id: 1, name: 'สัมภาษณ์งานคุณวีระ', completed: false},
        {id: 2, name: 'เตรียมเอกสารสัญญาจ้างคุณใจดี', completed: true},
      ]
    }
  };

  // --- DOM Elements ---
  const appContent = document.getElementById('app-content');
  const desktopContent = document.getElementById('desktop-content');
  const headerTitle = document.getElementById('header-title');
  const modalContainer = document.createElement('div');
  modalContainer.id = 'modal-container';
  document.body.appendChild(modalContainer);

  // --- NAVIGATION SETUP ---
  function setupNavigation() {
    // Desktop navigation
    const desktopNavItems = document.querySelectorAll('.desktop-nav .nav-item');
    desktopNavItems.forEach(item => {
      item.addEventListener('click', (e) => {
        const viewName = e.target.closest('.nav-item').id.replace('nav-', '');
        navigateTo(viewName);
      });
    });

    // Mobile navigation
    const mobileNavItems = document.querySelectorAll('.mobile-nav .nav-button');
    mobileNavItems.forEach(item => {
      item.addEventListener('click', (e) => {
        const viewName = e.target.closest('.nav-button').id.replace('nav-', '').replace('-mobile', '');
        navigateTo(viewName);
      });
    });
  }

  function navigateTo(viewName) {
    state.currentView = viewName;
    render();
  }

  // Setup navigation on load
  setupNavigation();

  // --- TEMPLATING ENGINE ---
  async function fetchTemplate(path) {
    const response = await fetch(path);
    if (!response.ok) throw new Error(`Template not found: ${path}`);
    return await response.text();
  }

  function interpolate(template, data) {
    return template.replace(/\{\{([^}]+)\}\}/g, (match, key) => data[key.trim()] || '');
  }

  // --- DATA MANIPULATION ---
  function addEmployee(name, position) {
    const newId = state.mockData.employees.length > 0 ? Math.max(...state.mockData.employees.map(e => e.id)) + 1 : 1;
    const avatar = name.split(' ').map(n => n[0]).join('').toUpperCase();
    const department = 'General'; // Default department
    state.mockData.employees.push({
      id: newId,
      name,
      position,
      department,
      avatar,
      startDate: new Date().toISOString().split('T')[0],
      email: `${name.toLowerCase().replace(' ', '.')}@company.com`,
      phone: '0XX-XXX-XXXX'
    });
    showNotification(`เพิ่มพนักงาน ${name} เรียบร้อยแล้ว`, 'success');
  }

  function addCandidate(name, position) {
    const newId = state.mockData.candidates.length > 0 ? Math.max(...state.mockData.candidates.map(c => c.id)) + 1 : 1;
    state.mockData.candidates.push({
      id: newId,
      name,
      position,
      status: 'new',
      date: new Date().toISOString().split('T')[0]
    });
    showNotification(`เพิ่มผู้สมัคร ${name} เรียบร้อยแล้ว`, 'success');
  }

  function addTask(name) {
    const newId = state.mockData.tasks.length > 0 ? Math.max(...state.mockData.tasks.map(t => t.id)) + 1 : 1;
    state.mockData.tasks.push({id: newId, name, completed: false});
    showNotification(`เพิ่มงาน "${name}" เรียบร้อยแล้ว`, 'success');
  }

  function toggleTask(taskId) {
    const task = state.mockData.tasks.find(t => t.id === taskId);
    if (task) {
      task.completed = !task.completed;
      showNotification(
        `งาน "${task.name}" ${task.completed ? 'เสร็จสิ้น' : 'ยกเลิกการเสร็จสิ้น'}แล้ว`,
        task.completed ? 'success' : 'info'
      );
    }
  }

  function approveLeave(leaveId) {
    const request = state.mockData.leaveRequests.find(r => r.id === leaveId);
    if (request) {
      request.status = 'approved';
      showNotification(`อนุมัติการลาของ ${request.employeeName} เรียบร้อยแล้ว`, 'success');
    }
  }

  function rejectLeave(leaveId) {
    const request = state.mockData.leaveRequests.find(r => r.id === leaveId);
    if (request) {
      request.status = 'rejected';
      showNotification(`ปฏิเสธการลาของ ${request.employeeName} เรียบร้อยแล้ว`, 'info');
    }
  }

  function updateModalVisibility() {
    const modalContainer = document.getElementById('modal-container');
    if (!modalContainer) return;
    if (state.modal && state.modal.isOpen) {
      modalContainer.classList.remove('hidden');
      document.body.classList.add('modal-open');
    } else {
      modalContainer.classList.add('hidden');
      document.body.classList.remove('modal-open');
    }
  }

  // --- RENDER FUNCTIONS ---

  async function render() {
    // Update header title
    const titleMap = {
      'dashboard': 'Dashboard',
      'employees': 'พนักงาน',
      'recruitment': 'สรรหาบุคลากร',
      'tasks': 'งาน',
      'leave': 'การลา',
      'employeeProfile': 'ข้อมูลพนักงาน'
    };

    headerTitle.textContent = titleMap[state.currentView] || state.currentView;

    // Update navigation states
    document.querySelectorAll('.nav-button, .nav-item').forEach(btn => btn.classList.remove('active'));

    // Update desktop nav
    const desktopNavItem = document.getElementById(`nav-${state.currentView}`);
    if (desktopNavItem) desktopNavItem.classList.add('active');

    // Update mobile nav
    const mobileNavItem = document.getElementById(`nav-${state.currentView}-mobile`);
    if (mobileNavItem) mobileNavItem.classList.add('active');

    // Clear content areas before rendering new content
    if (appContent) appContent.innerHTML = '';
    if (desktopContent) desktopContent.innerHTML = '';

    // Render content based on current view
    switch (state.currentView) {
      case 'dashboard': await renderDashboard(); break;
      case 'employees': await renderEmployees(); break;
      case 'recruitment': await renderRecruitment(); break;
      case 'tasks': await renderTasks(); break;
      case 'employeeProfile': await renderEmployeeProfile(); break;
      case 'leave': await renderLeave(); break;
    }

    await renderModal();
    updateModalVisibility();
  }

  async function renderDashboard() {
    const {employees, candidates, tasks} = state.mockData;
    const template = await fetchTemplate('templates/dashboard.html');

    // Summary Cards Data
    const summaryItems = [
      {number: employees.length, label: 'พนักงานทั้งหมด'},
      {number: candidates.filter(c => c.status === 'new').length, label: 'ผู้สมัครใหม่'},
      {number: tasks.filter(t => !t.completed).length, label: 'งานที่ต้องทำ'},
      {number: state.mockData.leaveRequests.filter(r => r.status === 'pending').length, label: 'รออนุมัติลา'}
    ];
    const summary_items_html = summaryItems.map(item =>
      `<div class="summary-card">
        <div class="number">${item.number}</div>
        <div class="label">${item.label}</div>
      </div>`
    ).join('');

    // Task List Data
    const urgentTasks = tasks.filter(t => !t.completed);
    const task_list_html = urgentTasks.map(task => `
        <div class="leave-request-item task-item" data-task-id="${task.id}" data-action="toggleTask">
            <div class="leave-info">
                <div class="name">${task.name}</div>
                <div class="leave-type">งานที่รอดำเนินการ</div>
            </div>
            <button class="button-approve" onclick="toggleTask(${task.id}); render();">
                <i class="fas fa-check"></i> เสร็จแล้ว
            </button>
        </div>
    `).join('') || '<div class="text-center text-secondary"><i class="fas fa-check-circle"></i><br>ไม่มีงานค้างที่ต้องทำ</div>';

    // Render the template for both mobile and desktop
    const contentHtml = interpolate(template, {
      summary_items: summary_items_html,
      task_list: task_list_html,
      urgent_count: urgentTasks.length
    });

    if (appContent) appContent.innerHTML = contentHtml;
    if (desktopContent) desktopContent.innerHTML = contentHtml;

    // --- Chart Rendering ---
    // This needs to run AFTER the canvas elements are in the DOM
    renderDepartmentChart();
    renderCandidateChart();
  }

  function renderDepartmentChart() {
    const ctx = document.getElementById('departmentChart')?.getContext('2d');
    if (!ctx) return;

    const departmentCounts = state.mockData.employees.reduce((acc, emp) => {
      acc[emp.department] = (acc[emp.department] || 0) + 1;
      return acc;
    }, {});

    new Chart(ctx, {
      type: 'pie',
      data: {
        labels: Object.keys(departmentCounts),
        datasets: [{
          label: 'พนักงาน',
          data: Object.values(departmentCounts),
          backgroundColor: ['#4a90e2', '#50e3c2', '#f5a623', '#bd10e0', '#9013fe'],
          hoverOffset: 4
        }]
      },
      options: {
        responsive: true,
        maintainAspectRatio: true,
        plugins: {
          legend: {
            position: 'top',
          }
        }
      }
    });
  }

  function renderCandidateChart() {
    const ctx = document.getElementById('candidateChart')?.getContext('2d');
    if (!ctx) return;

    const candidateCountsByMonth = state.mockData.candidates.reduce((acc, can) => {
      const month = new Date(can.date).toLocaleString('default', {month: 'short'});
      acc[month] = (acc[month] || 0) + 1;
      return acc;
    }, {});

    const sortedMonths = Object.keys(candidateCountsByMonth).sort((a, b) =>
      new Date(`1 ${a} 2023`) - new Date(`1 ${b} 2023`));
    const sortedCounts = sortedMonths.map(month => candidateCountsByMonth[month]);

    new Chart(ctx, {
      type: 'bar',
      data: {
        labels: sortedMonths,
        datasets: [{
          label: 'จำนวนผู้สมัคร',
          data: sortedCounts,
          backgroundColor: '#50e3c2',
          borderColor: '#4a90e2',
          borderWidth: 1
        }]
      },
      options: {
        scales: {
          y: {
            beginAtZero: true
          }
        },
        responsive: true,
        maintainAspectRatio: true,
      }
    });
  }

  async function renderEmployees() {
    const template = await fetchTemplate('templates/employees.html');
    const employee_list_html = state.mockData.employees.map(emp => `
        <div class="employee-card" data-action="viewProfile" data-employee-id="${emp.id}">
            <div class="employee-info">
                <div class="employee-avatar">${emp.avatar}</div>
                <div class="employee-details">
                    <h3>${emp.name}</h3>
                    <p><i class="fas fa-briefcase"></i> ${emp.position}</p>
                    <p><i class="fas fa-building"></i> ${emp.department}</p>
                </div>
            </div>
        </div>
    `).join('');

    const contentHtml = interpolate(template, {employee_list: employee_list_html});
    if (appContent) appContent.innerHTML = contentHtml;
    if (desktopContent) desktopContent.innerHTML = contentHtml;
  }

  async function renderEmployeeProfile() {
    const employee = state.mockData.employees.find(e => e.id === state.currentEmployeeId);
    if (!employee) {
      // Handle case where employee not found, maybe redirect to employee list
      state.currentView = 'employees';
      await render();
      return;
    }

    const template = await fetchTemplate('templates/employee-profile.html');
    const contentHtml = interpolate(template, employee);
    if (appContent) appContent.innerHTML = contentHtml;
    if (desktopContent) desktopContent.innerHTML = contentHtml;
  }

  async function renderRecruitment() {
    const template = await fetchTemplate('templates/recruitment.html');

    const getStatusText = (status) => {
      switch (status) {
        case 'new': return 'ใหม่';
        case 'interview': return 'สัมภาษณ์';
        case 'hired': return 'รับเข้าทำงาน';
        default: return status;
      }
    };

    const candidate_list_html = state.mockData.candidates.map(can => `
        <div class="candidate-card">
            <div class="candidate-header">
                <div class="candidate-info">
                    <h3>${can.name}</h3>
                    <p><i class="fas fa-briefcase"></i> ${can.position}</p>
                    <p><i class="fas fa-calendar"></i> วันที่สมัคร: ${can.date}</p>
                </div>
                <span class="candidate-status ${can.status}">${getStatusText(can.status)}</span>
            </div>
        </div>
    `).join('');

    const contentHtml = interpolate(template, {candidate_list: candidate_list_html});
    if (appContent) appContent.innerHTML = contentHtml;
    if (desktopContent) desktopContent.innerHTML = contentHtml;
  }

  async function renderLeave() {
    const template = await fetchTemplate('templates/leave.html');
    const pendingCount = state.mockData.leaveRequests.filter(req => req.status === 'pending').length;

    const getStatusText = (status) => {
      switch (status) {
        case 'pending': return 'รออนุมัติ';
        case 'approved': return 'อนุมัติแล้ว';
        case 'rejected': return 'ปฏิเสธ';
        default: return status;
      }
    };

    const leave_requests_html = state.mockData.leaveRequests.map(req => {
      let actionsOrStatus;
      if (req.status === 'pending') {
        actionsOrStatus = `
          <div class="leave-actions">
            <button class="button-approve" data-action="approveLeave" data-leave-id="${req.id}">
              <i class="fas fa-check"></i> อนุมัติ
            </button>
            <button class="button-reject" data-action="rejectLeave" data-leave-id="${req.id}">
              <i class="fas fa-times"></i> ปฏิเสธ
            </button>
          </div>
        `;
      } else {
        actionsOrStatus = `<span class="leave-status ${req.status}">${getStatusText(req.status)}</span>`;
      }

      return `
        <div class="leave-request-item">
          <div class="leave-info">
            <div class="name">${req.employeeName}</div>
            <div class="leave-type">${req.type}</div>
            <div class="leave-date"><i class="fas fa-calendar"></i> ${req.startDate} ถึง ${req.endDate}</div>
          </div>
          ${actionsOrStatus}
        </div>
      `;
    }).join('');

    const contentHtml = interpolate(template, {
      leave_requests: leave_requests_html,
      pending_count: pendingCount
    });

    if (appContent) appContent.innerHTML = contentHtml;
    if (desktopContent) desktopContent.innerHTML = contentHtml;
  }

  async function renderTasks() {
    const template = await fetchTemplate('templates/tasks.html');
    const task_list_html = state.mockData.tasks.map(task => `
        <div class="task-item ${task.completed ? 'completed' : ''}" data-action="toggleTask" data-task-id="${task.id}">
            <div class="leave-info">
                <div class="name">
                    <i class="far ${task.completed ? 'fa-check-square' : 'fa-square'}" style="margin-right: 0.5rem; color: var(--primary-color);"></i>
                    ${task.name}
                </div>
                <div class="leave-type">${task.completed ? 'เสร็จแล้ว' : 'รอดำเนินการ'}</div>
            </div>
            ${!task.completed ? '<button class="button-primary" onclick="toggleTask(' + task.id + '); render();"><i class="fas fa-check"></i> เสร็จแล้ว</button>' : '<span class="badge badge-success">เสร็จแล้ว</span>'}
        </div>
    `).join('');

    const contentHtml = interpolate(template, {task_list: task_list_html});
    if (appContent) appContent.innerHTML = contentHtml;
    if (desktopContent) desktopContent.innerHTML = contentHtml;
  }

  async function renderModal() {
    if (!state.modal.isOpen) {
      modalContainer.innerHTML = '';
      updateModalVisibility();
      return;
    }

    const template = await fetchTemplate('templates/modal.html');
    let title, formContent;

    switch (state.modal.type) {
      case 'addEmployee':
        title = 'เพิ่มพนักงานใหม่';
        formContent = `
                    <div class="form-group"><label for="employee-name">ชื่อ-สกุล</label><input type="text" id="employee-name" required></div>
                    <div class="form-group"><label for="employee-position">ตำแหน่ง</label><input type="text" id="employee-position" required></div>`;
        break;
      case 'addCandidate':
        title = 'เพิ่มผู้สมัครใหม่';
        formContent = `
                    <div class="form-group"><label for="candidate-name">ชื่อ-สกุล</label><input type="text" id="candidate-name" required></div>
                    <div class="form-group"><label for="candidate-position">ตำแหน่งที่สมัคร</label><input type="text" id="candidate-position" required></div>`;
        break;
      case 'addTask':
        title = 'เพิ่มงานใหม่';
        formContent = `<div class="form-group"><label for="task-name">ชื่องาน</label><input type="text" id="task-name" required></div>`;
        break;
      default: return;
    }

    modalContainer.innerHTML = interpolate(template, {modal_title: title, form_content: formContent});
    updateModalVisibility();
  }

  // --- EVENT LISTENERS ---
  appContent.addEventListener('click', (e) => {
    const target = e.target.closest('[data-action]');
    if (!target) return;
    const {action, modalType, taskId, employeeId, leaveId} = target.dataset;

    switch (action) {
      case 'openModal':
        state.modal.isOpen = true;
        state.modal.type = modalType;
        break;
      case 'toggleTask':
        toggleTask(parseInt(taskId));
        break;
      case 'viewProfile':
        state.currentView = 'employeeProfile';
        state.currentEmployeeId = parseInt(employeeId);
        break;
      case 'backToEmployees':
        state.currentView = 'employees';
        state.currentEmployeeId = null;
        break;
      case 'approveLeave':
        approveLeave(parseInt(leaveId));
        break;
      case 'rejectLeave':
        rejectLeave(parseInt(leaveId));
        break;
    }
    render();
  });

  // Event listener for desktop content
  if (desktopContent) {
    desktopContent.addEventListener('click', (e) => {
      const target = e.target.closest('[data-action]');
      if (!target) return;
      const {action, modalType, taskId, employeeId, leaveId} = target.dataset;

      switch (action) {
        case 'openModal':
          state.modal.isOpen = true;
          state.modal.type = modalType;
          break;
        case 'toggleTask':
          toggleTask(parseInt(taskId));
          break;
        case 'viewProfile':
          state.currentView = 'employeeProfile';
          state.currentEmployeeId = parseInt(employeeId);
          break;
        case 'backToEmployees':
          state.currentView = 'employees';
          state.currentEmployeeId = null;
          break;
        case 'approveLeave':
          approveLeave(parseInt(leaveId));
          break;
        case 'rejectLeave':
          rejectLeave(parseInt(leaveId));
          break;
      }
      render();
    });
  }

  modalContainer.addEventListener('click', (e) => {
    const target = e.target;
    if (target.dataset.action === 'closeModal' || target.classList.contains('modal-overlay')) {
      state.modal.isOpen = false;
      render();
      updateModalVisibility();
    }
  });

  modalContainer.addEventListener('submit', (e) => {
    e.preventDefault();
    if (e.target.id === 'modal-form') {
      switch (state.modal.type) {
        case 'addEmployee':
          addEmployee(document.getElementById('employee-name').value, document.getElementById('employee-position').value);
          break;
        case 'addCandidate':
          addCandidate(document.getElementById('candidate-name').value, document.getElementById('candidate-position').value);
          break;
        case 'addTask':
          addTask(document.getElementById('task-name').value);
          break;
      }
      state.modal.isOpen = false;
      render();
    }
  });

  // Add global functions to window for inline onclick handlers
  window.toggleTask = toggleTask;
  window.approveLeave = approveLeave;
  window.rejectLeave = rejectLeave;

  // Enhanced event delegation for better performance
  document.addEventListener('click', (e) => {
    const action = e.target.closest('[data-action]')?.dataset.action;
    if (!action) return;

    const target = e.target.closest('[data-action]');

    switch (action) {
      case 'openModal':
        state.modal.isOpen = true;
        state.modal.type = target.dataset.modalType;
        render();
        break;
      case 'closeModal':
        state.modal.isOpen = false;
        state.modal.type = null;
        render();
        break;
      case 'viewProfile':
        state.currentEmployeeId = parseInt(target.dataset.employeeId);
        state.currentView = 'employeeProfile';
        render();
        break;
      case 'toggleTask':
        toggleTask(parseInt(target.dataset.taskId));
        render();
        break;
      case 'approveLeave':
        approveLeave(parseInt(target.dataset.leaveId));
        render();
        break;
      case 'rejectLeave':
        rejectLeave(parseInt(target.dataset.leaveId));
        render();
        break;
    }
  });

  // Add form submission handling
  document.addEventListener('submit', (e) => {
    e.preventDefault();
    const form = e.target;

    if (form.id === 'modal-form') {
      handleFormSubmission();
    }
  });

  function handleFormSubmission() {
    const formType = state.modal.type;

    switch (formType) {
      case 'addEmployee':
        const empName = document.getElementById('employee-name').value;
        const empPosition = document.getElementById('employee-position').value;
        if (empName && empPosition) {
          addEmployee(empName, empPosition);
          state.modal.isOpen = false;
          state.modal.type = null;
          render();
        }
        break;
      case 'addCandidate':
        const canName = document.getElementById('candidate-name').value;
        const canPosition = document.getElementById('candidate-position').value;
        if (canName && canPosition) {
          addCandidate(canName, canPosition);
          state.modal.isOpen = false;
          state.modal.type = null;
          render();
        }
        break;
      case 'addTask':
        const taskName = document.getElementById('task-name').value;
        if (taskName) {
          addTask(taskName);
          state.modal.isOpen = false;
          state.modal.type = null;
          render();
        }
        break;
    }
  }

  // Add search functionality
  document.addEventListener('input', (e) => {
    if (e.target.classList.contains('search-input')) {
      handleSearch(e.target.value);
    }
  });

  function handleSearch(query) {
    // Simple search implementation - could be enhanced
    const searchableElements = document.querySelectorAll('.employee-card, .candidate-card, .task-item, .leave-request-item');

    searchableElements.forEach(element => {
      const text = element.textContent.toLowerCase();
      const shouldShow = text.includes(query.toLowerCase());
      element.style.display = shouldShow ? '' : 'none';
    });
  }

  // Add keyboard navigation
  document.addEventListener('keydown', (e) => {
    // ESC key to close modal
    if (e.key === 'Escape' && state.modal.isOpen) {
      state.modal.isOpen = false;
      state.modal.type = null;
      render();
      updateModalVisibility();
    }

    // Number keys for quick navigation (1-5)
    if (e.key >= '1' && e.key <= '5' && !e.target.matches('input, textarea')) {
      const views = ['dashboard', 'employees', 'recruitment', 'tasks', 'leave'];
      const viewIndex = parseInt(e.key) - 1;
      if (views[viewIndex]) {
        navigateTo(views[viewIndex]);
      }
    }
  });

  // Add loading states for better UX
  function showLoading(element) {
    element.classList.add('loading-state');
  }

  function hideLoading(element) {
    element.classList.remove('loading-state');
  }

  // Add notification system
  function showNotification(message, type = 'info') {
    const notification = document.createElement('div');
    notification.className = `notification notification-${type}`;
    notification.innerHTML = `
      <div class="notification-content">
        <i class="fas fa-${type === 'success' ? 'check-circle' : type === 'error' ? 'exclamation-circle' : 'info-circle'}"></i>
        <span>${message}</span>
      </div>
    `;

    document.body.appendChild(notification);

    // Auto remove after 4 seconds
    setTimeout(() => {
      notification.style.animation = 'fadeOut 0.3s ease-out forwards';
      setTimeout(() => notification.remove(), 300);
    }, 4000);
  }

  // --- INITIAL RENDER ---
  render();
});