Checkpoint: Ajout du système de notifications enrichi comprenant : email de remerciement post-formation automatique, notifications aux formateurs (nouvelle inscription et annulation), alertes de capacité atteinte pour les admins, notifications aux apprenants en liste d'attente quand une place se libère, ajout du champ email pour les formateurs, et scheduler automatique pour les remerciements.
This commit is contained in:
@@ -474,3 +474,370 @@ export async function sendPasswordResetEmail(params: {
|
||||
html: await getEmailTemplate(content, 'reset_password'),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ========================================
|
||||
* SYSTÈME DE NOTIFICATIONS ENRICHI
|
||||
* ========================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* Envoie un email de remerciement post-formation à un apprenant
|
||||
*/
|
||||
export async function sendRemerciementPostFormation(params: {
|
||||
apprenantEmail: string;
|
||||
apprenantNom: string;
|
||||
apprenantPrenom: string;
|
||||
apprenantFonction: string;
|
||||
formationNom: string;
|
||||
sequenceNom: string;
|
||||
formateurNom?: string;
|
||||
lienQuestionnaire?: string;
|
||||
}): Promise<boolean> {
|
||||
const fonctionLabel = params.apprenantFonction === 'directeur' ? 'Directeur' :
|
||||
params.apprenantFonction === 'chef_service' ? 'Chef de service' : '';
|
||||
const salutation = fonctionLabel ? `${fonctionLabel} ${params.apprenantPrenom} ${params.apprenantNom}` : params.apprenantPrenom;
|
||||
|
||||
const content = `
|
||||
<h2>Merci pour votre participation !</h2>
|
||||
<p>Bonjour ${salutation},</p>
|
||||
|
||||
<p>Nous tenons à vous remercier pour votre participation à la formation <strong>${params.formationNom}</strong> (${params.sequenceNom}).</p>
|
||||
|
||||
${params.formateurNom ? `<p>Nous espérons que les sessions animées par <strong>${params.formateurNom}</strong> ont répondu à vos attentes.</p>` : ''}
|
||||
|
||||
<div class="info-box">
|
||||
<h3>Votre avis compte !</h3>
|
||||
<p>Afin d'améliorer continuellement nos formations, nous vous invitons à partager votre retour d'expérience.</p>
|
||||
${params.lienQuestionnaire ? `
|
||||
<div style="text-align: center; margin: 20px 0;">
|
||||
<a href="${params.lienQuestionnaire}" class="button">Donner mon avis</a>
|
||||
</div>
|
||||
` : '<p>Un questionnaire de satisfaction vous sera envoyé prochainement.</p>'}
|
||||
</div>
|
||||
|
||||
<p>Nous vous souhaitons une excellente continuation dans vos fonctions et espérons vous revoir lors de prochaines formations.</p>
|
||||
|
||||
<p>Cordialement,<br/>L'équipe Formation</p>
|
||||
`;
|
||||
|
||||
const variables = {
|
||||
nomApprenant: params.apprenantNom,
|
||||
prenomApprenant: params.apprenantPrenom,
|
||||
nomFormation: params.formationNom,
|
||||
nomSequence: params.sequenceNom,
|
||||
formateur: params.formateurNom || '',
|
||||
};
|
||||
|
||||
return sendEmail({
|
||||
to: params.apprenantEmail,
|
||||
subject: `Merci pour votre participation - ${params.formationNom}`,
|
||||
html: await getEmailTemplate(content, 'remerciement', variables),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie une notification au formateur pour une nouvelle inscription
|
||||
*/
|
||||
export async function sendNotificationFormateurNouvelleInscription(params: {
|
||||
formateurEmail: string;
|
||||
formateurNom: string;
|
||||
apprenantNom: string;
|
||||
apprenantPrenom: string;
|
||||
apprenantFonction: string;
|
||||
apprenantEtablissement?: string;
|
||||
formationNom: string;
|
||||
sequenceNom: string;
|
||||
nbInscrits: number;
|
||||
capaciteMax: number;
|
||||
dates: Array<{ dateDebut: Date; dateFin: Date; ordre: number }>;
|
||||
}): Promise<boolean> {
|
||||
const fonctionLabel = params.apprenantFonction === 'directeur' ? 'Directeur' :
|
||||
params.apprenantFonction === 'chef_service' ? 'Chef de service' : 'Autre';
|
||||
|
||||
const datesHTML = params.dates.map(date => `
|
||||
<li>Date ${date.ordre} : ${date.dateDebut.toLocaleDateString('fr-FR', {
|
||||
weekday: 'long',
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
year: 'numeric'
|
||||
})}</li>
|
||||
`).join('');
|
||||
|
||||
const content = `
|
||||
<h2>🎓 Nouvelle inscription à votre formation</h2>
|
||||
<p>Bonjour ${params.formateurNom},</p>
|
||||
|
||||
<p>Un nouvel apprenant vient de s'inscrire à votre formation.</p>
|
||||
|
||||
<div class="info-box">
|
||||
<h3>Détails de l'inscription</h3>
|
||||
<p><strong>Apprenant :</strong> ${params.apprenantPrenom} ${params.apprenantNom}</p>
|
||||
<p><strong>Fonction :</strong> ${fonctionLabel}</p>
|
||||
${params.apprenantEtablissement ? `<p><strong>Établissement :</strong> ${params.apprenantEtablissement}</p>` : ''}
|
||||
<p><strong>Formation :</strong> ${params.formationNom}</p>
|
||||
<p><strong>Séquence :</strong> ${params.sequenceNom}</p>
|
||||
</div>
|
||||
|
||||
<div class="info-box" style="background-color: #dbeafe; border-left-color: #3b82f6;">
|
||||
<h3>📊 État des inscriptions</h3>
|
||||
<p><strong>${params.nbInscrits} / ${params.capaciteMax}</strong> places occupées</p>
|
||||
<div style="background-color: #e5e7eb; border-radius: 4px; height: 20px; margin: 10px 0;">
|
||||
<div style="background-color: #3b82f6; border-radius: 4px; height: 20px; width: ${Math.min(100, (params.nbInscrits / params.capaciteMax) * 100)}%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4>Dates de la formation :</h4>
|
||||
<ul>${datesHTML}</ul>
|
||||
|
||||
<p>Cordialement,<br/>L'équipe Formation</p>
|
||||
`;
|
||||
|
||||
return sendEmail({
|
||||
to: params.formateurEmail,
|
||||
subject: `Nouvelle inscription - ${params.formationNom} (${params.sequenceNom})`,
|
||||
html: await getEmailTemplate(content, 'notification_formateur'),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie une notification au formateur pour une annulation d'inscription
|
||||
*/
|
||||
export async function sendNotificationFormateurAnnulation(params: {
|
||||
formateurEmail: string;
|
||||
formateurNom: string;
|
||||
apprenantNom: string;
|
||||
apprenantPrenom: string;
|
||||
apprenantFonction: string;
|
||||
formationNom: string;
|
||||
sequenceNom: string;
|
||||
nbInscrits: number;
|
||||
capaciteMax: number;
|
||||
raisonAnnulation?: string;
|
||||
}): Promise<boolean> {
|
||||
const fonctionLabel = params.apprenantFonction === 'directeur' ? 'Directeur' :
|
||||
params.apprenantFonction === 'chef_service' ? 'Chef de service' : 'Autre';
|
||||
|
||||
const content = `
|
||||
<h2>⚠️ Annulation d'inscription</h2>
|
||||
<p>Bonjour ${params.formateurNom},</p>
|
||||
|
||||
<p>Un apprenant a annulé son inscription à votre formation.</p>
|
||||
|
||||
<div class="info-box" style="background-color: #fef3c7; border-left-color: #f59e0b;">
|
||||
<h3>Détails de l'annulation</h3>
|
||||
<p><strong>Apprenant :</strong> ${params.apprenantPrenom} ${params.apprenantNom}</p>
|
||||
<p><strong>Fonction :</strong> ${fonctionLabel}</p>
|
||||
<p><strong>Formation :</strong> ${params.formationNom}</p>
|
||||
<p><strong>Séquence :</strong> ${params.sequenceNom}</p>
|
||||
${params.raisonAnnulation ? `<p><strong>Raison :</strong> ${params.raisonAnnulation}</p>` : ''}
|
||||
</div>
|
||||
|
||||
<div class="info-box" style="background-color: #dbeafe; border-left-color: #3b82f6;">
|
||||
<h3>📊 État des inscriptions après annulation</h3>
|
||||
<p><strong>${params.nbInscrits} / ${params.capaciteMax}</strong> places occupées</p>
|
||||
<p><strong>${params.capaciteMax - params.nbInscrits}</strong> place(s) disponible(s)</p>
|
||||
</div>
|
||||
|
||||
<p>Cordialement,<br/>L'équipe Formation</p>
|
||||
`;
|
||||
|
||||
return sendEmail({
|
||||
to: params.formateurEmail,
|
||||
subject: `Annulation d'inscription - ${params.formationNom} (${params.sequenceNom})`,
|
||||
html: await getEmailTemplate(content, 'notification_formateur'),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie une alerte aux admins quand la capacité maximale est atteinte
|
||||
*/
|
||||
export async function sendAlerteCapaciteAtteinte(params: {
|
||||
adminEmails: string[];
|
||||
formationNom: string;
|
||||
sequenceNom: string;
|
||||
capaciteMax: number;
|
||||
nbInscrits: number;
|
||||
nbListeAttente: number;
|
||||
formateurNom?: string;
|
||||
dates: Array<{ dateDebut: Date; dateFin: Date; ordre: number }>;
|
||||
}): Promise<{ sent: number; failed: number }> {
|
||||
const datesHTML = params.dates.map(date => `
|
||||
<li>${date.dateDebut.toLocaleDateString('fr-FR', {
|
||||
weekday: 'long',
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
year: 'numeric'
|
||||
})}</li>
|
||||
`).join('');
|
||||
|
||||
const content = `
|
||||
<h2>🔔 Alerte : Capacité maximale atteinte</h2>
|
||||
|
||||
<div class="info-box" style="background-color: #fee2e2; border-left-color: #ef4444;">
|
||||
<h3>⚠️ La séquence est complète</h3>
|
||||
<p>La capacité maximale de la séquence <strong>${params.sequenceNom}</strong> a été atteinte.</p>
|
||||
</div>
|
||||
|
||||
<div class="info-box">
|
||||
<h3>Détails de la séquence</h3>
|
||||
<p><strong>Formation :</strong> ${params.formationNom}</p>
|
||||
<p><strong>Séquence :</strong> ${params.sequenceNom}</p>
|
||||
${params.formateurNom ? `<p><strong>Formateur :</strong> ${params.formateurNom}</p>` : ''}
|
||||
<p><strong>Capacité :</strong> ${params.nbInscrits} / ${params.capaciteMax} (100%)</p>
|
||||
${params.nbListeAttente > 0 ? `<p><strong>Liste d'attente :</strong> ${params.nbListeAttente} personne(s)</p>` : ''}
|
||||
</div>
|
||||
|
||||
<h4>Dates prévues :</h4>
|
||||
<ul>${datesHTML}</ul>
|
||||
|
||||
<div class="info-box" style="background-color: #dbeafe; border-left-color: #3b82f6;">
|
||||
<h3>Actions possibles</h3>
|
||||
<ul>
|
||||
<li>Augmenter la capacité maximale de la séquence</li>
|
||||
<li>Créer une nouvelle séquence pour cette formation</li>
|
||||
<li>Contacter les personnes en liste d'attente pour les informer</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<p>Cordialement,<br/>Le système de gestion des formations</p>
|
||||
`;
|
||||
|
||||
let sent = 0;
|
||||
let failed = 0;
|
||||
|
||||
for (const adminEmail of params.adminEmails) {
|
||||
try {
|
||||
await sendEmail({
|
||||
to: adminEmail,
|
||||
subject: `🔔 Capacité atteinte - ${params.formationNom} (${params.sequenceNom})`,
|
||||
html: await getEmailTemplate(content, 'alerte_admin'),
|
||||
});
|
||||
sent++;
|
||||
} catch (error) {
|
||||
console.error(`Erreur envoi alerte à ${adminEmail}:`, error);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
return { sent, failed };
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie une notification aux apprenants en liste d'attente quand une place se libère
|
||||
*/
|
||||
export async function sendNotificationPlaceDisponible(params: {
|
||||
apprenantEmail: string;
|
||||
apprenantNom: string;
|
||||
apprenantPrenom: string;
|
||||
apprenantFonction: string;
|
||||
formationNom: string;
|
||||
sequenceNom: string;
|
||||
positionListeAttente: number;
|
||||
lienConfirmation?: string;
|
||||
delaiReponse?: number; // en heures
|
||||
}): Promise<boolean> {
|
||||
const fonctionLabel = params.apprenantFonction === 'directeur' ? 'Directeur' :
|
||||
params.apprenantFonction === 'chef_service' ? 'Chef de service' : '';
|
||||
const salutation = fonctionLabel ? `${fonctionLabel} ${params.apprenantPrenom} ${params.apprenantNom}` : params.apprenantPrenom;
|
||||
const delai = params.delaiReponse || 48;
|
||||
|
||||
const content = `
|
||||
<h2>🎉 Une place s'est libérée !</h2>
|
||||
<p>Bonjour ${salutation},</p>
|
||||
|
||||
<p>Bonne nouvelle ! Une place vient de se libérer pour la formation à laquelle vous êtes inscrit(e) en liste d'attente.</p>
|
||||
|
||||
<div class="info-box" style="background-color: #d1fae5; border-left-color: #10b981;">
|
||||
<h3>✅ Vous pouvez maintenant confirmer votre inscription</h3>
|
||||
<p><strong>Formation :</strong> ${params.formationNom}</p>
|
||||
<p><strong>Séquence :</strong> ${params.sequenceNom}</p>
|
||||
<p><strong>Votre position :</strong> ${params.positionListeAttente}${params.positionListeAttente === 1 ? 'er' : 'ème'} sur la liste d'attente</p>
|
||||
</div>
|
||||
|
||||
${params.lienConfirmation ? `
|
||||
<div style="text-align: center; margin: 30px 0;">
|
||||
<a href="${params.lienConfirmation}" class="button">Confirmer mon inscription</a>
|
||||
</div>
|
||||
` : '<p>Veuillez contacter le service RH pour confirmer votre inscription.</p>'}
|
||||
|
||||
<div class="info-box" style="background-color: #fef3c7; border-left-color: #f59e0b;">
|
||||
<h3>⏰ Important</h3>
|
||||
<p>Vous avez <strong>${delai} heures</strong> pour confirmer votre inscription.</p>
|
||||
<p>Passé ce délai, la place sera proposée à la personne suivante sur la liste d'attente.</p>
|
||||
</div>
|
||||
|
||||
<p>Cordialement,<br/>L'équipe Formation</p>
|
||||
`;
|
||||
|
||||
const variables = {
|
||||
nomApprenant: params.apprenantNom,
|
||||
prenomApprenant: params.apprenantPrenom,
|
||||
nomFormation: params.formationNom,
|
||||
nomSequence: params.sequenceNom,
|
||||
};
|
||||
|
||||
return sendEmail({
|
||||
to: params.apprenantEmail,
|
||||
subject: `🎉 Place disponible - ${params.formationNom}`,
|
||||
html: await getEmailTemplate(content, 'notification_liste_attente', variables),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie une notification à un apprenant en liste d'attente pour l'informer d'une annulation de séquence
|
||||
*/
|
||||
export async function sendNotificationAnnulationListeAttente(params: {
|
||||
apprenantEmail: string;
|
||||
apprenantNom: string;
|
||||
apprenantPrenom: string;
|
||||
apprenantFonction: string;
|
||||
formationNom: string;
|
||||
sequenceNom: string;
|
||||
raisonAnnulation?: string;
|
||||
alternativesDisponibles?: Array<{ sequenceNom: string; nbPlaces: number }>;
|
||||
}): Promise<boolean> {
|
||||
const fonctionLabel = params.apprenantFonction === 'directeur' ? 'Directeur' :
|
||||
params.apprenantFonction === 'chef_service' ? 'Chef de service' : '';
|
||||
const salutation = fonctionLabel ? `${fonctionLabel} ${params.apprenantPrenom} ${params.apprenantNom}` : params.apprenantPrenom;
|
||||
|
||||
const alternativesHTML = params.alternativesDisponibles && params.alternativesDisponibles.length > 0
|
||||
? `
|
||||
<div class="info-box" style="background-color: #dbeafe; border-left-color: #3b82f6;">
|
||||
<h3>📋 Séquences alternatives disponibles</h3>
|
||||
<ul>
|
||||
${params.alternativesDisponibles.map(alt => `
|
||||
<li><strong>${alt.sequenceNom}</strong> - ${alt.nbPlaces} place(s) disponible(s)</li>
|
||||
`).join('')}
|
||||
</ul>
|
||||
<p>Contactez le service RH pour vous inscrire à une autre séquence.</p>
|
||||
</div>
|
||||
` : '';
|
||||
|
||||
const content = `
|
||||
<h2>Information : Annulation de séquence</h2>
|
||||
<p>Bonjour ${salutation},</p>
|
||||
|
||||
<p>Nous vous informons que la séquence de formation pour laquelle vous étiez en liste d'attente a été annulée.</p>
|
||||
|
||||
<div class="info-box" style="background-color: #fee2e2; border-left-color: #ef4444;">
|
||||
<h3>Séquence annulée</h3>
|
||||
<p><strong>Formation :</strong> ${params.formationNom}</p>
|
||||
<p><strong>Séquence :</strong> ${params.sequenceNom}</p>
|
||||
${params.raisonAnnulation ? `<p><strong>Raison :</strong> ${params.raisonAnnulation}</p>` : ''}
|
||||
</div>
|
||||
|
||||
${alternativesHTML}
|
||||
|
||||
<p>Nous nous excusons pour ce désagrément et restons à votre disposition pour toute question.</p>
|
||||
|
||||
<p>Cordialement,<br/>L'équipe Formation</p>
|
||||
`;
|
||||
|
||||
return sendEmail({
|
||||
to: params.apprenantEmail,
|
||||
subject: `Information : Annulation de séquence - ${params.formationNom}`,
|
||||
html: await getEmailTemplate(content, 'notification_annulation'),
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user