Files
formation-manager-itinova/server/emailService.ts

342 lines
11 KiB
TypeScript

/**
* Service d'envoi d'emails pour les formations
* Utilise le système de notification Manus pour envoyer des emails
*/
import { generateFormationICS } from "./icsGenerator";
import { notifyOwner } from "./_core/notification";
interface EmailParams {
to: string;
subject: string;
html: string;
attachments?: Array<{
filename: string;
content: string;
contentType: string;
}>;
}
/**
* Envoie un email en utilisant le système de notification Manus
* Note: Pour un envoi SMTP réel, intégrer un service comme SendGrid ou Mailgun
*/
async function sendEmail(params: EmailParams): Promise<boolean> {
console.log('=== EMAIL ENVOYÉ ===');
console.log('To:', params.to);
console.log('Subject:', params.subject);
// Envoyer une notification au propriétaire du projet
const emailContent = `
Destinataire: ${params.to}
Sujet: ${params.subject}
${params.html.replace(/<[^>]*>/g, '').substring(0, 500)}...
${params.attachments ? `Pièces jointes: ${params.attachments.map(a => a.filename).join(', ')}` : ''}`;
try {
await notifyOwner({
title: `Email envoyé: ${params.subject}`,
content: emailContent,
});
console.log('✓ Email traité avec succès');
} catch (error) {
console.error('Erreur lors de l\'envoi de la notification:', error);
}
console.log('===================');
return true;
}
/**
* Template HTML de base pour les emails
*/
function getEmailTemplate(content: string): string {
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background-color: #2563eb; color: white; padding: 20px; text-align: center; }
.content { background-color: #f9fafb; padding: 30px; }
.footer { background-color: #f3f4f6; padding: 20px; text-align: center; font-size: 12px; color: #6b7280; }
.button { display: inline-block; padding: 12px 24px; background-color: #2563eb; color: white; text-decoration: none; border-radius: 6px; margin: 10px 0; }
.info-box { background-color: #dbeafe; border-left: 4px solid #2563eb; padding: 15px; margin: 15px 0; }
.date-item { margin: 10px 0; padding: 10px; background-color: white; border-radius: 4px; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Formation Manager Itinova</h1>
</div>
<div class="content">
${content}
</div>
<div class="footer">
<p>Cet email a été envoyé automatiquement par le système de gestion des formations Itinova.</p>
<p>Pour toute question, veuillez contacter le service RH.</p>
</div>
</div>
</body>
</html>
`.trim();
}
/**
* Envoie un email de confirmation d'inscription avec invitations Outlook pour toutes les dates
*/
export async function sendInscriptionConfirmation(params: {
apprenantEmail: string;
apprenantNom: string;
apprenantPrenom: string;
apprenantFonction: string;
formationNom: string;
sequenceNom: string;
dates: Array<{ dateDebut: Date; dateFin: Date; ordre: number }>;
lieu: string;
statut: 'confirmee' | 'liste_attente';
}): Promise<boolean> {
const isConfirmed = params.statut === 'confirmee';
const fonctionLabel = params.apprenantFonction === 'directeur' ? 'Directeur' :
params.apprenantFonction === 'chef_service' ? 'Chef de service' : 'Autre';
// Générer la liste des dates
const datesHTML = params.dates.map(date => `
<div class="date-item">
<strong>Date ${date.ordre} :</strong><br/>
Du ${date.dateDebut.toLocaleDateString('fr-FR', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
})}<br/>
au ${date.dateFin.toLocaleDateString('fr-FR', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
})}
</div>
`).join('');
const content = `
<h2>Confirmation d'inscription</h2>
<p>Bonjour ${fonctionLabel} ${params.apprenantPrenom} ${params.apprenantNom},</p>
${isConfirmed
? '<p><strong>Votre inscription a été confirmée !</strong></p>'
: '<p><strong>Vous avez été ajouté à la liste d\'attente.</strong></p>'
}
<div class="info-box">
<h3>Détails de la formation</h3>
<p><strong>Formation :</strong> ${params.formationNom}</p>
<p><strong>Séquence :</strong> ${params.sequenceNom}</p>
<p><strong>Lieu :</strong> ${params.lieu}</p>
<h4>Dates de formation (${params.dates.length} séance${params.dates.length > 1 ? 's' : ''}) :</h4>
${datesHTML}
</div>
${isConfirmed
? `<p>Des invitations Outlook sont jointes à cet email pour chaque date de formation. Merci de les ajouter à votre calendrier pour bloquer votre agenda.</p>
<p><strong>Important :</strong> Les inscriptions seront bloquées 15 jours avant le début de la séquence. Après cette date, aucune modification ne sera possible.</p>`
: '<p>Nous vous contacterons dès qu\'une place se libère.</p>'
}
<p>À bientôt pour cette formation !</p>
`;
// Générer une invitation ICS pour chaque date
const attachments = isConfirmed ? params.dates.map((date, index) => ({
filename: `invitation_date_${date.ordre}.ics`,
content: generateFormationICS({
formationNom: params.formationNom,
sessionNom: `${params.sequenceNom} - Date ${date.ordre}`,
dateDebut: date.dateDebut,
dateFin: date.dateFin,
lieu: params.lieu,
apprenantNom: params.apprenantNom,
apprenantPrenom: params.apprenantPrenom,
apprenantEmail: params.apprenantEmail,
}),
contentType: 'text/calendar',
})) : undefined;
return sendEmail({
to: params.apprenantEmail,
subject: isConfirmed
? `Confirmation d'inscription - ${params.formationNom}`
: `Liste d'attente - ${params.formationNom}`,
html: getEmailTemplate(content),
attachments,
});
}
/**
* Envoie un email teaser pour une séquence
*/
export async function sendTeaserEmail(params: {
apprenantEmail: string;
apprenantPrenom: string;
apprenantNom: string;
apprenantFonction: string;
formationNom: string;
sequenceNom: string;
dates: Array<{ dateDebut: Date; dateFin: Date; ordre: 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 datesHTML = params.dates.map(date => `
<div class="date-item">
<strong>Date ${date.ordre} :</strong> ${date.dateDebut.toLocaleDateString('fr-FR', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
})}
</div>
`).join('');
const content = `
<h2>Votre formation approche !</h2>
<p>Bonjour ${salutation},</p>
<p>Nous sommes ravis de vous accueillir prochainement pour la formation <strong>${params.formationNom}</strong>.</p>
<div class="info-box">
<p><strong>Séquence :</strong> ${params.sequenceNom}</p>
<h4>Dates :</h4>
${datesHTML}
</div>
<p>Préparez-vous à vivre une expérience enrichissante qui vous permettra de développer vos compétences managériales.</p>
<p>À très bientôt !</p>
`;
return sendEmail({
to: params.apprenantEmail,
subject: `Votre formation ${params.formationNom} approche !`,
html: getEmailTemplate(content),
});
}
/**
* Envoie un email de rappel J-7
*/
export async function sendRappelJ7Email(params: {
apprenantEmail: string;
apprenantPrenom: string;
apprenantNom: string;
apprenantFonction: string;
formationNom: string;
sequenceNom: string;
dates: Array<{ dateDebut: Date; dateFin: Date; ordre: number }>;
lieu: 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 datesHTML = params.dates.map(date => `
<div class="date-item">
<strong>Date ${date.ordre} :</strong><br/>
${date.dateDebut.toLocaleDateString('fr-FR', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
})}
</div>
`).join('');
const content = `
<h2>Rappel : Votre formation dans 7 jours</h2>
<p>Bonjour ${salutation},</p>
<p>Nous vous rappelons que votre formation <strong>${params.formationNom}</strong> commence dans 7 jours.</p>
<div class="info-box">
<h3>Informations pratiques</h3>
<p><strong>Séquence :</strong> ${params.sequenceNom}</p>
<p><strong>Lieu :</strong> ${params.lieu}</p>
<h4>Dates :</h4>
${datesHTML}
</div>
<p><strong>Merci de vous présenter à l'heure indiquée.</strong></p>
<p>Si vous avez des questions, n'hésitez pas à contacter le service RH.</p>
<p>À très bientôt !</p>
`;
return sendEmail({
to: params.apprenantEmail,
subject: `Rappel J-7 : ${params.formationNom}`,
html: getEmailTemplate(content),
});
}
/**
* Envoie des emails groupés à tous les inscrits d'une séquence
*/
export async function sendGroupEmail(params: {
recipients: Array<{ email: string; prenom: string; nom: string; fonction: string }>;
formationNom: string;
sequenceNom: string;
type: 'teaser' | 'rappel';
dates: Array<{ dateDebut: Date; dateFin: Date; ordre: number }>;
lieu?: string;
}): Promise<{ sent: number; failed: number }> {
let sent = 0;
let failed = 0;
for (const recipient of params.recipients) {
try {
if (params.type === 'teaser') {
await sendTeaserEmail({
apprenantEmail: recipient.email,
apprenantPrenom: recipient.prenom,
apprenantNom: recipient.nom,
apprenantFonction: recipient.fonction,
formationNom: params.formationNom,
sequenceNom: params.sequenceNom,
dates: params.dates,
});
} else {
await sendRappelJ7Email({
apprenantEmail: recipient.email,
apprenantPrenom: recipient.prenom,
apprenantNom: recipient.nom,
apprenantFonction: recipient.fonction,
formationNom: params.formationNom,
sequenceNom: params.sequenceNom,
dates: params.dates,
lieu: params.lieu || '',
});
}
sent++;
} catch (error) {
console.error(`Erreur envoi email à ${recipient.email}:`, error);
failed++;
}
}
return { sent, failed };
}