150 lines
5.7 KiB
TypeScript
150 lines
5.7 KiB
TypeScript
/**
|
||
* 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);
|
||
}
|