import { Staff, StaffCreateInput, StaffUpdateInput } from "@repo/db/types"; import { prisma as db } from "@repo/db/client"; export interface IStorage { getStaff(id: number): Promise; getAllStaff(): Promise; createStaff(staff: StaffCreateInput): Promise; updateStaff( id: number, updates: StaffUpdateInput, ): Promise; deleteStaff(id: number): Promise; countAppointmentsByStaffId(staffId: number): Promise; countClaimsByStaffId(staffId: number): Promise; } export const staffStorage: IStorage = { // Staff methods async getStaff(id: number): Promise { const staff = await db.staff.findUnique({ where: { id } }); return staff ?? undefined; }, async getAllStaff(): Promise { return db.staff.findMany({ orderBy: { displayOrder: "asc" }, }); }, async createStaff(staff: StaffCreateInput): Promise { const max = await db.staff.aggregate({ where: { userId: staff.userId, displayOrder: { gt: 0 }, }, _max: { displayOrder: true }, }); return db.staff.create({ data: { ...staff, displayOrder: (max._max.displayOrder ?? 0) + 1, }, }); }, async updateStaff( id: number, updates: StaffUpdateInput, ): Promise { return db.$transaction(async (tx: any) => { const staff = await tx.staff.findUnique({ where: { id } }); if (!staff) return undefined; const { userId, displayOrder: oldOrder } = staff; const newOrder = updates.displayOrder; if (newOrder === undefined || newOrder === oldOrder) { return tx.staff.update({ where: { id }, data: updates, }); } const totalStaff = await tx.staff.count({ where: { userId } }); if (newOrder < 1 || newOrder > totalStaff) { throw new Error(`displayOrder must be between 1 and ${totalStaff}`); } const occupyingStaff = await tx.staff.findFirst({ where: { userId, displayOrder: newOrder, }, }); // CASE 1: staff already had a slot → SWAP if (oldOrder && oldOrder > 0 && occupyingStaff) { await tx.staff.update({ where: { id: occupyingStaff.id }, data: { displayOrder: oldOrder }, }); } // CASE 2: first-time assignment (oldOrder = 0) if ((!oldOrder || oldOrder === 0) && occupyingStaff) { // find first free slot const usedOrders = await tx.staff.findMany({ where: { userId, displayOrder: { gt: 0 }, }, select: { displayOrder: true }, orderBy: { displayOrder: "asc" }, }); const usedSet = new Set(usedOrders.map((s: any) => s.displayOrder)); let freeSlot = 1; while (usedSet.has(freeSlot)) freeSlot++; await tx.staff.update({ where: { id: occupyingStaff.id }, data: { displayOrder: freeSlot }, }); } return tx.staff.update({ where: { id }, data: { ...updates, displayOrder: newOrder, }, }); }); }, async deleteStaff(id: number): Promise { return db.$transaction(async (tx: any) => { const staff = await tx.staff.findUnique({ where: { id } }); if (!staff) return false; const { userId, displayOrder } = staff; await tx.staff.delete({ where: { id } }); // Shift left to remove gap await tx.staff.updateMany({ where: { userId, displayOrder: { gt: displayOrder }, }, data: { displayOrder: { decrement: 1 }, }, }); return true; }); }, async countAppointmentsByStaffId(staffId: number): Promise { return await db.appointment.count({ where: { staffId } }); }, async countClaimsByStaffId(staffId: number): Promise { return await db.claim.count({ where: { staffId } }); }, };