"use server"; import { revalidatePath } from "next/cache"; import type { ActionState } from "@/lib/work-orders/action-state"; import { moduleGuard } from "@/server/action-guard"; import { ctxFromGuard } from "@/server/services/context"; import { approveTimeEntries, approveTimeEntry, rejectTimeEntry } from "@/server/services/field/time-entries"; import { ok, str, toErrorState } from "./_form"; /** * L12 back office: approve / reject manual time entries and correction proposals (`time:approve`). * Thin adapters — permission, team scope, "never own entries", audit and events live in * services/field/time-entries.ts. */ const guard = moduleGuard("work_orders"); function revalidate(workOrderId?: string) { revalidatePath("/work-orders/time-approvals"); if (workOrderId) revalidatePath(`/work-orders/${workOrderId}`); } export async function approveTimeEntryAction(_prev: ActionState, fd: FormData): Promise { const id = str(fd, "timeEntryId") ?? ""; let tenantId: string | undefined; let actorId: string | undefined; try { const g = await guard("time:approve"); tenantId = g.session.user.tenantId; actorId = g.session.user.id; await approveTimeEntry(ctxFromGuard(g), id); } catch (err) { return toErrorState(err, { tenantId, actorId, entity: "time_entry", entityId: id }); } revalidate(str(fd, "workOrderId")); return ok(); } export async function rejectTimeEntryAction(_prev: ActionState, fd: FormData): Promise { const id = str(fd, "timeEntryId") ?? ""; let tenantId: string | undefined; let actorId: string | undefined; try { const g = await guard("time:approve"); tenantId = g.session.user.tenantId; actorId = g.session.user.id; await rejectTimeEntry(ctxFromGuard(g), id, str(fd, "reason") ?? ""); } catch (err) { return toErrorState(err, { tenantId, actorId, entity: "time_entry", entityId: id }); } revalidate(str(fd, "workOrderId")); return ok(); } /** Bulk approval of the selected entries (checkboxes `ids`); entries that cannot be approved stay open. */ export async function approveTimeEntriesAction(_prev: ActionState, fd: FormData): Promise { const ids = fd.getAll("ids").filter((v): v is string => typeof v === "string" && v.length > 0); let tenantId: string | undefined; let actorId: string | undefined; try { const g = await guard("time:approve"); tenantId = g.session.user.tenantId; actorId = g.session.user.id; const results = await approveTimeEntries(ctxFromGuard(g), ids); const failed = results.filter((r) => !r.ok).length; revalidate(); if (ids.length === 0 || failed === results.length) return { status: "error", code: "invalid", message: "time_bulk_none", at: Date.now() }; return ok(); } catch (err) { return toErrorState(err, { tenantId, actorId, entity: "time_entry" }); } }