import { NextRequest, NextResponse } from "next/server"; import { prisma } from "@/lib/types/prisma"; import { auth } from "@/lib/auth-session/auth"; import { Resend } from "resend"; import { createElement } from "react"; import DentalInvoice from "@/components/emails/email-bookings"; const resend = new Resend(process.env.RESEND_API_KEY); export async function POST(request: NextRequest) { try { const session = await auth.api.getSession({ headers: request.headers, }); if (!session) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } const body = await request.json(); const { appointmentData } = body; if (!appointmentData) { return NextResponse.json( { error: "Appointment data is required" }, { status: 400 } ); } interface ServiceItem { qty: number; description: string; unitPrice: number; total: number; } const { patientId, personalInfo, appointment, services, specialRequests } = appointmentData as { patientId: string; personalInfo: { firstName: string; lastName: string; email: string; address?: string; city?: string; contactNumber?: string; }; appointment: { dentistId: string; dentistName: string; date: string; time: string; }; services: ServiceItem[]; specialRequests: string; }; // Validate required fields if ( !patientId || !appointment.dentistId || !appointment.date || !appointment.time ) { return NextResponse.json( { error: "Missing required appointment fields" }, { status: 400 } ); } // Check if time slot is already booked const existingAppointment = await prisma.appointment.findFirst({ where: { dentistId: appointment.dentistId, date: new Date(appointment.date), timeSlot: appointment.time, status: { in: ["pending", "confirmed"], }, }, }); if (existingAppointment) { return NextResponse.json( { error: "This time slot is already booked" }, { status: 409 } ); } // Create appointments for each service const createdAppointments = []; for (const service of services) { if (service.qty > 0 && service.description) { // Find the service in database const dbService = await prisma.service.findFirst({ where: { name: service.description }, }); if (dbService) { const newAppointment = await prisma.appointment.create({ data: { patientId, dentistId: appointment.dentistId, serviceId: dbService.id, date: new Date(appointment.date), timeSlot: appointment.time, notes: specialRequests || null, status: "pending", }, include: { patient: true, dentist: true, service: true, }, }); createdAppointments.push(newAppointment); } } } if (createdAppointments.length === 0) { return NextResponse.json( { error: "No valid services selected" }, { status: 400 } ); } // Send confirmation email to patient with professional invoice template // Generate invoice number const invoiceNumber = `INV-${Date.now()}-${Math.random().toString(36).substring(7).toUpperCase()}`; const invoiceDate = new Date().toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric", }); const dueDate = new Date( Date.now() + 30 * 24 * 60 * 60 * 1000 ).toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric", }); // Format appointment date and time const formattedAppointmentDate = new Date( appointment.date ).toLocaleDateString("en-US", { weekday: "long", year: "numeric", month: "long", day: "numeric", }); // Calculate next appointment (6 months from now for regular checkup) const nextApptDate = new Date(appointment.date); nextApptDate.setMonth(nextApptDate.getMonth() + 6); const nextAppointmentDate = nextApptDate.toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric", }); // Calculate total duration (assuming each service takes 60 minutes) const totalDuration = services .filter((s) => s.qty > 0) .reduce((sum, s) => sum + s.qty * 60, 0); // Calculate financial totals const subtotal = services .filter((s) => s.qty > 0) .reduce((sum, s) => sum + s.total, 0); const tax = subtotal * 0.12; // 12% tax const totalDue = subtotal + tax; // Filter services with qty > 0 for email const activeServices = services.filter((s) => s.qty > 0); try { console.log("Attempting to send email to:", personalInfo.email); console.log( "From address:", `${process.env.EMAIL_SENDER_NAME} <${process.env.EMAIL_SENDER_ADDRESS}>` ); const emailResult = await resend.emails.send({ from: `${process.env.EMAIL_SENDER_NAME} <${process.env.EMAIL_SENDER_ADDRESS}>`, to: personalInfo.email, subject: `Appointment Confirmation - Invoice #${invoiceNumber}`, react: createElement(DentalInvoice, { invoiceNumber, invoiceDate, dueDate, patientName: `${personalInfo.firstName} ${personalInfo.lastName}`, patientAddress: personalInfo.address || "N/A", patientCity: personalInfo.city || "N/A", patientPhone: personalInfo.contactNumber || "N/A", patientEmail: personalInfo.email, bookingId: createdAppointments[0]?.id || "PENDING", appointmentDate: formattedAppointmentDate, appointmentTime: appointment.time, doctorName: appointment.dentistName, treatmentRoom: "Room 1", appointmentDuration: `${totalDuration} minutes`, reasonForVisit: specialRequests || services .filter((s) => s.qty > 0) .map((s) => s.description) .join(", "), pdfDownloadUrl: `${process.env.NEXT_PUBLIC_APP_URL}/patient/appointments`, paymentStatus: "Pending Payment", nextAppointmentDate, nextAppointmentTime: appointment.time, nextAppointmentPurpose: "Regular Dental Checkup & Cleaning", services: activeServices, subtotal, tax, totalDue, }), }); console.log("Email sent successfully:", emailResult); } catch (emailError) { console.error("Error sending email:", emailError); console.error( "Email error details:", JSON.stringify(emailError, null, 2) ); // Don't fail the appointment creation if email fails } // Create notification for patient await prisma.notification.create({ data: { userId: patientId, title: "Appointment Booked", message: `Your appointment with Dr. ${appointment.dentistName} has been booked for ${new Date(appointment.date).toLocaleDateString()} at ${appointment.time}`, type: "email", }, }); // Create notification for dentist await prisma.notification.create({ data: { userId: appointment.dentistId, title: "New Appointment", message: `New appointment request from ${personalInfo.firstName} ${personalInfo.lastName} for ${new Date(appointment.date).toLocaleDateString()} at ${appointment.time}`, type: "email", }, }); return NextResponse.json( { success: true, appointments: createdAppointments, message: "Appointment booked successfully! Check your email for confirmation.", }, { status: 201 } ); } catch (error) { console.error("Error booking appointment:", error); return NextResponse.json( { error: "Failed to book appointment" }, { status: 500 } ); } }