(Feat): More changes
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
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";
|
||||
import type { Attendance, CheckInOutRequest, User, UpdateAttendanceStatusRequest, AttendanceStatus } from "../types/index.ts";
|
||||
|
||||
const router = new Router();
|
||||
|
||||
@@ -237,6 +237,136 @@ router.post("/check-out", authenticateToken, authorize("Supervisor", "SuperAdmin
|
||||
}
|
||||
});
|
||||
|
||||
// Update attendance status (mark as Absent, HalfDay, Late)
|
||||
router.put("/:id/status", authenticateToken, authorize("Supervisor", "SuperAdmin"), async (ctx) => {
|
||||
try {
|
||||
const attendanceId = ctx.params.id;
|
||||
const body = await ctx.request.body.json() as UpdateAttendanceStatusRequest;
|
||||
const { status, remark } = body;
|
||||
|
||||
// Validate status
|
||||
const validStatuses: AttendanceStatus[] = ["CheckedIn", "CheckedOut", "Absent", "HalfDay", "Late"];
|
||||
if (!validStatuses.includes(status)) {
|
||||
ctx.response.status = 400;
|
||||
ctx.response.body = { error: "Invalid status. Must be one of: CheckedIn, CheckedOut, Absent, HalfDay, Late" };
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if record exists
|
||||
const existing = await db.query<Attendance[]>(
|
||||
"SELECT * FROM attendance WHERE id = ?",
|
||||
[attendanceId]
|
||||
);
|
||||
|
||||
if (existing.length === 0) {
|
||||
ctx.response.status = 404;
|
||||
ctx.response.body = { error: "Attendance record not found" };
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the status
|
||||
await db.execute(
|
||||
"UPDATE attendance SET status = ?, remark = ? WHERE id = ?",
|
||||
[status, remark || null, attendanceId]
|
||||
);
|
||||
|
||||
const updatedRecord = await db.query<Attendance[]>(
|
||||
`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]
|
||||
);
|
||||
|
||||
ctx.response.body = updatedRecord[0];
|
||||
} catch (error) {
|
||||
console.error("Update attendance status error:", error);
|
||||
ctx.response.status = 500;
|
||||
ctx.response.body = { error: "Internal server error" };
|
||||
}
|
||||
});
|
||||
|
||||
// Mark employee as absent (create absent record)
|
||||
router.post("/mark-absent", authenticateToken, authorize("Supervisor", "SuperAdmin"), async (ctx) => {
|
||||
try {
|
||||
const currentUser = getCurrentUser(ctx);
|
||||
const body = await ctx.request.body.json();
|
||||
const { employeeId, workDate, remark } = body;
|
||||
|
||||
if (!employeeId || !workDate) {
|
||||
ctx.response.status = 400;
|
||||
ctx.response.body = { error: "Employee ID and work date required" };
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if record already exists for this date
|
||||
const existing = await db.query<Attendance[]>(
|
||||
"SELECT * FROM attendance WHERE employee_id = ? AND work_date = ?",
|
||||
[employeeId, workDate]
|
||||
);
|
||||
|
||||
if (existing.length > 0) {
|
||||
// Update existing record to Absent
|
||||
await db.execute(
|
||||
"UPDATE attendance SET status = ?, remark = ? WHERE id = ?",
|
||||
["Absent", remark || "Marked absent", existing[0].id]
|
||||
);
|
||||
|
||||
const updatedRecord = await db.query<Attendance[]>(
|
||||
`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 = ?`,
|
||||
[existing[0].id]
|
||||
);
|
||||
|
||||
ctx.response.body = updatedRecord[0];
|
||||
} else {
|
||||
// Create new absent record
|
||||
const result = await db.execute(
|
||||
"INSERT INTO attendance (employee_id, supervisor_id, work_date, status, remark) VALUES (?, ?, ?, ?, ?)",
|
||||
[employeeId, currentUser.id, workDate, "Absent", remark || "Marked absent"]
|
||||
);
|
||||
|
||||
const newRecord = await db.query<Attendance[]>(
|
||||
`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("Mark absent error:", error);
|
||||
ctx.response.status = 500;
|
||||
ctx.response.body = { error: "Internal server error" };
|
||||
}
|
||||
});
|
||||
|
||||
// Get attendance summary
|
||||
router.get("/summary/stats", authenticateToken, async (ctx) => {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user