- Implemented AbsenceNotifyView for notifying absence with session details. - Created AnalyticsView for displaying group analytics and managing students at risk. - Added ClassAttendanceView for marking attendance in classes. - Defined new schemas for analytics and attendance in shared package.
85 lines
2.4 KiB
TypeScript
85 lines
2.4 KiB
TypeScript
import type { PrismaClient } from '@generated/prisma/client';
|
|
import type { AttendeeDto, ProblemDetails, Result } from '@gruperly/shared';
|
|
import { err, ok } from '@gruperly/shared';
|
|
import { capacityReachedProblem, conflictProblem } from '@/http/problem-builders';
|
|
import { type AttendeeRecord, toAttendeeDto } from '@/modules/attendees/lib/helpers';
|
|
import { type GroupWaitlistEntryRecord } from './helpers';
|
|
|
|
export type PromoteDb = Pick<PrismaClient, 'attendee' | 'groupWaitlistEntry'>;
|
|
|
|
type PromoteGroup = {
|
|
id: string;
|
|
capacity: number | null;
|
|
};
|
|
|
|
export async function findFirstPendingWaitlistEntry(
|
|
db: PromoteDb,
|
|
groupId: string,
|
|
): Promise<GroupWaitlistEntryRecord | null> {
|
|
const record = await db.groupWaitlistEntry.findFirst({
|
|
where: { groupId, status: 'PENDING' },
|
|
orderBy: { createdAt: 'asc' },
|
|
});
|
|
return record as GroupWaitlistEntryRecord | null;
|
|
}
|
|
|
|
export async function promoteWaitlistEntryRecord(
|
|
db: PromoteDb,
|
|
group: PromoteGroup,
|
|
entry: GroupWaitlistEntryRecord,
|
|
): Promise<Result<AttendeeDto, ProblemDetails>> {
|
|
if (group.capacity !== null) {
|
|
const currentCount = await db.attendee.count({
|
|
where: { groupId: group.id, status: 'ACTIVE' },
|
|
});
|
|
if (currentCount >= group.capacity) {
|
|
return err(capacityReachedProblem(group.capacity));
|
|
}
|
|
}
|
|
|
|
const existing = await db.attendee.findFirst({
|
|
where: { groupId: group.id, phone: entry.phone },
|
|
});
|
|
if (existing) {
|
|
return err(
|
|
conflictProblem({
|
|
detail: 'Ya existe un alumno registrado con este número de teléfono en este grupo.',
|
|
code: 'attendee_already_registered',
|
|
}),
|
|
);
|
|
}
|
|
|
|
const attendee = await db.attendee.create({
|
|
data: {
|
|
groupId: group.id,
|
|
fullName: entry.fullName,
|
|
phone: entry.phone,
|
|
email: entry.email,
|
|
notes: entry.notes,
|
|
},
|
|
});
|
|
|
|
return ok(toAttendeeDto(attendee as unknown as AttendeeRecord));
|
|
}
|
|
|
|
export type PromoteFirstResult = {
|
|
attendee: AttendeeDto;
|
|
entry: GroupWaitlistEntryRecord;
|
|
};
|
|
|
|
export async function promoteFirstPendingWaitlistEntry(
|
|
db: PromoteDb,
|
|
group: PromoteGroup,
|
|
): Promise<Result<PromoteFirstResult | null, ProblemDetails>> {
|
|
const entry = await findFirstPendingWaitlistEntry(db, group.id);
|
|
if (!entry) {
|
|
return ok(null);
|
|
}
|
|
|
|
const result = await promoteWaitlistEntryRecord(db, group, entry);
|
|
if (!result.ok) {
|
|
return result;
|
|
}
|
|
|
|
return ok({ attendee: result.value, entry });
|
|
} |