Files
DentalManagement2025/apps/Backend/src/storage/staff-storage.ts

149 lines
4.0 KiB
TypeScript

import { Staff, StaffCreateInput, StaffUpdateInput } from "@repo/db/types";
import { prisma as db } from "@repo/db/client";
export interface IStorage {
getStaff(id: number): Promise<Staff | undefined>;
getAllStaff(): Promise<Staff[]>;
createStaff(staff: StaffCreateInput): Promise<Staff>;
updateStaff(
id: number,
updates: StaffUpdateInput,
): Promise<Staff | undefined>;
deleteStaff(id: number): Promise<boolean>;
countAppointmentsByStaffId(staffId: number): Promise<number>;
countClaimsByStaffId(staffId: number): Promise<number>;
}
export const staffStorage: IStorage = {
// Staff methods
async getStaff(id: number): Promise<Staff | undefined> {
const staff = await db.staff.findUnique({ where: { id } });
return staff ?? undefined;
},
async getAllStaff(): Promise<Staff[]> {
return db.staff.findMany({
orderBy: { displayOrder: "asc" },
});
},
async createStaff(staff: StaffCreateInput): Promise<Staff> {
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<Staff | undefined> {
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<boolean> {
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<number> {
return await db.appointment.count({ where: { staffId } });
},
async countClaimsByStaffId(staffId: number): Promise<number> {
return await db.claim.count({ where: { staffId } });
},
};