Fill out the employee details. Email, First Name, Last Name, and Password are required.
+
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";
+ }
+ }
+};
diff --git a/CYPHER_HR_Employee_Template.csv b/CYPHER_HR_Employee_Template.csv
new file mode 100644
index 0000000..65b7abf
--- /dev/null
+++ b/CYPHER_HR_Employee_Template.csv
@@ -0,0 +1,4 @@
+first_name,last_name,email,password,department,position,phone
+John,Doe,john.doe@company.com,password123,IT,Developer,1234567890
+Jane,Smith,jane.smith@company.com,password123,HR,Manager,0987654321
+Alex,Johnson,alex.j@company.com,password123,Sales,Executive,5551234567
diff --git a/README.md b/README.md
index 4bb2680..2574de8 100644
--- a/README.md
+++ b/README.md
@@ -68,6 +68,7 @@ CYPHER-HR is built as an enterprise-grade Single Page Application (SPA), rivalin
- **Granular Leave Controls:** Create custom leave policies (e.g., Sick, Casual, Maternity) and set exact monthly limits.
- **Direct Allowance Adjustments:** Manually increment or decrement individual employee leave balances when necessary.
- **Automated Monthly Resets:** A built-in backend Cron Job automatically refreshes all enabled leave allowances at midnight on the 1st of every month.
+- **Bulk Employee Import:** Need to onboard 500 people? Upload an entire roster at once via Excel (`.xls`, `.xlsx`) or `.csv`. A pre-formatted `CYPHER_HR_Employee_Template.csv` is included directly in the repository for quick, seamless mass-onboarding. Files are parsed instantly in the browser using the blazing-fast SheetJS library.
@@ -89,6 +90,33 @@ CYPHER-HR is built as an enterprise-grade Single Page Application (SPA), rivalin
- **Custom Date Picker Engine:** Bypasses ugly native browser inputs with a fully custom, interactive calendar dropdown built in Vanilla JS.
+---
+
+## 🛠️ Feature Deep-Dive
+
+We don't just list features; we build them right. Here is exactly how some of the most advanced capabilities of CYPHER-HR work under the hood.
+
+### 📈 Bulk Employee Onboarding (Excel & CSV)
+Manually typing out 100 new hires is a nightmare. CYPHER-HR includes a **Mass Import Engine**.
+1. **The Template:** Find the `CYPHER_HR_Employee_Template.csv` file located directly in the repository root. Open it in Microsoft Excel, Google Sheets, or Apple Numbers.
+2. **The Formatting:** Just fill out `first_name`, `last_name`, `email`, and `password`. The system is smart enough to handle missing optional fields (like department or phone).
+3. **The Engine:** Go to the Admin Dashboard -> Employees -> Click **Import Employees**. Upload your spreadsheet.
+4. **Client-Side Parsing:** Using the blazing-fast `SheetJS` library via CDN, the dashboard parses the entire Excel file directly in your browser. This means zero lag and zero heavy lifting for your backend server!
+5. **Smart Skip:** If you accidentally upload an employee who already exists, the backend intelligently skips the duplicate email without crashing the rest of the batch.
+
+### 🌗 Global Dark Mode
+A true corporate system respects your eyes during late-night shifts.
+- **Not just a theme:** The dark mode toggle actively shifts the entire CSS variable hierarchy (`--bg`, `--surface`, `--text`).
+- **Persistent State:** Your preference is automatically saved to your browser's `localStorage`. Next time you boot up the dashboard, it remembers if you prefer light or dark mode.
+
+### 📄 PDF Reporting Engine
+Instead of just handing you raw data, CYPHER-HR creates beautiful, structured PDF documents.
+- **Powered by jsPDF:** Using `jsPDF` and `jspdf-autotable`, the frontend takes your filtered leave requests and dynamically draws a professional PDF document.
+- **Custom Headers:** The PDF automatically injects the current timestamp, the applied filters, and draws corporate-style tables that you can immediately send to payroll or management.
+
+### 🌍 Local Area Network (LAN) Ready
+CYPHER-HR isn't just stuck on `localhost`. The backend Express server is explicitly bound to `0.0.0.0`, meaning it is instantly accessible to anyone on your local network out-of-the-box. Spin it up on a spare office computer, and your entire HR department can securely connect via the host's local IP address without any complex reverse-proxy setup.
+
---
@@ -145,6 +173,7 @@ Want to modify the code? Here is exactly what every file in the project does:
| **`employee.js`** | The employee portal. Renders personalized stat cards, leave allowance progress bars, and handles the logic for submitting new time-off requests. |
| **`reports.js`** | The reporting UI controller. It manages the complex filtering forms, queries the API with URL parameters, and renders the data tables. |
| **`pdf_reports.js`** | The specialized PDF export engine. It takes JSON data from `reports.js`, configures document layouts, creates custom headers, and utilizes `jspdf-autotable` to draw perfectly aligned tables before initiating a browser download. |
+| **`Add_bulk_employees.js`** | The Excel/CSV parsing engine. Hooks into the Admin portal, manages the file upload modal, parses spreadsheets locally using SheetJS, and shoots JSON payloads to the backend for mass insertion. |
diff --git a/admin.js b/admin.js
index 575b287..38f4902 100644
--- a/admin.js
+++ b/admin.js
@@ -109,7 +109,10 @@ const Admin = {
return `