import { Router } from "@oak/oak"; import { db } from "../config/database.ts"; import { authenticateToken, authorize, getCurrentUser } from "../middleware/auth.ts"; import { sanitizeInput } from "../middleware/security.ts"; import type { WorkAllocation, CreateWorkAllocationRequest, ContractorRate } from "../types/index.ts"; const router = new Router(); // Get all work allocations router.get("/", authenticateToken, async (ctx) => { try { const currentUser = getCurrentUser(ctx); const params = ctx.request.url.searchParams; const employeeId = params.get("employeeId"); const status = params.get("status"); const departmentId = params.get("departmentId"); let query = ` SELECT wa.*, e.name as employee_name, e.username as employee_username, s.name as supervisor_name, c.name as contractor_name, sd.name as sub_department_name, d.name as department_name FROM work_allocations wa JOIN users e ON wa.employee_id = e.id JOIN users s ON wa.supervisor_id = s.id JOIN users c ON wa.contractor_id = c.id LEFT JOIN sub_departments sd ON wa.sub_department_id = sd.id LEFT JOIN departments d ON e.department_id = d.id WHERE 1=1 `; const queryParams: unknown[] = []; // Role-based filtering if (currentUser.role === "Supervisor") { query += " AND wa.supervisor_id = ?"; queryParams.push(currentUser.id); } else if (currentUser.role === "Employee") { query += " AND wa.employee_id = ?"; queryParams.push(currentUser.id); } else if (currentUser.role === "Contractor") { query += " AND wa.contractor_id = ?"; queryParams.push(currentUser.id); } if (employeeId) { query += " AND wa.employee_id = ?"; queryParams.push(employeeId); } if (status) { query += " AND wa.status = ?"; queryParams.push(status); } if (departmentId) { query += " AND e.department_id = ?"; queryParams.push(departmentId); } query += " ORDER BY wa.assigned_date DESC, wa.created_at DESC"; const allocations = await db.query(query, queryParams); ctx.response.body = allocations; } catch (error) { console.error("Get work allocations error:", error); ctx.response.status = 500; ctx.response.body = { error: "Internal server error" }; } }); // Get work allocation by ID router.get("/:id", authenticateToken, async (ctx) => { try { const allocationId = ctx.params.id; const allocations = await db.query( `SELECT wa.*, e.name as employee_name, e.username as employee_username, s.name as supervisor_name, c.name as contractor_name, sd.name as sub_department_name, d.name as department_name FROM work_allocations wa JOIN users e ON wa.employee_id = e.id JOIN users s ON wa.supervisor_id = s.id JOIN users c ON wa.contractor_id = c.id LEFT JOIN sub_departments sd ON wa.sub_department_id = sd.id LEFT JOIN departments d ON e.department_id = d.id WHERE wa.id = ?`, [allocationId] ); if (allocations.length === 0) { ctx.response.status = 404; ctx.response.body = { error: "Work allocation not found" }; return; } ctx.response.body = allocations[0]; } catch (error) { console.error("Get work allocation error:", error); ctx.response.status = 500; ctx.response.body = { error: "Internal server error" }; } }); // Create work allocation (Supervisor or SuperAdmin) router.post("/", authenticateToken, authorize("Supervisor", "SuperAdmin"), async (ctx) => { try { const currentUser = getCurrentUser(ctx); const body = await ctx.request.body.json() as CreateWorkAllocationRequest; const { employeeId, contractorId, subDepartmentId, activity, description, assignedDate, rate, units, totalAmount, departmentId } = body; if (!employeeId || !contractorId || !assignedDate) { ctx.response.status = 400; ctx.response.body = { error: "Missing required fields" }; return; } // Verify employee exists let employeeQuery = "SELECT * FROM users WHERE id = ?"; const employeeParams: unknown[] = [employeeId]; if (currentUser.role === "Supervisor") { employeeQuery += " AND department_id = ?"; employeeParams.push(currentUser.departmentId); } const employees = await db.query<{ id: number }[]>(employeeQuery, employeeParams); if (employees.length === 0) { ctx.response.status = 403; ctx.response.body = { error: "Employee not found or not in your department" }; return; } // Use provided rate or get contractor's current rate let finalRate = rate; if (!finalRate) { const rates = await db.query( "SELECT rate FROM contractor_rates WHERE contractor_id = ? ORDER BY effective_date DESC LIMIT 1", [contractorId] ); finalRate = rates.length > 0 ? rates[0].rate : null; } const sanitizedActivity = activity ? sanitizeInput(activity) : null; const sanitizedDescription = description ? sanitizeInput(description) : null; const result = await db.execute( `INSERT INTO work_allocations (employee_id, supervisor_id, contractor_id, sub_department_id, activity, description, assigned_date, rate, units, total_amount) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [employeeId, currentUser.id, contractorId, subDepartmentId || null, sanitizedActivity, sanitizedDescription, assignedDate, finalRate, units || null, totalAmount || null] ); const newAllocation = await db.query( `SELECT wa.*, e.name as employee_name, e.username as employee_username, s.name as supervisor_name, c.name as contractor_name, sd.name as sub_department_name, d.name as department_name FROM work_allocations wa JOIN users e ON wa.employee_id = e.id JOIN users s ON wa.supervisor_id = s.id JOIN users c ON wa.contractor_id = c.id LEFT JOIN sub_departments sd ON wa.sub_department_id = sd.id LEFT JOIN departments d ON e.department_id = d.id WHERE wa.id = ?`, [result.insertId] ); ctx.response.status = 201; ctx.response.body = newAllocation[0]; } catch (error) { console.error("Create work allocation error:", error); ctx.response.status = 500; ctx.response.body = { error: "Internal server error" }; } }); // Update work allocation status (Supervisor or SuperAdmin) router.put("/:id/status", authenticateToken, authorize("Supervisor", "SuperAdmin"), async (ctx) => { try { const currentUser = getCurrentUser(ctx); const allocationId = ctx.params.id; const body = await ctx.request.body.json() as { status: string; completionDate?: string }; const { status, completionDate } = body; if (!status) { ctx.response.status = 400; ctx.response.body = { error: "Status required" }; return; } // Verify allocation exists and user has access let query = "SELECT * FROM work_allocations WHERE id = ?"; const params: unknown[] = [allocationId]; if (currentUser.role === "Supervisor") { query += " AND supervisor_id = ?"; params.push(currentUser.id); } const allocations = await db.query(query, params); if (allocations.length === 0) { ctx.response.status = 403; ctx.response.body = { error: "Work allocation not found or access denied" }; return; } await db.execute( "UPDATE work_allocations SET status = ?, completion_date = ? WHERE id = ?", [status, completionDate || null, allocationId] ); const updatedAllocation = await db.query( `SELECT wa.*, e.name as employee_name, e.username as employee_username, s.name as supervisor_name, c.name as contractor_name, sd.name as sub_department_name, d.name as department_name FROM work_allocations wa JOIN users e ON wa.employee_id = e.id JOIN users s ON wa.supervisor_id = s.id JOIN users c ON wa.contractor_id = c.id LEFT JOIN sub_departments sd ON wa.sub_department_id = sd.id LEFT JOIN departments d ON e.department_id = d.id WHERE wa.id = ?`, [allocationId] ); ctx.response.body = updatedAllocation[0]; } catch (error) { console.error("Update work allocation error:", error); ctx.response.status = 500; ctx.response.body = { error: "Internal server error" }; } }); // Delete work allocation (Supervisor or SuperAdmin) router.delete("/:id", authenticateToken, authorize("Supervisor", "SuperAdmin"), async (ctx) => { try { const currentUser = getCurrentUser(ctx); const allocationId = ctx.params.id; // Verify allocation exists and user has access let query = "SELECT * FROM work_allocations WHERE id = ?"; const params: unknown[] = [allocationId]; if (currentUser.role === "Supervisor") { query += " AND supervisor_id = ?"; params.push(currentUser.id); } const allocations = await db.query(query, params); if (allocations.length === 0) { ctx.response.status = 403; ctx.response.body = { error: "Work allocation not found or access denied" }; return; } await db.execute("DELETE FROM work_allocations WHERE id = ?", [allocationId]); ctx.response.body = { message: "Work allocation deleted successfully" }; } catch (error) { console.error("Delete work allocation error:", error); ctx.response.status = 500; ctx.response.body = { error: "Internal server error" }; } }); export default router;