import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common"; import { PrismaService } from "../../prisma/prisma.service"; @Injectable() export class CalendarService { constructor(private readonly prisma: PrismaService) {} routingForms(tenantId: string) { return this.prisma.calendarRoutingForm.findMany({ where: { tenantId }, orderBy: { createdAt: "desc" }, include: { event: true, _count: { select: { slots: true, bookings: true } } } }); } createRoutingForm(tenantId: string, payload: any) { return this.prisma.calendarRoutingForm.create({ data: { tenantId, eventId: payload.eventId || null, name: String(payload.name ?? ""), slug: String(payload.slug ?? "").toLowerCase().trim(), description: payload.description ? String(payload.description) : null, durationMinutes: Number(payload.durationMinutes ?? 30), active: payload.active === undefined ? true : Boolean(payload.active) } }); } slots(tenantId: string, routingFormId?: string) { return this.prisma.calendarSlot.findMany({ where: { tenantId, ...(routingFormId ? { routingFormId } : {}) }, orderBy: { startsAt: "asc" }, include: { routingForm: true } }); } async createSlot(tenantId: string, payload: any) { const routingForm = await this.prisma.calendarRoutingForm.findFirst({ where: { tenantId, id: String(payload.routingFormId ?? "") } }); if (!routingForm) throw new NotFoundException("Routing form not found"); const startsAt = new Date(String(payload.startsAt ?? "")); const endsAt = new Date(String(payload.endsAt ?? "")); if (!Number.isFinite(startsAt.getTime()) || !Number.isFinite(endsAt.getTime()) || endsAt <= startsAt) { throw new BadRequestException("Invalid slot time"); } return this.prisma.calendarSlot.create({ data: { tenantId, routingFormId: routingForm.id, startsAt, endsAt, capacity: Number(payload.capacity ?? 1), active: payload.active === undefined ? true : Boolean(payload.active) }, include: { routingForm: true } }); } bookings(tenantId: string) { return this.prisma.booking.findMany({ where: { tenantId }, orderBy: { startsAt: "desc" }, include: { routingForm: true, event: true, attendee: true } }); } async createBooking(tenantId: string, payload: any) { const routingForm = await this.prisma.calendarRoutingForm.findFirst({ where: { tenantId, id: String(payload.routingFormId ?? "") } }); if (!routingForm) throw new NotFoundException("Routing form not found"); const startsAt = new Date(String(payload.startsAt ?? "")); const endsAt = new Date(String(payload.endsAt ?? "")); if (!Number.isFinite(startsAt.getTime()) || !Number.isFinite(endsAt.getTime()) || endsAt <= startsAt) { throw new BadRequestException("Invalid booking time"); } return this.prisma.booking.create({ data: { tenantId, routingFormId: routingForm.id, eventId: payload.eventId || routingForm.eventId || null, attendeeId: payload.attendeeId || null, fullName: String(payload.fullName ?? ""), email: String(payload.email ?? "").toLowerCase().trim(), phone: payload.phone ? String(payload.phone) : null, startsAt, endsAt, status: payload.status ? String(payload.status) : "booked", notes: payload.notes ? String(payload.notes) : null }, include: { routingForm: true, event: true, attendee: true } }); } }