'use server'; import { revalidatePath } from 'next/cache'; import { diffOf, recordAudit } from '@/lib/audit'; import { requireAdmin } from '@/lib/auth/actions'; import { prisma } from '@/lib/db'; import type { CivilDate } from '@/lib/schedule/civil'; import { countDays, findOverlap, validatePeriod } from '@/lib/schedule/vacations'; export type ActionResult = { ok: true } | { ok: false; error: string }; export async function createVacation(input: { startDate: CivilDate; endDate: CivilDate; labelFr: string; }): Promise { const gate = await requireAdmin(); if (!gate.ok) { return { ok: false, error: gate.error }; } const labelFr = input.labelFr.trim(); const problems = validatePeriod({ ...input, labelFr }); if (problems.length > 0) { return { ok: false, error: problems.join(' ') }; } // Two periods covering the same day would both be in effect with no way to // say which; refuse rather than merge silently. const existing = await prisma.vacationPeriod.findMany(); const clash = findOverlap( { startDate: input.startDate, endDate: input.endDate }, existing.map((period) => ({ id: period.id, labelFr: period.labelFr, startDate: asCivil(period.startDate), endDate: asCivil(period.endDate), })), ); if (clash) { return { ok: false, error: `Cette période chevauche « ${clash.labelFr} » (${clash.startDate} au ${clash.endDate}).`, }; } const created = await prisma.vacationPeriod.create({ data: { startDate: asDate(input.startDate), endDate: asDate(input.endDate), labelFr, createdBy: gate.value.email, }, }); await recordAudit({ userEmail: gate.value.email, action: 'vacation.create', entity: 'VacationPeriod', entityId: created.id, diff: diffOf(null, { libellé: labelFr, du: input.startDate, au: input.endDate, jours: countDays(input), }), }); revalidateAll(); return { ok: true }; } export async function deleteVacation(id: string): Promise { const gate = await requireAdmin(); if (!gate.ok) { return { ok: false, error: gate.error }; } const existing = await prisma.vacationPeriod.findUnique({ where: { id } }); if (!existing) { return { ok: false, error: 'Cette période a déjà été supprimée.' }; } await prisma.vacationPeriod.delete({ where: { id } }); await recordAudit({ userEmail: gate.value.email, action: 'vacation.delete', entity: 'VacationPeriod', entityId: id, diff: diffOf( { libellé: existing.labelFr, du: asCivil(existing.startDate), au: asCivil(existing.endDate), }, null, ), }); revalidateAll(); return { ok: true }; } function asCivil(value: Date): CivilDate { return value.toISOString().slice(0, 10); } function asDate(value: CivilDate): Date { return new Date(`${value}T00:00:00.000Z`); } function revalidateAll(): void { revalidatePath('/admin'); revalidatePath('/admin/vacances'); revalidatePath('/admin/exceptions'); }