/opt/mawid/apps/api/src/reminders
Edit: /opt/mawid/apps/api/src/reminders/reminder-sender.service.ts (2133B)
import { Injectable } from '@nestjs/common';
import { t, type Language } from '@mawid/shared';
import type { Appointment, Clinic, Patient, Service } from '@mawid/db';
import { formatLocal } from '../agent/tz';
import { WaSenderService } from '../whatsapp/wa-sender.service';
export type ReminderTone = 'first' | 'final' | 'nudge';
export interface ReminderTarget {
appointment: Appointment;
clinic: Clinic;
patient: Patient;
service: Service;
}
/**
* Builds and sends the reminder template message with โ
Confirm / โ Cancel /
* ๐ Reschedule quick-reply buttons. Template must be pre-approved in Meta as
* "appointment_reminder" โ payload shape is stubbed until real credentials
* exist (PROJECT_PLAN ยง7.6).
*/
@Injectable()
export class ReminderSenderService {
constructor(private readonly sender: WaSenderService) {}
async send(target: ReminderTarget, tone: ReminderTone): Promise
{
const { appointment, clinic, patient, service } = target;
const lang = this.lang(patient.language);
const serviceName =
((service.name as Record)[lang] ?? (service.name as Record).en) ||
'appointment';
const time = formatLocal(clinic.timezone, appointment.startsAt);
const key = tone === 'first' ? 'reminderFirst' : tone === 'nudge' ? 'reminderNudge' : 'reminderFinal';
const bodyText = t(key, lang, { service: serviceName, time });
const components = [
{ type: 'body', parameters: [{ type: 'text', text: serviceName }, { type: 'text', text: time }] },
...['confirm', 'cancel', 'resched'].map((action, index) => ({
type: 'button',
sub_type: 'quick_reply',
index,
parameters: [{ type: 'payload', payload: `${action}:${appointment.id}` }],
})),
];
await this.sender.sendTemplate(
appointment.clinicId,
patient.id,
'appointment_reminder',
lang,
components,
bodyText,
);
}
private lang(language: string): Language {
return (['ar', 'tr', 'en'] as const).includes(language as Language)
? (language as Language)
: 'en';
}
}