Checkpoint: Refonte majeure : transformation du système de sessions en séquences avec support de jusqu'à 4 dates par séquence. L'inscription à une séquence inscrit automatiquement l'apprenant à toutes les dates. Mise à jour complète du backend (schéma DB, procédures tRPC, emails, exports) et du frontend (AdminSequences, AdminSequenceInscrits, Inscription, Admin). Toutes les fonctionnalités existantes (filtres, statistiques, exports PDF/Excel, emails automatiques) sont préservées et adaptées au nouveau modèle.
This commit is contained in:
@@ -67,6 +67,7 @@ function getEmailTemplate(content: string): string {
|
||||
.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>
|
||||
@@ -88,7 +89,7 @@ function getEmailTemplate(content: string): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie un email de confirmation d'inscription avec invitation Outlook
|
||||
* Envoie un email de confirmation d'inscription avec invitations Outlook pour toutes les dates
|
||||
*/
|
||||
export async function sendInscriptionConfirmation(params: {
|
||||
apprenantEmail: string;
|
||||
@@ -96,9 +97,8 @@ export async function sendInscriptionConfirmation(params: {
|
||||
apprenantPrenom: string;
|
||||
apprenantFonction: string;
|
||||
formationNom: string;
|
||||
sessionNom: string;
|
||||
dateDebut: Date;
|
||||
dateFin: Date;
|
||||
sequenceNom: string;
|
||||
dates: Array<{ dateDebut: Date; dateFin: Date; ordre: number }>;
|
||||
lieu: string;
|
||||
statut: 'confirmee' | 'liste_attente';
|
||||
}): Promise<boolean> {
|
||||
@@ -106,6 +106,29 @@ export async function sendInscriptionConfirmation(params: {
|
||||
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>
|
||||
@@ -118,49 +141,36 @@ export async function sendInscriptionConfirmation(params: {
|
||||
<div class="info-box">
|
||||
<h3>Détails de la formation</h3>
|
||||
<p><strong>Formation :</strong> ${params.formationNom}</p>
|
||||
<p><strong>Session :</strong> ${params.sessionNom}</p>
|
||||
<p><strong>Date de début :</strong> ${params.dateDebut.toLocaleDateString('fr-FR', {
|
||||
weekday: 'long',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
})}</p>
|
||||
<p><strong>Date de fin :</strong> ${params.dateFin.toLocaleDateString('fr-FR', {
|
||||
weekday: 'long',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
})}</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>Une invitation Outlook est jointe à cet email. Merci de l'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 session. Après cette date, aucune modification ne sera possible.</p>`
|
||||
? `<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>
|
||||
`;
|
||||
|
||||
const attachments = isConfirmed ? [{
|
||||
filename: 'invitation.ics',
|
||||
// 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.sessionNom,
|
||||
dateDebut: params.dateDebut,
|
||||
dateFin: params.dateFin,
|
||||
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;
|
||||
})) : undefined;
|
||||
|
||||
return sendEmail({
|
||||
to: params.apprenantEmail,
|
||||
@@ -173,7 +183,7 @@ export async function sendInscriptionConfirmation(params: {
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie un email teaser pour une session
|
||||
* Envoie un email teaser pour une séquence
|
||||
*/
|
||||
export async function sendTeaserEmail(params: {
|
||||
apprenantEmail: string;
|
||||
@@ -181,13 +191,24 @@ export async function sendTeaserEmail(params: {
|
||||
apprenantNom: string;
|
||||
apprenantFonction: string;
|
||||
formationNom: string;
|
||||
sessionNom: string;
|
||||
dateDebut: Date;
|
||||
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>
|
||||
@@ -195,13 +216,9 @@ export async function sendTeaserEmail(params: {
|
||||
<p>Nous sommes ravis de vous accueillir prochainement pour la formation <strong>${params.formationNom}</strong>.</p>
|
||||
|
||||
<div class="info-box">
|
||||
<p><strong>Session :</strong> ${params.sessionNom}</p>
|
||||
<p><strong>Date :</strong> ${params.dateDebut.toLocaleDateString('fr-FR', {
|
||||
weekday: 'long',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
})}</p>
|
||||
<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>
|
||||
@@ -225,14 +242,28 @@ export async function sendRappelJ7Email(params: {
|
||||
apprenantNom: string;
|
||||
apprenantFonction: string;
|
||||
formationNom: string;
|
||||
sessionNom: string;
|
||||
dateDebut: Date;
|
||||
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>
|
||||
@@ -241,16 +272,10 @@ export async function sendRappelJ7Email(params: {
|
||||
|
||||
<div class="info-box">
|
||||
<h3>Informations pratiques</h3>
|
||||
<p><strong>Session :</strong> ${params.sessionNom}</p>
|
||||
<p><strong>Date :</strong> ${params.dateDebut.toLocaleDateString('fr-FR', {
|
||||
weekday: 'long',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
})}</p>
|
||||
<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>
|
||||
@@ -268,14 +293,14 @@ export async function sendRappelJ7Email(params: {
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie des emails groupés à tous les inscrits d'une session
|
||||
* 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;
|
||||
sessionNom: string;
|
||||
sequenceNom: string;
|
||||
type: 'teaser' | 'rappel';
|
||||
dateDebut: Date;
|
||||
dates: Array<{ dateDebut: Date; dateFin: Date; ordre: number }>;
|
||||
lieu?: string;
|
||||
}): Promise<{ sent: number; failed: number }> {
|
||||
let sent = 0;
|
||||
@@ -290,8 +315,8 @@ export async function sendGroupEmail(params: {
|
||||
apprenantNom: recipient.nom,
|
||||
apprenantFonction: recipient.fonction,
|
||||
formationNom: params.formationNom,
|
||||
sessionNom: params.sessionNom,
|
||||
dateDebut: params.dateDebut,
|
||||
sequenceNom: params.sequenceNom,
|
||||
dates: params.dates,
|
||||
});
|
||||
} else {
|
||||
await sendRappelJ7Email({
|
||||
@@ -300,8 +325,8 @@ export async function sendGroupEmail(params: {
|
||||
apprenantNom: recipient.nom,
|
||||
apprenantFonction: recipient.fonction,
|
||||
formationNom: params.formationNom,
|
||||
sessionNom: params.sessionNom,
|
||||
dateDebut: params.dateDebut,
|
||||
sequenceNom: params.sequenceNom,
|
||||
dates: params.dates,
|
||||
lieu: params.lieu || '',
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user