"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const express_1 = require("express"); const bcryptjs_1 = __importDefault(require("bcryptjs")); const zod_1 = require("zod"); const prisma_1 = __importDefault(require("../lib/prisma")); const auth_1 = require("../middleware/auth"); const validate_1 = require("../middleware/validate"); const router = (0, express_1.Router)(); const createEmployeeSchema = zod_1.z.object({ firstName: zod_1.z.string().min(1), lastName: zod_1.z.string().min(1), workEmail: zod_1.z.string().email(), departmentId: zod_1.z.string().optional(), positionId: zod_1.z.string().optional(), locationId: zod_1.z.string().optional(), managerId: zod_1.z.string().optional(), employmentType: zod_1.z.enum(['FULL_TIME', 'PART_TIME', 'CONTRACT', 'INTERN']).default('FULL_TIME'), salary: zod_1.z.number().min(0).default(0), phone: zod_1.z.string().optional(), gender: zod_1.z.string().optional(), startDate: zod_1.z.string().optional(), pfApplicable: zod_1.z.boolean().default(true), esiApplicable: zod_1.z.boolean().default(true), }); // GET /employees router.get('/', auth_1.requireAuth, async (req, res) => { try { const { search, departmentId, status, page = '1', limit = '20' } = req.query; const skip = (parseInt(page) - 1) * parseInt(limit); const where = { companyId: req.user.companyId, }; if (search) { where.OR = [ { firstName: { contains: search, mode: 'insensitive' } }, { lastName: { contains: search, mode: 'insensitive' } }, { workEmail: { contains: search, mode: 'insensitive' } }, { employeeCode: { contains: search, mode: 'insensitive' } }, ]; } if (departmentId) where.departmentId = departmentId; if (status) where.status = status; const [employees, total] = await Promise.all([ prisma_1.default.employee.findMany({ where, skip, take: parseInt(limit), include: { department: { select: { name: true } }, position: { select: { name: true } }, location: { select: { name: true } }, manager: { select: { firstName: true, lastName: true } }, }, orderBy: { createdAt: 'desc' }, }), prisma_1.default.employee.count({ where }), ]); return res.json({ data: employees, total, page: parseInt(page), limit: parseInt(limit), totalPages: Math.ceil(total / parseInt(limit)), }); } catch (err) { console.error(err); return res.status(500).json({ error: 'Internal server error' }); } }); // GET /employees/org-chart router.get('/org-chart', auth_1.requireAuth, async (req, res) => { try { const employees = await prisma_1.default.employee.findMany({ where: { companyId: req.user.companyId, status: { not: 'TERMINATED' } }, select: { id: true, firstName: true, lastName: true, managerId: true, position: { select: { name: true } }, department: { select: { name: true } }, }, }); const buildTree = (employees, parentId = null) => { return employees .filter(e => e.managerId === parentId) .map(e => ({ ...e, children: buildTree(employees, e.id), })); }; const tree = buildTree(employees); return res.json(tree); } catch (err) { return res.status(500).json({ error: 'Internal server error' }); } }); // GET /employees/:id router.get('/:id', auth_1.requireAuth, async (req, res) => { try { const employee = await prisma_1.default.employee.findFirst({ where: { id: req.params.id, companyId: req.user.companyId }, include: { department: true, position: true, location: true, manager: { select: { id: true, firstName: true, lastName: true } }, reports: { select: { id: true, firstName: true, lastName: true, position: { select: { name: true } } } }, leaveBalances: { include: { policy: true } }, }, }); if (!employee) { return res.status(404).json({ error: 'Employee not found' }); } return res.json(employee); } catch (err) { return res.status(500).json({ error: 'Internal server error' }); } }); // POST /employees router.post('/', auth_1.requireAuth, (0, validate_1.validate)(createEmployeeSchema), async (req, res) => { try { const companyId = req.user.companyId; const data = req.body; // Generate employee code const count = await prisma_1.default.employee.count({ where: { companyId } }); const employeeCode = `EMP${String(count + 1).padStart(3, '0')}`; // Create user account const tempPassword = Math.random().toString(36).slice(-8); const passwordHash = await bcryptjs_1.default.hash(tempPassword, 12); const user = await prisma_1.default.user.create({ data: { companyId, email: data.workEmail, passwordHash, role: 'EMPLOYEE', }, }); const employee = await prisma_1.default.employee.create({ data: { userId: user.id, companyId, employeeCode, firstName: data.firstName, lastName: data.lastName, workEmail: data.workEmail, departmentId: data.departmentId || null, positionId: data.positionId || null, locationId: data.locationId || null, managerId: data.managerId || null, employmentType: data.employmentType, salary: data.salary, phone: data.phone || null, gender: data.gender || null, startDate: data.startDate ? new Date(data.startDate) : new Date(), pfApplicable: data.pfApplicable, esiApplicable: data.esiApplicable, }, include: { department: true, position: true, location: true, }, }); // Initialize leave balances for current year const policies = await prisma_1.default.leavePolicy.findMany({ where: { companyId, isActive: true } }); const year = new Date().getFullYear(); if (policies.length > 0) { await prisma_1.default.leaveBalance.createMany({ data: policies.map(p => ({ employeeId: employee.id, policyId: p.id, year, allocated: p.accrualDays * 12, used: 0, balance: p.accrualDays * 12, })), skipDuplicates: true, }); } return res.status(201).json({ ...employee, tempPassword }); } catch (err) { if (err.code === 'P2002') { return res.status(409).json({ error: 'Email already exists' }); } console.error(err); return res.status(500).json({ error: 'Internal server error' }); } }); // PATCH /employees/:id router.patch('/:id', auth_1.requireAuth, async (req, res) => { try { const { id } = req.params; const existing = await prisma_1.default.employee.findFirst({ where: { id, companyId: req.user.companyId }, }); if (!existing) return res.status(404).json({ error: 'Employee not found' }); const employee = await prisma_1.default.employee.update({ where: { id }, data: { ...req.body, updatedAt: new Date(), }, include: { department: true, position: true, location: true, }, }); return res.json(employee); } catch (err) { return res.status(500).json({ error: 'Internal server error' }); } }); // DELETE /employees/:id (soft delete) router.delete('/:id', auth_1.requireAuth, async (req, res) => { try { await prisma_1.default.employee.update({ where: { id: req.params.id }, data: { status: 'TERMINATED' }, }); return res.json({ message: 'Employee terminated' }); } catch (err) { return res.status(500).json({ error: 'Internal server error' }); } }); exports.default = router; //# sourceMappingURL=employees.js.map