queue-med/shared/whatsappTemplates.ts

150 lines
5.7 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* WhatsApp message templates with dynamic variable interpolation.
*
* Variables disponibles :
* {{nom}} Nom du patient
* {{ticket}} Numéro de ticket
* {{position}} Position dans la file
* {{attente}} Temps d'attente estimé (en minutes)
* {{cabinet}} Nom du cabinet
*/
// ─── Types ───────────────────────────────────────────────────────────────────
export type TemplateType = "joined" | "soon" | "called" | "withdrawn";
export interface TemplateVariable {
key: string; // e.g. "{{nom}}"
label: string; // e.g. "Nom du patient"
description: string;
}
export interface TemplateContext {
nom: string;
ticket: number | string;
position: number | string;
attente: number | string;
cabinet: string;
}
// ─── Available variables ─────────────────────────────────────────────────────
export const TEMPLATE_VARIABLES: TemplateVariable[] = [
{ key: "{{nom}}", label: "Nom du patient", description: "Prénom ou nom complet du patient" },
{ key: "{{ticket}}", label: "N° de ticket", description: "Numéro de ticket attribué" },
{ key: "{{position}}", label: "Position", description: "Position actuelle dans la file" },
{ key: "{{attente}}", label: "Temps d'attente", description: "Estimation en minutes" },
{ key: "{{cabinet}}", label: "Nom du cabinet", description: "Nom du cabinet médical" },
];
// ─── Default templates (French) ──────────────────────────────────────────────
export const DEFAULT_TEMPLATES: Record<TemplateType, string> = {
joined:
`🏥 *Salle d'attente {{cabinet}}*\n\n` +
`✅ Vous êtes inscrit(e) dans la file d'attente.\n\n` +
`🎫 Numéro de ticket : *{{ticket}}*\n` +
`📍 Position : *{{position}}*\n` +
`⏱️ Attente estimée : *~{{attente}} min*\n\n` +
`Vous recevrez un message quand votre tour approche.\n` +
`_Ne perdez pas votre position restez à proximité._`,
soon:
`🏥 *Salle d'attente {{cabinet}}*\n\n` +
`⚡ *Votre tour approche !*\n\n` +
`🎫 Ticket n° *{{ticket}}*\n` +
`⏳ Environ *{{attente}} minutes* restantes\n\n` +
`Merci de vous rendre en salle d'attente maintenant.`,
called:
`🏥 *Salle d'attente {{cabinet}}*\n\n` +
`🔔 *C'est votre tour !*\n\n` +
`🎫 Ticket n° *{{ticket}}* Veuillez vous présenter au cabinet.\n\n` +
`_Si vous n'êtes pas disponible, vous serez marqué(e) absent(e) après 5 minutes._`,
withdrawn:
`🏥 *Salle d'attente {{cabinet}}*\n\n` +
`✅ Votre désistement a bien été enregistré.\n\n` +
`🎫 Ticket n° *{{ticket}}* annulé.\n\n` +
`Merci et à bientôt !`,
};
// ─── Template labels ─────────────────────────────────────────────────────────
export const TEMPLATE_LABELS: Record<TemplateType, { title: string; description: string; icon: string }> = {
joined: {
title: "Inscription",
description: "Envoyé quand le patient rejoint la file d'attente",
icon: "✅",
},
soon: {
title: "Tour approche",
description: "Envoyé quand le patient est bientôt appelé (position ≤ 2)",
icon: "⚡",
},
called: {
title: "Appel",
description: "Envoyé quand c'est le tour du patient",
icon: "🔔",
},
withdrawn: {
title: "Désistement",
description: "Envoyé quand le patient se désiste",
icon: "👋",
},
};
// ─── Interpolation engine ────────────────────────────────────────────────────
/**
* Replace all {{variable}} placeholders in a template string with actual values.
*/
export function interpolateTemplate(template: string, context: TemplateContext): string {
return template
.replace(/\{\{nom\}\}/g, String(context.nom))
.replace(/\{\{ticket\}\}/g, String(context.ticket))
.replace(/\{\{position\}\}/g, String(context.position))
.replace(/\{\{attente\}\}/g, String(context.attente))
.replace(/\{\{cabinet\}\}/g, String(context.cabinet));
}
/**
* Get the effective template for a given type: custom if set, otherwise default.
*/
export function getEffectiveTemplate(
type: TemplateType,
customTemplates?: Partial<Record<TemplateType, string | null>>
): string {
const custom = customTemplates?.[type];
if (custom && custom.trim().length > 0) return custom;
return DEFAULT_TEMPLATES[type];
}
/**
* Build a final message by resolving the template and interpolating variables.
*/
export function buildMessage(
type: TemplateType,
context: TemplateContext,
customTemplates?: Partial<Record<TemplateType, string | null>>
): string {
const template = getEffectiveTemplate(type, customTemplates);
return interpolateTemplate(template, context);
}
// ─── Preview with sample data ────────────────────────────────────────────────
export const SAMPLE_CONTEXT: TemplateContext = {
nom: "Marie Dupont",
ticket: 42,
position: 3,
attente: 12,
cabinet: "Cabinet Dr Martin",
};
/**
* Generate a preview of a template with sample data.
*/
export function previewTemplate(template: string): string {
return interpolateTemplate(template, SAMPLE_CONTEXT);
}