123 lines
3.8 KiB
TypeScript
123 lines
3.8 KiB
TypeScript
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 | undefined> =
|
|
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<AppointmentWithRelations<TInclude>> {
|
|
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<TInclude>;
|
|
}
|
|
|
|
// 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<TInclude>;
|
|
} catch (error) {
|
|
console.error("[safeFindManyAppointments] Error fetching relations:", error);
|
|
// Return appointments without relations if relation fetch fails
|
|
return validAppointments as AppointmentWithRelations<TInclude>;
|
|
}
|
|
} catch (error) {
|
|
console.error("[safeFindManyAppointments] Error:", error);
|
|
// Return empty array on error instead of crashing
|
|
return [] as unknown as AppointmentWithRelations<TInclude>;
|
|
}
|
|
}
|
|
|