import { Body, Controller, Get, Param, Post, Req } from "@nestjs/common"; import { z } from "zod"; import { AttendanceService, type RecordAttendanceInput, } from "./attendance.service.js"; import { Permissions, RequirePermission, } from "../middleware/permission.guard.js"; import type { AuthenticatedRequest } from "../middleware/auth.middleware.js"; import { UnauthorizedError, ValidationError, } from "../shared/errors/application-error.js"; const recordAttendanceSchema = z.object({ scheduleId: z.string().min(1), studentId: z.string().min(1), status: z.enum(["present", "absent", "late", "leave"]), remark: z.string().optional(), schoolId: z.string().min(1), }); @Controller("v1/attendance") export class AttendanceController { constructor(private readonly attendanceService: AttendanceService) {} @Post() @RequirePermission(Permissions.ATTENDANCE_CREATE) async record( @Body() body: unknown, @Req() req: AuthenticatedRequest, ): Promise<{ success: true; data: { id: string } }> { const userId = req.userId; if (!userId) { throw new UnauthorizedError("Missing x-user-id header"); } const parsed = recordAttendanceSchema.safeParse(body); if (!parsed.success) { throw new ValidationError( "Invalid attendance input", parsed.error.flatten(), ); } const input: RecordAttendanceInput = { ...parsed.data, recordedBy: userId, }; const result = await this.attendanceService.recordAttendance(input, userId); return { success: true, data: result }; } @Get(":id") @RequirePermission(Permissions.ATTENDANCE_READ) async findOne(@Param("id") id: string): Promise<{ success: true; data: Awaited>; }> { const data = await this.attendanceService.getAttendance(id); return { success: true, data }; } @Get("student/:studentId") @RequirePermission(Permissions.ATTENDANCE_READ) async listByStudent(@Param("studentId") studentId: string): Promise<{ success: true; data: Awaited>; }> { const data = await this.attendanceService.listByStudent(studentId); return { success: true, data }; } @Get("class/:classId") @RequirePermission(Permissions.ATTENDANCE_READ) async listByClass(@Param("classId") classId: string): Promise<{ success: true; data: Awaited>; }> { const data = await this.attendanceService.listByClass(classId); return { success: true, data }; } }