Checkpoint: Modification de la procédure testerRappel pour envoyer le rappel à tous les inscrits confirmés :

- Remplacement de l'envoi au premier inscrit uniquement par un envoi à tous les inscrits confirmés
- Ajout d'un compteur de succès et d'échecs
- Gestion des erreurs individuelles pour chaque envoi
- Message de retour détaillé avec le nombre d'envois réussis et échoués
- Déploiement sur le VPS de production
- Mise à jour du fichier todo.md
This commit is contained in:
Manus
2026-01-15 13:01:17 -05:00
parent e642f23170
commit 820c12d6c1
2 changed files with 52 additions and 35 deletions

View File

@@ -1424,48 +1424,64 @@ export const appRouter = router({
throw new TRPCError({ code: 'BAD_REQUEST', message: 'Aucun apprenant inscrit confirmé pour cette séquence' });
}
// Envoyer le rappel de test au premier inscrit uniquement
const premierInscrit = inscritsConfirmes[0];
const apprenant = premierInscrit.apprenant;
if (!apprenant || !apprenant.email) {
throw new TRPCError({ code: 'BAD_REQUEST', message: 'Apprenant introuvable ou sans email' });
}
// Envoyer le rappel de test à l'administrateur connecté
// Envoyer le rappel de test à tous les inscrits confirmés
const { sendRappelEmail } = await import('./emailService');
try {
await sendRappelEmail({
apprenantEmail: apprenant.email,
apprenantPrenom: apprenant.prenom,
apprenantNom: apprenant.nom,
apprenantFonction: apprenant.fonction || '',
formationNom: formation.nom,
sequenceNom: sequence.nom,
dates: dates.map((d: any) => ({
dateDebut: d.dateDebut,
dateFin: d.dateFin,
ordre: d.ordre
})),
lieu: sequence.lieu || '',
formateur: formateurNom,
templateType: rappelAUtiliser.templateType,
attachmentUrl: rappelAUtiliser.urlFichier || undefined,
attachmentFilename: rappelAUtiliser.nomFichier || undefined,
attachmentMimeType: rappelAUtiliser.typeFichier || undefined,
});
let nbEnvoyes = 0;
let nbEchecs = 0;
const erreurs: string[] = [];
return {
success: true,
message: `Rappel de test envoyé à ${apprenant.email}`,
destinataire: apprenant.email,
};
} catch (error: any) {
for (const inscrit of inscritsConfirmes) {
const apprenant = inscrit.apprenant;
if (!apprenant || !apprenant.email) {
nbEchecs++;
erreurs.push(`Apprenant ${apprenant?.nom || 'inconnu'}: email manquant`);
continue;
}
try {
await sendRappelEmail({
apprenantEmail: apprenant.email,
apprenantPrenom: apprenant.prenom,
apprenantNom: apprenant.nom,
apprenantFonction: apprenant.fonction || '',
formationNom: formation.nom,
sequenceNom: sequence.nom,
dates: dates.map((d: any) => ({
dateDebut: d.dateDebut,
dateFin: d.dateFin,
ordre: d.ordre
})),
lieu: sequence.lieu || '',
formateur: formateurNom,
templateType: rappelAUtiliser.templateType,
attachmentUrl: rappelAUtiliser.urlFichier || undefined,
attachmentFilename: rappelAUtiliser.nomFichier || undefined,
attachmentMimeType: rappelAUtiliser.typeFichier || undefined,
});
nbEnvoyes++;
} catch (error: any) {
nbEchecs++;
erreurs.push(`${apprenant.email}: ${error.message}`);
}
}
if (nbEnvoyes === 0) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: `Erreur lors de l'envoi: ${error.message}`,
message: `Aucun email envoyé. Erreurs: ${erreurs.join(', ')}`,
});
}
return {
success: true,
message: `Rappel de test envoyé à ${nbEnvoyes} apprenant(s)${nbEchecs > 0 ? ` (${nbEchecs} échec(s))` : ''}`,
nbEnvoyes,
nbEchecs,
erreurs: nbEchecs > 0 ? erreurs : undefined,
};
}),
historique: adminProcedure

View File

@@ -820,3 +820,4 @@
- [x] Corriger l'erreur de validation du type de rappel dans la procédure testerRappel (typeRappel invalide)
- [x] Corriger l'erreur "Cannot read properties of undefined (reading 'replace')" lors de l'envoi de test de rappel
- [x] Améliorer le bouton "Tester le rappel" pour utiliser automatiquement le bon template configuré pour la séquence (au lieu de toujours envoyer rappel J-7)
- [x] Modifier la procédure testerRappel pour envoyer le rappel à tous les inscrits confirmés (au lieu du premier uniquement)