import { Router } from "@oak/oak"; import { db } from "../config/database.ts"; import { authenticateToken, authorize, getCurrentUser } from "../middleware/auth.ts"; import type { Attendance, CheckInOutRequest, User } from "../types/index.ts"; const router = new Router(); // Get all attendance records router.get("/", authenticateToken, async (ctx) => { try { const currentUser = getCurrentUser(ctx); const params = ctx.request.url.searchParams; const employeeId = params.get("employeeId"); const startDate = params.get("startDate"); const endDate = params.get("endDate"); const status = params.get("status"); let query = ` SELECT a.*, e.name as employee_name, e.username as employee_username, s.name as supervisor_name, d.name as department_name, c.name as contractor_name FROM attendance a JOIN users e ON a.employee_id = e.id JOIN users s ON a.supervisor_id = s.id LEFT JOIN departments d ON e.department_id = d.id LEFT JOIN users c ON e.contractor_id = c.id WHERE 1=1 `; const queryParams: unknown[] = []; // Role-based filtering if (currentUser.role === "Supervisor") { query += " AND a.supervisor_id = ?"; queryParams.push(currentUser.id); } else if (currentUser.role === "Employee") { query += " AND a.employee_id = ?"; queryParams.push(currentUser.id); } if (employeeId) { query += " AND a.employee_id = ?"; queryParams.push(employeeId); } if (startDate) { query += " AND a.work_date >= ?"; queryParams.push(startDate); } if (endDate) { query += " AND a.work_date <= ?"; queryParams.push(endDate); } if (status) { query += " AND a.status = ?"; queryParams.push(status); } query += " ORDER BY a.work_date DESC, a.check_in_time DESC"; const records = await db.query(query, queryParams); ctx.response.body = records; } catch (error) { console.error("Get attendance error:", error); ctx.response.status = 500; ctx.response.body = { error: "Internal server error" }; } }); // Get attendance by ID router.get("/:id", authenticateToken, async (ctx) => { try { const attendanceId = ctx.params.id; const records = await db.query( `SELECT a.*, e.name as employee_name, e.username as employee_username, s.name as supervisor_name, d.name as department_name, c.name as contractor_name FROM attendance a JOIN users e ON a.employee_id = e.id JOIN users s ON a.supervisor_id = s.id LEFT JOIN departments d ON e.department_id = d.id LEFT JOIN users c ON e.contractor_id = c.id WHERE a.id = ?`, [attendanceId] ); if (records.length === 0) { ctx.response.status = 404; ctx.response.body = { error: "Attendance record not found" }; return; } ctx.response.body = records[0]; } catch (error) { console.error("Get attendance error:", error); ctx.response.status = 500; ctx.response.body = { error: "Internal server error" }; } }); // Check in employee (Supervisor or SuperAdmin) router.post("/check-in", authenticateToken, authorize("Supervisor", "SuperAdmin"), async (ctx) => { try { const currentUser = getCurrentUser(ctx); const body = await ctx.request.body.json() as CheckInOutRequest; const { employeeId, workDate } = body; if (!employeeId || !workDate) { ctx.response.status = 400; ctx.response.body = { error: "Employee ID and work date required" }; return; } // Verify employee exists let employeeQuery = "SELECT * FROM users WHERE id = ? AND role = ?"; const employeeParams: unknown[] = [employeeId, "Employee"]; if (currentUser.role === "Supervisor") { employeeQuery += " AND department_id = ?"; employeeParams.push(currentUser.departmentId); } const employees = await db.query(employeeQuery, employeeParams); if (employees.length === 0) { ctx.response.status = 403; ctx.response.body = { error: "Employee not found or not in your department" }; return; } // Check if already checked in today const existing = await db.query( "SELECT * FROM attendance WHERE employee_id = ? AND work_date = ? AND status = ?", [employeeId, workDate, "CheckedIn"] ); if (existing.length > 0) { ctx.response.status = 400; ctx.response.body = { error: "Employee already checked in today" }; return; } const checkInTime = new Date().toISOString().slice(0, 19).replace("T", " "); const result = await db.execute( "INSERT INTO attendance (employee_id, supervisor_id, check_in_time, work_date, status) VALUES (?, ?, ?, ?, ?)", [employeeId, currentUser.id, checkInTime, workDate, "CheckedIn"] ); const newRecord = await db.query( `SELECT a.*, e.name as employee_name, e.username as employee_username, s.name as supervisor_name, d.name as department_name, c.name as contractor_name FROM attendance a JOIN users e ON a.employee_id = e.id JOIN users s ON a.supervisor_id = s.id LEFT JOIN departments d ON e.department_id = d.id LEFT JOIN users c ON e.contractor_id = c.id WHERE a.id = ?`, [result.insertId] ); ctx.response.status = 201; ctx.response.body = newRecord[0]; } catch (error) { console.error("Check in error:", error); ctx.response.status = 500; ctx.response.body = { error: "Internal server error" }; } }); // Check out employee (Supervisor or SuperAdmin) router.post("/check-out", authenticateToken, authorize("Supervisor", "SuperAdmin"), async (ctx) => { try { const currentUser = getCurrentUser(ctx); const body = await ctx.request.body.json() as CheckInOutRequest; const { employeeId, workDate } = body; if (!employeeId || !workDate) { ctx.response.status = 400; ctx.response.body = { error: "Employee ID and work date required" }; return; } // Find the check-in record let query = "SELECT * FROM attendance WHERE employee_id = ? AND work_date = ? AND status = ?"; const params: unknown[] = [employeeId, workDate, "CheckedIn"]; if (currentUser.role === "Supervisor") { query += " AND supervisor_id = ?"; params.push(currentUser.id); } const records = await db.query(query, params); if (records.length === 0) { ctx.response.status = 404; ctx.response.body = { error: "No check-in record found for today" }; return; } const checkOutTime = new Date().toISOString().slice(0, 19).replace("T", " "); await db.execute( "UPDATE attendance SET check_out_time = ?, status = ? WHERE id = ?", [checkOutTime, "CheckedOut", records[0].id] ); const updatedRecord = await db.query( `SELECT a.*, e.name as employee_name, e.username as employee_username, s.name as supervisor_name, d.name as department_name, c.name as contractor_name FROM attendance a JOIN users e ON a.employee_id = e.id JOIN users s ON a.supervisor_id = s.id LEFT JOIN departments d ON e.department_id = d.id LEFT JOIN users c ON e.contractor_id = c.id WHERE a.id = ?`, [records[0].id] ); ctx.response.body = updatedRecord[0]; } catch (error) { console.error("Check out error:", error); ctx.response.status = 500; ctx.response.body = { error: "Internal server error" }; } }); // Get attendance summary router.get("/summary/stats", authenticateToken, async (ctx) => { try { const currentUser = getCurrentUser(ctx); const params = ctx.request.url.searchParams; const startDate = params.get("startDate"); const endDate = params.get("endDate"); const departmentId = params.get("departmentId"); let query = ` SELECT COUNT(DISTINCT a.employee_id) as total_employees, COUNT(DISTINCT CASE WHEN a.status = 'CheckedIn' THEN a.employee_id END) as checked_in, COUNT(DISTINCT CASE WHEN a.status = 'CheckedOut' THEN a.employee_id END) as checked_out, d.name as department_name FROM attendance a JOIN users e ON a.employee_id = e.id LEFT JOIN departments d ON e.department_id = d.id WHERE 1=1 `; const queryParams: unknown[] = []; if (currentUser.role === "Supervisor") { query += " AND a.supervisor_id = ?"; queryParams.push(currentUser.id); } if (startDate) { query += " AND a.work_date >= ?"; queryParams.push(startDate); } if (endDate) { query += " AND a.work_date <= ?"; queryParams.push(endDate); } if (departmentId) { query += " AND e.department_id = ?"; queryParams.push(departmentId); } query += " GROUP BY d.id, d.name"; const summary = await db.query(query, queryParams); ctx.response.body = summary; } catch (error) { console.error("Get attendance summary error:", error); ctx.response.status = 500; ctx.response.body = { error: "Internal server error" }; } }); export default router;