/opt/mawid/apps/api/src/summary
Edit: /opt/mawid/apps/api/src/summary/summary.service.ts (1780B)
import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
export interface WeeklySummary {
from: string;
to: string;
appointments: number;
cancellations: number;
noShows: number;
confirmedAfterReminder: number; // "no-shows prevented"
refilledSlots: number;
}
@Injectable()
export class SummaryService {
constructor(private readonly prisma: PrismaService) {}
/** Numbers for the 7 days ending at `now` (PROJECT_PLAN Phase 6 item 5). */
async computeWeekly(clinicId: string, now: Date = new Date()): Promise
{
const from = new Date(now.getTime() - 7 * 24 * 3600_000);
const inWeek = { clinicId, startsAt: { gte: from, lt: now } };
const [appointments, cancellations, noShows, confirmedAfterReminder, refilledSlots] =
await Promise.all([
this.prisma.appointment.count({
where: { ...inWeek, status: { notIn: ['cancelled'] } },
}),
this.prisma.appointment.count({ where: { ...inWeek, status: 'cancelled' } }),
this.prisma.appointment.count({ where: { ...inWeek, status: 'no_show' } }),
// Prevented no-show = reached confirmed/completed after a reminder was sent.
this.prisma.appointment.count({
where: {
...inWeek,
status: { in: ['confirmed', 'completed'] },
reminderJobs: { some: { status: 'sent' } },
},
}),
this.prisma.auditLog.count({
where: { clinicId, action: 'waitlist:refilled', createdAt: { gte: from, lt: now } },
}),
]);
return {
from: from.toISOString(),
to: now.toISOString(),
appointments,
cancellations,
noShows,
confirmedAfterReminder,
refilledSlots,
};
}
}