Checkpoint: Ajout de 3 améliorations pour le système de notifications :
1. Interface de gestion des emails formateurs (Configuration > Formateurs) - permet de gérer les adresses email des formateurs pour activer/désactiver les notifications 2. Tableau de bord des notifications (Configuration > Notifications) - historique complet des emails envoyés avec filtres et statistiques 3. Lien vers questionnaire de satisfaction automatiquement inclus dans l'email de remerciement post-formation
This commit is contained in:
@@ -9,6 +9,7 @@ import * as analyticsDb from "./analyticsDb";
|
||||
import { generateAnalyticsExcel, generateAnalyticsPDF } from "./analyticsExportService";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { sendInscriptionConfirmation, sendGroupEmail, sendNotificationFormateurNouvelleInscription, sendNotificationFormateurAnnulation, sendAlerteCapaciteAtteinte, sendNotificationPlaceDisponible, sendRemerciementPostFormation } from "./emailService";
|
||||
import { logNotification } from "./notificationLogsDb";
|
||||
import { generateExcelExport, generatePDFExport, generateFeuillePresence } from "./exportService";
|
||||
import { parseExcelFile, validateImportData, importToDatabase } from "./importExcel";
|
||||
|
||||
@@ -226,6 +227,7 @@ export const appRouter = router({
|
||||
|
||||
create: adminProcedure.input(z.object({
|
||||
nom: z.string().min(1),
|
||||
email: z.string().email().optional(),
|
||||
})).mutation(async ({ input }) => {
|
||||
await db.createFormateur(input);
|
||||
return { success: true };
|
||||
@@ -234,6 +236,7 @@ export const appRouter = router({
|
||||
update: adminProcedure.input(z.object({
|
||||
id: z.number(),
|
||||
nom: z.string().min(1).optional(),
|
||||
email: z.string().email().nullable().optional(),
|
||||
})).mutation(async ({ input }) => {
|
||||
const { id, ...data } = input;
|
||||
await db.updateFormateur(id, data);
|
||||
@@ -487,8 +490,29 @@ export const appRouter = router({
|
||||
ordre: d.ordre,
|
||||
})),
|
||||
});
|
||||
// Logger la notification
|
||||
await logNotification({
|
||||
type: "notification_formateur_inscription",
|
||||
sequenceId: input.sequenceId,
|
||||
apprenantId: inscriptionApprenant.id,
|
||||
formateurId: formateur.id,
|
||||
emailDestinataire: formateur.email,
|
||||
sujet: `Nouvelle inscription - ${inscriptionFormation.nom}`,
|
||||
statut: "success",
|
||||
});
|
||||
console.log(`[Notification] Email envoyé au formateur ${formateur.email} pour nouvelle inscription`);
|
||||
} catch (e) {
|
||||
} catch (e: any) {
|
||||
// Logger l'échec
|
||||
await logNotification({
|
||||
type: "notification_formateur_inscription",
|
||||
sequenceId: input.sequenceId,
|
||||
apprenantId: inscriptionApprenant.id,
|
||||
formateurId: formateur.id,
|
||||
emailDestinataire: formateur.email,
|
||||
sujet: `Nouvelle inscription - ${inscriptionFormation.nom}`,
|
||||
statut: "failed",
|
||||
messageErreur: e.message,
|
||||
});
|
||||
console.error(`[Notification] Erreur envoi email formateur:`, e);
|
||||
}
|
||||
}
|
||||
@@ -582,8 +606,29 @@ export const appRouter = router({
|
||||
nbInscrits: nbInscritsApres,
|
||||
capaciteMax: sequence.capaciteMax,
|
||||
});
|
||||
// Logger la notification
|
||||
await logNotification({
|
||||
type: "notification_formateur_annulation",
|
||||
sequenceId: input.sequenceId,
|
||||
apprenantId: apprenant.id,
|
||||
formateurId: formateur.id,
|
||||
emailDestinataire: formateur.email,
|
||||
sujet: `Annulation d'inscription - ${formation.nom}`,
|
||||
statut: "success",
|
||||
});
|
||||
console.log(`[Notification] Email d'annulation envoyé au formateur ${formateur.email}`);
|
||||
} catch (e) {
|
||||
} catch (e: any) {
|
||||
// Logger l'échec
|
||||
await logNotification({
|
||||
type: "notification_formateur_annulation",
|
||||
sequenceId: input.sequenceId,
|
||||
apprenantId: apprenant.id,
|
||||
formateurId: formateur.id,
|
||||
emailDestinataire: formateur.email,
|
||||
sujet: `Annulation d'inscription - ${formation.nom}`,
|
||||
statut: "failed",
|
||||
messageErreur: e.message,
|
||||
});
|
||||
console.error(`[Notification] Erreur envoi email formateur:`, e);
|
||||
}
|
||||
}
|
||||
@@ -607,8 +652,27 @@ export const appRouter = router({
|
||||
positionListeAttente: 1,
|
||||
delaiReponse: 48,
|
||||
});
|
||||
// Logger la notification
|
||||
await logNotification({
|
||||
type: "notification_liste_attente",
|
||||
sequenceId: input.sequenceId,
|
||||
apprenantId: premierEnAttente.apprenant.id,
|
||||
emailDestinataire: premierEnAttente.apprenant.email,
|
||||
sujet: `Place disponible - ${formation.nom}`,
|
||||
statut: "success",
|
||||
});
|
||||
console.log(`[Notification] Email place disponible envoyé à ${premierEnAttente.apprenant.email}`);
|
||||
} catch (e) {
|
||||
} catch (e: any) {
|
||||
// Logger l'échec
|
||||
await logNotification({
|
||||
type: "notification_liste_attente",
|
||||
sequenceId: input.sequenceId,
|
||||
apprenantId: premierEnAttente.apprenant.id,
|
||||
emailDestinataire: premierEnAttente.apprenant.email,
|
||||
sujet: `Place disponible - ${formation.nom}`,
|
||||
statut: "failed",
|
||||
messageErreur: e.message,
|
||||
});
|
||||
console.error(`[Notification] Erreur envoi email liste d'attente:`, e);
|
||||
}
|
||||
}
|
||||
@@ -1631,6 +1695,46 @@ export const appRouter = router({
|
||||
await processRemerciementsAutomatiques();
|
||||
return { success: true };
|
||||
}),
|
||||
|
||||
// Historique des notifications
|
||||
historique: adminProcedure
|
||||
.input(z.object({
|
||||
type: z.enum(["remerciement", "notification_formateur_inscription", "notification_formateur_annulation", "alerte_capacite", "notification_liste_attente"]).optional(),
|
||||
dateDebut: z.string().optional(),
|
||||
dateFin: z.string().optional(),
|
||||
statut: z.enum(["success", "failed"]).optional(),
|
||||
email: z.string().optional(),
|
||||
sequenceId: z.number().optional(),
|
||||
limit: z.number().default(50),
|
||||
offset: z.number().default(0),
|
||||
}))
|
||||
.query(async ({ input }) => {
|
||||
const { getNotificationLogs } = await import("./notificationLogsDb");
|
||||
return getNotificationLogs({
|
||||
type: input.type,
|
||||
dateDebut: input.dateDebut ? new Date(input.dateDebut) : undefined,
|
||||
dateFin: input.dateFin ? new Date(input.dateFin) : undefined,
|
||||
statut: input.statut,
|
||||
email: input.email,
|
||||
sequenceId: input.sequenceId,
|
||||
limit: input.limit,
|
||||
offset: input.offset,
|
||||
});
|
||||
}),
|
||||
|
||||
// Statistiques des notifications
|
||||
statistiques: adminProcedure
|
||||
.input(z.object({
|
||||
dateDebut: z.string().optional(),
|
||||
dateFin: z.string().optional(),
|
||||
}))
|
||||
.query(async ({ input }) => {
|
||||
const { getNotificationStats } = await import("./notificationLogsDb");
|
||||
return getNotificationStats({
|
||||
dateDebut: input.dateDebut ? new Date(input.dateDebut) : undefined,
|
||||
dateFin: input.dateFin ? new Date(input.dateFin) : undefined,
|
||||
});
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user