/* ═══ CYPHER-HR Bulk Import Module ═══ */ const AddBulkEmployees = { showModal() { showModal('Import Employees', `

Instructions:

  1. Download the template below.
  2. Fill out the employee details. Email, First Name, Last Name, and Password are required.
  3. Upload the completed .csv, .xls, or .xlsx file.
`, ` `); }, downloadTemplate() { const csvContent = "first_name,last_name,email,password,department,position,phone\nJohn,Doe,john.doe@company.com,password123,IT,Developer,1234567890\nJane,Smith,jane.smith@company.com,password123,HR,Manager,0987654321"; const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.setAttribute("href", url); link.setAttribute("download", "CYPHER_HR_Employee_Template.csv"); document.body.appendChild(link); link.click(); document.body.removeChild(link); }, async processFile() { const fileInput = document.getElementById('bulkUploadFile'); const errEl = document.getElementById('bulkError'); const succEl = document.getElementById('bulkSuccess'); const btn = document.getElementById('btnProcessImport'); errEl.style.display = 'none'; succEl.style.display = 'none'; if (!fileInput.files || fileInput.files.length === 0) { errEl.textContent = "Please select a file to upload."; errEl.style.display = 'block'; return; } const file = fileInput.files[0]; // Check if SheetJS is loaded if (typeof XLSX === 'undefined') { errEl.textContent = "Excel parser library not loaded. Please refresh the page."; errEl.style.display = 'block'; return; } btn.disabled = true; btn.innerHTML = `${icon('clock', 18)} Processing...`; try { const data = await file.arrayBuffer(); const workbook = XLSX.read(data); const firstSheetName = workbook.SheetNames[0]; const worksheet = workbook.Sheets[firstSheetName]; const json = XLSX.utils.sheet_to_json(worksheet, { defval: "" }); if (json.length === 0) throw new Error("The uploaded file is empty."); // Normalize keys and validate const payload = json.map((row, index) => { // Find keys case-insensitively const getVal = (keyStr) => { const key = Object.keys(row).find(k => k.toLowerCase().replace(/ /g, '_') === keyStr); return key ? row[key] : ''; }; const emp = { first_name: getVal('first_name'), last_name: getVal('last_name'), email: getVal('email'), password: getVal('password'), department: getVal('department'), position: getVal('position'), phone: getVal('phone') }; if (!emp.first_name || !emp.last_name || !emp.email || !emp.password) { throw new Error(`Row ${index + 2} is missing required fields (First Name, Last Name, Email, or Password).`); } return emp; }); // Send to backend const response = await App.api('/api/employees/bulk', { method: 'POST', body: JSON.stringify({ employees: payload }) }); succEl.textContent = `Successfully imported ${response.inserted} employees. Skipped ${response.skipped} duplicates.`; succEl.style.display = 'block'; // Refresh the grid underneath if Admin is on the employees view if (typeof Admin !== 'undefined' && document.getElementById('employeesGrid')) { const updatedGridHTML = await Admin.employeesContent(); const contentArea = document.getElementById('contentArea'); if (contentArea) contentArea.innerHTML = updatedGridHTML; } setTimeout(closeModal, 3000); } catch (err) { errEl.textContent = err.message || "Failed to process file."; errEl.style.display = 'block'; } finally { btn.disabled = false; btn.textContent = "Process Import"; } } };