"use server"; import { auth } from "@/auth"; import { db } from "@/lib/db"; import { hasPermission } from "@/lib/permissions"; import { CREWING_ENABLED } from "@/lib/feature-flags"; import { AttendanceStatus } from "@prisma/client"; import { z } from "zod"; import { revalidatePath } from "next/cache"; type ActionResult = { ok: true } | { error: string }; const markSchema = z.object({ date: z.string().min(1), status: z.nativeEnum(AttendanceStatus) }); // Bulk-save the dirty cells from the month calendar (Site staff). One upsert per // (assignment, date); a single ATTENDANCE_RECORDED audit row per save. export async function saveAttendance(assignmentId: string, marks: { date: string; status: AttendanceStatus }[]): Promise { if (!CREWING_ENABLED) return { error: "Crewing is not enabled" }; const session = await auth(); if (!session?.user) return { error: "Unauthorized" }; if (!hasPermission(session.user.role, "record_attendance")) return { error: "Unauthorized" }; if (!assignmentId) return { error: "Crew member is required" }; const parsed = z.array(markSchema).max(40).safeParse(marks); if (!parsed.success) return { error: "Invalid attendance data" }; if (parsed.data.length === 0) return { ok: true }; const assignment = await db.crewAssignment.findUnique({ where: { id: assignmentId }, select: { crewMemberId: true } }); if (!assignment) return { error: "Crew assignment not found" }; await db.$transaction( parsed.data.map((m) => db.attendance.upsert({ where: { assignmentId_date: { assignmentId, date: new Date(m.date) } }, update: { status: m.status, recordedById: session.user.id }, create: { assignmentId, date: new Date(m.date), status: m.status, recordedById: session.user.id }, }) ) ); await db.crewAction.create({ data: { actionType: "ATTENDANCE_RECORDED", actorId: session.user.id, crewMemberId: assignment.crewMemberId, metadata: { count: parsed.data.length } }, }); revalidatePath("/crewing/attendance"); return { ok: true }; }