const { Employee, Vendor, GatePass, RenewRequest, Document, SafetyInduction, SafetyBadge } = require('../models'); const { toValidObjectIdStrings } = require('../utils/objectId'); function latestByEmployee(rows) { const map = new Map(); for (const row of rows) { const key = String(row.employee_id); if (!map.has(key)) map.set(key, row); } return map; } async function vendorDashboard(req, res, next) { try { const vendorId = req.user.vendor_id; const employees = await Employee.find({ vendor_id: vendorId }).sort({ created_at: -1 }).lean(); const employeeIds = employees.map((e) => e._id); const gatePasses = await GatePass.find({ employee_id: { $in: employeeIds } }).sort({ created_at: -1 }).lean(); const gatePassIds = gatePasses.map((g) => g._id); const renewRequests = await RenewRequest.find({ gate_pass_id: { $in: gatePassIds }, status: 'Requested' }, { gate_pass_id: 1 }) .lean(); const documents = await Document.find({ employee_id: { $in: employeeIds } }).lean(); const inductions = await SafetyInduction.find({ employee_id: { $in: employeeIds } }).lean(); const gatePassByEmployee = latestByEmployee(gatePasses); const renewRequestedByGatePass = new Set(renewRequests.map((r) => String(r.gate_pass_id))); const inductionByEmployee = new Map(inductions.map((i) => [String(i.employee_id), i])); const docsByEmployee = new Map(); for (const doc of documents) { const key = String(doc.employee_id); if (!docsByEmployee.has(key)) docsByEmployee.set(key, []); docsByEmployee.get(key).push(doc); } const rows = employees.map((employee) => { const key = String(employee._id); const gp = gatePassByEmployee.get(key); const docs = docsByEmployee.get(key) || []; const induction = inductionByEmployee.get(key); const rejectedDocs = docs .filter((d) => d.hr_status === 'Rejected') .map((d) => `${d.type}${d.hr_remarks ? ` - ${d.hr_remarks}` : ''}`); const approvedTypes = docs.filter((d) => d.verified_by_hr).map((d) => d.type); return { id: key, employee_code: employee.employee_code, name: employee.name, status: employee.status, due_date: employee.due_date, profile_photo_path: employee.profile_photo_path, gate_pass_id: gp ? String(gp._id) : 0, issue_date: gp?.issue_date || null, expiry_date: gp?.expiry_date || null, pass_status: gp?.status || null, can_renew: gp?.expiry_date ? new Date(gp.expiry_date) < new Date() : false, renew_requested: gp ? renewRequestedByGatePass.has(String(gp._id)) : false, total_docs_count: docs.length, pending_docs_count: docs.filter((d) => d.hr_status === 'Pending').length, rejected_docs_count: rejectedDocs.length, rejected_docs_details: rejectedDocs.length ? rejectedDocs.join(' | ') : null, approved_doc_types: approvedTypes.length ? approvedTypes.join('|') : null, induction_id: induction ? String(induction._id) : null, scheduled_test_date: induction?.scheduled_date || null, test_status: induction?.test_status || null, test_date: induction?.test_date || null, failure_remarks: induction?.failure_remarks || null, retest_requested: Boolean(induction?.retest_requested) }; }); return res.json(rows); } catch (error) { return next(error); } } async function hrDashboard(req, res, next) { try { const employees = await Employee.find({ is_active: true }).sort({ created_at: -1 }).lean(); const vendorIds = toValidObjectIdStrings(employees.map((e) => e.vendor_id)); const vendors = vendorIds.length ? await Vendor.find({ _id: { $in: vendorIds }, is_active: true }, { name: 1 }).lean() : []; const vendorMap = new Map(vendors.map((v) => [String(v._id), v])); const filteredEmployees = employees.filter((e) => vendorMap.has(String(e.vendor_id))); const filteredEmployeeIds = filteredEmployees.map((e) => e._id); const documents = await Document.find({ employee_id: { $in: filteredEmployeeIds } }).lean(); const badges = await SafetyBadge.find({ employee_id: { $in: filteredEmployeeIds } }, { employee_id: 1, expiry_date: 1 }).lean(); const gatePasses = await GatePass.find({ employee_id: { $in: filteredEmployeeIds } }).sort({ created_at: -1 }).lean(); const docsByEmployee = new Map(); for (const doc of documents) { const key = String(doc.employee_id); if (!docsByEmployee.has(key)) docsByEmployee.set(key, []); docsByEmployee.get(key).push(doc); } const badgeMap = new Map(badges.map((b) => [String(b.employee_id), b])); const passMap = latestByEmployee(gatePasses); const rows = []; for (const employee of filteredEmployees) { const key = String(employee._id); const vendor = vendorMap.get(String(employee.vendor_id)); const docs = docsByEmployee.get(key) || []; const badge = badgeMap.get(key); const pass = passMap.get(key); if (docs.length === 0) { rows.push({ employee_id: key, employee_code: employee.employee_code, employee_name: employee.name, status: employee.status, profile_photo_path: employee.profile_photo_path, vendor_name: vendor?.name || 'N/A', document_id: null, type: null, file_path: null, verified_by_hr: false, hr_status: null, hr_remarks: null, pvc_validity_date: null, hse_due_date: badge?.expiry_date || null, pass_due_date: pass?.expiry_date || null, pass_status: pass?.status || null }); } else { for (const doc of docs) { rows.push({ employee_id: key, employee_code: employee.employee_code, employee_name: employee.name, status: employee.status, profile_photo_path: employee.profile_photo_path, vendor_name: vendor?.name || 'N/A', document_id: String(doc._id), type: doc.type, file_path: doc.file_path, pvc_validity_date: doc.pvc_validity_date, verified_by_hr: doc.verified_by_hr, hr_status: doc.hr_status, hr_remarks: doc.hr_remarks, hse_due_date: badge?.expiry_date || null, pass_due_date: pass?.expiry_date || null, pass_status: pass?.status || null }); } } } return res.json(rows); } catch (error) { return next(error); } } async function safetyDashboard(req, res, next) { try { const inductions = await SafetyInduction.find({ test_status: 'Pending' }) .sort({ scheduled_date: 1 }) .populate({ path: 'employee_id', model: 'Employee', select: 'name vendor_id is_active status' }) .lean(); const calendar = []; for (const induction of inductions) { const employee = induction.employee_id; if (!employee || !employee.is_active) continue; const vendor = await Vendor.findById(employee.vendor_id, { name: 1, is_active: 1 }).lean(); if (!vendor || !vendor.is_active) continue; const badge = await SafetyBadge.findOne({ employee_id: employee._id }, { badge_no: 1, expiry_date: 1 }).lean(); calendar.push({ id: String(induction._id), employee_id: String(employee._id), employee_code: employee.employee_code, employee_name: employee.name, vendor_name: vendor.name, scheduled_date: induction.scheduled_date, completed_date: induction.completed_date, test_status: induction.test_status, test_date: induction.test_date, failure_remarks: induction.failure_remarks, retest_requested: induction.retest_requested, retest_requested_at: induction.retest_requested_at, badge_no: badge?.badge_no || null, expiry_date: badge?.expiry_date || null }); } const hrApprovedEmployees = await Employee.find({ status: 'HR_Approved', is_active: true }) .sort({ created_at: -1 }) .lean(); const eligible = []; for (const employee of hrApprovedEmployees) { const vendor = await Vendor.findById(employee.vendor_id, { name: 1, is_active: 1 }).lean(); if (!vendor || !vendor.is_active) continue; const induction = await SafetyInduction.findOne({ employee_id: employee._id }, { retest_requested: 1 }).lean(); if (!induction || induction.retest_requested === true) { eligible.push({ id: String(employee._id), employee_code: employee.employee_code, name: employee.name, vendor_name: vendor.name }); } } return res.json({ calendar, eligible }); } catch (error) { return next(error); } } module.exports = { vendorDashboard, hrDashboard, safetyDashboard };