"use server"; import { auth } from "@/auth"; import { prisma } from "@/lib/db/prisma"; import { Permission } from "@/generated/prisma/enums"; import { assertOwnerPermission, OwnerPermissionDeniedError } from "@/features/owner/authorization"; import { writeAuditLog } from "@/lib/audit/write-audit-log"; import { getActingBusinessContext } from "@/lib/business/get-acting-business-context"; import { createBusinessHoliday, deleteBusinessHoliday, findHolidayConflicts, validateHolidayInput, } from "@/services/business/holiday.service"; import type { HolidayInput } from "@/services/business/holiday.service"; export interface HolidayActionResult { success: boolean; error?: string; /** Appointments already booked inside the range — shown for review, never cancelled automatically. */ conflicts?: { id: string; customerName: string; serviceNames: string; startAt: string; staffName: string; }[]; } /** Holidays change when the whole business can be booked, so they share Business Details' permission. */ async function requireBusinessManager() { const session = await auth(); if (!session?.user) return { staff: null, error: "You must be signed in." } as const; try { await assertOwnerPermission(session.user.id, Permission.MANAGE_BUSINESS_PROFILE); } catch (error) { if (error instanceof OwnerPermissionDeniedError) return { staff: null, error: error.message } as const; throw error; } const context = await getActingBusinessContext(); if (!context) return { staff: null, error: "You must be signed in as staff." } as const; return { staff: { businessId: context.businessId, userId: context.userId }, error: null, } as const; } export async function addHolidayAction( holiday: HolidayInput, acknowledgeConflicts?: boolean, ): Promise { const { staff, error } = await requireBusinessManager(); if (!staff) return { success: false, error: error ?? "Not authorized." }; const validation = validateHolidayInput(holiday); if (!validation.valid) return { success: false, error: validation.issues[0]?.message }; if (!acknowledgeConflicts) { const business = await prisma.business.findUnique({ where: { id: staff.businessId }, select: { timezone: true }, }); if (!business) return { success: false, error: "Business not found." }; const conflicts = await findHolidayConflicts(prisma, { businessId: staff.businessId, businessTimezone: business.timezone, startDate: holiday.startDate, endDate: holiday.endDate, }); if (conflicts.length > 0) { return { success: false, error: "Some appointments are already booked on these dates.", conflicts: conflicts.map((c) => ({ id: c.id, customerName: `${c.customer.firstName} ${c.customer.lastName}`, serviceNames: c.services.map((s) => s.service.name).join(" + "), startAt: c.startAt.toISOString(), staffName: c.staff.user.name ?? "Staff", })), }; } } const result = await createBusinessHoliday(prisma, { businessId: staff.businessId, holiday }); if (!result.success) return { success: false, error: result.validation.issues[0]?.message }; await writeAuditLog(prisma, { actorUserId: staff.userId, action: "BUSINESS_HOLIDAY_CREATED", entityType: "BusinessHoliday", entityId: result.id, metadata: { name: holiday.name.trim(), startDate: holiday.startDate, endDate: holiday.endDate }, }); return { success: true }; } export async function deleteHolidayAction(holidayId: string): Promise { const { staff, error } = await requireBusinessManager(); if (!staff) return { success: false, error: error ?? "Not authorized." }; const deleted = await deleteBusinessHoliday(prisma, { businessId: staff.businessId, holidayId }); if (!deleted) return { success: false, error: "That holiday no longer exists." }; await writeAuditLog(prisma, { actorUserId: staff.userId, action: "BUSINESS_HOLIDAY_DELETED", entityType: "BusinessHoliday", entityId: holidayId, }); return { success: true }; }