import { prisma } from "@/lib/types/prisma"; import type { Prisma } from "@prisma/client"; type AppointmentSelect = { id: string; patientId: string; dentistId: string; serviceId: string; date: Date; timeSlot: string; status: string; notes: string | null; cancelReason: string | null; createdAt: Date; updatedAt: Date; }; type AppointmentWithRelations = TInclude extends Prisma.AppointmentInclude ? Prisma.AppointmentGetPayload<{ include: TInclude }>[] : AppointmentSelect[]; /** * Safely fetch appointments with relations, filtering out orphaned records * This prevents Prisma errors when appointments reference non-existent users */ export async function safeFindManyAppointments< TInclude extends Prisma.AppointmentInclude | undefined = undefined >( args: { where?: Prisma.AppointmentWhereInput; include?: TInclude; orderBy?: Prisma.AppointmentOrderByWithRelationInput | Prisma.AppointmentOrderByWithRelationInput[]; take?: number; skip?: number; } ): Promise> { try { // First, get all appointments without relations const appointments = await prisma.appointment.findMany({ where: args.where, select: { id: true, patientId: true, dentistId: true, serviceId: true, date: true, timeSlot: true, status: true, notes: true, cancelReason: true, createdAt: true, updatedAt: true, }, orderBy: args.orderBy, take: args.take, skip: args.skip, }); // Get all unique patient and dentist IDs const patientIds = [...new Set(appointments.map((apt) => apt.patientId))]; const dentistIds = [...new Set(appointments.map((apt) => apt.dentistId))]; const serviceIds = [...new Set(appointments.map((apt) => apt.serviceId))]; // Verify that all referenced users and services exist const [patients, dentists, services] = await Promise.all([ prisma.user.findMany({ where: { id: { in: patientIds } }, select: { id: true }, }), prisma.user.findMany({ where: { id: { in: dentistIds } }, select: { id: true }, }), prisma.service.findMany({ where: { id: { in: serviceIds } }, select: { id: true }, }), ]); const validPatientIds = new Set(patients.map((p) => p.id)); const validDentistIds = new Set(dentists.map((d) => d.id)); const validServiceIds = new Set(services.map((s) => s.id)); // Filter out appointments with invalid references const validAppointments = appointments.filter( (apt) => validPatientIds.has(apt.patientId) && validDentistIds.has(apt.dentistId) && validServiceIds.has(apt.serviceId) ); // If no include needed, return early if (!args.include || validAppointments.length === 0) { return validAppointments as AppointmentWithRelations; } // Now fetch the full appointments with relations for valid ones only const validAppointmentIds = validAppointments.map((apt) => apt.id); try { const fullAppointments = await prisma.appointment.findMany({ where: { id: { in: validAppointmentIds }, }, include: args.include, orderBy: args.orderBy, }); return fullAppointments as AppointmentWithRelations; } catch (error) { console.error("[safeFindManyAppointments] Error fetching relations:", error); // Return appointments without relations if relation fetch fails return validAppointments as AppointmentWithRelations; } } catch (error) { console.error("[safeFindManyAppointments] Error:", error); // Return empty array on error instead of crashing return [] as unknown as AppointmentWithRelations; } }