1144 lines
No EOL
45 KiB
TypeScript
1144 lines
No EOL
45 KiB
TypeScript
// lib/emailTemplateService.ts - Version modernisée
|
||
import { SESClient, SendEmailCommand } from "@aws-sdk/client-ses";
|
||
import { readFileSync } from "fs";
|
||
import { join } from "path";
|
||
// Importer la build ESM/UMD de Handlebars pour éviter l'avertissement webpack sur require.extensions
|
||
import Handlebars from 'handlebars/dist/handlebars.js';
|
||
import { emailLogger, type EmailType, extractSesDataFromResponse } from './emailLoggingService';
|
||
|
||
// Couleurs standardisées pour tous les emails
|
||
const STANDARD_COLORS = {
|
||
HEADER: '#171424',
|
||
BUTTON: '#efc543',
|
||
BUTTON_HOVER: '#d4a834',
|
||
BUTTON_TEXT: '#000000',
|
||
ALERT_RED: '#EF4444',
|
||
} as const;
|
||
|
||
export interface EmailConfigV2 {
|
||
type: EmailTypeV2;
|
||
toEmail: string;
|
||
ccEmail?: string;
|
||
subject?: string; // Rendu optionnel, car peut être défini dans le template
|
||
data: EmailDataV2;
|
||
}
|
||
|
||
export type EmailTypeV2 =
|
||
| 'contract-created'
|
||
| 'contract-updated'
|
||
| 'contract-cancelled'
|
||
| 'employee-created' // Ajout du nouveau type
|
||
| 'invitation'
|
||
| 'auto-declaration-invitation' // Nouveau type pour l'invitation auto-déclaration
|
||
| 'invoice'
|
||
| 'signature-request'
|
||
| 'signature-request-employer'
|
||
| 'signature-request-employee'
|
||
| 'bulk-signature-notification' // Nouveau type pour notification de signatures en masse
|
||
| 'salary-transfer-notification' // Nouveau type pour notification d'appel à virement
|
||
| 'contribution-notification' // Nouveau type pour notification de cotisations
|
||
| 'notification'
|
||
// Support
|
||
| 'support-reply' // Réponse du staff à un ticket support
|
||
| 'support-ticket-created' // Notification interne : nouveau ticket créé
|
||
| 'support-ticket-reply' // Notification interne : réponse utilisateur à un ticket
|
||
// Accès / habilitations
|
||
| 'account-activation'
|
||
| 'access-updated'
|
||
| 'access-revoked'
|
||
// Sécurité compte
|
||
| 'password-created'
|
||
| 'password-changed'
|
||
| 'twofa-enabled'
|
||
| 'twofa-disabled';
|
||
|
||
export interface EmailDataV2 {
|
||
firstName?: string;
|
||
organizationName?: string;
|
||
employeeName?: string;
|
||
contractReference?: string;
|
||
startDate?: string;
|
||
endDate?: string;
|
||
profession?: string;
|
||
amount?: string;
|
||
ctaUrl?: string;
|
||
customMessage?: string;
|
||
// Ajout des champs pour la carte d'info
|
||
companyName?: string;
|
||
employerCode?: string;
|
||
userName?: string;
|
||
productionName?: string;
|
||
supportUrl?: string;
|
||
// Ajout des champs pour les factures et virements
|
||
invoiceNumber?: string;
|
||
invoiceDate?: string;
|
||
sepaDate?: string;
|
||
paymentMethod?: string;
|
||
invoiceType?: string;
|
||
// Ajout des champs pour les notifications de signatures en masse
|
||
contractCount?: number;
|
||
handlerName?: string;
|
||
status?: string;
|
||
// Ajout des champs pour les appels à virement
|
||
totalAmount?: string;
|
||
periodLabel?: string;
|
||
deadline?: string;
|
||
transferReference?: string;
|
||
// Ajout des champs pour les tickets support
|
||
ticketId?: string;
|
||
ticketSubject?: string;
|
||
staffName?: string;
|
||
staffMessage?: string;
|
||
// Ajout des champs pour les notifications internes de tickets
|
||
ticketCategory?: string;
|
||
ticketMessage?: string;
|
||
ticketStatus?: string;
|
||
userEmail?: string;
|
||
userMessage?: string;
|
||
[key: string]: unknown;
|
||
}
|
||
|
||
interface EmailTemplateV2 {
|
||
subject: string;
|
||
title: string;
|
||
mainMessage: string;
|
||
greeting?: string;
|
||
closingMessage?: string;
|
||
ctaText?: string;
|
||
ctaUrl?: string; // URL du bouton CTA
|
||
footerText: string;
|
||
preheaderText: string;
|
||
|
||
// Configuration des cartes
|
||
infoCard?: Array<{ label: string; key: string; }>;
|
||
detailsCard?: {
|
||
title: string;
|
||
rows: Array<{ label: string; key: string; }>;
|
||
};
|
||
bankCard?: {
|
||
title: string;
|
||
subtitle?: string;
|
||
conditionKey: string; // Clé pour vérifier la condition
|
||
conditionValue: string; // Valeur attendue
|
||
additionalConditionKey?: string;
|
||
additionalConditionValue?: string;
|
||
rows: Array<{ label: string; value: string; }>;
|
||
disclaimer?: string;
|
||
};
|
||
|
||
// Couleurs et indicateurs
|
||
colors: {
|
||
headerColor: string;
|
||
titleColor: string;
|
||
buttonColor: string;
|
||
buttonTextColor: string;
|
||
cardBorder: string;
|
||
cardBackgroundColor: string;
|
||
cardTitleColor: string;
|
||
alertIndicatorColor?: string;
|
||
};
|
||
}
|
||
|
||
const EMAIL_TEMPLATES_V2: Record<EmailTypeV2, EmailTemplateV2> = {
|
||
'password-created': {
|
||
subject: 'Mot de passe créé sur votre compte',
|
||
title: 'Mot de passe créé',
|
||
greeting: '{{#if firstName}}Bonjour {{firstName}},{{/if}}',
|
||
mainMessage: 'Vous venez de créer un mot de passe pour votre compte Odentas Paie.',
|
||
closingMessage: 'Si vous n’êtes pas à l’origine de cette action, <a href="{{supportUrl}}" style="color:#0B5FFF; text-decoration:none;">contactez le support</a> immédiatement.',
|
||
ctaText: 'Gérer ma sécurité',
|
||
footerText: 'Vous recevez cet e-mail suite à une modification de la sécurité de votre compte.',
|
||
preheaderText: 'Mot de passe créé · Sécurisez votre compte',
|
||
colors: {
|
||
headerColor: STANDARD_COLORS.HEADER,
|
||
titleColor: '#0F172A',
|
||
buttonColor: STANDARD_COLORS.BUTTON,
|
||
buttonTextColor: STANDARD_COLORS.BUTTON_TEXT,
|
||
cardBackgroundColor: '#FFFFFF',
|
||
cardBorder: '#E5E7EB',
|
||
cardTitleColor: '#0F172A',
|
||
alertIndicatorColor: '#22C55E',
|
||
},
|
||
infoCard: [
|
||
{ label: 'Utilisateur', key: 'userEmail' },
|
||
{ label: 'Statut', key: 'status' },
|
||
],
|
||
detailsCard: {
|
||
title: 'Détails',
|
||
rows: [
|
||
{ label: 'Date', key: 'eventDate' },
|
||
{ label: 'Plateforme', key: 'platform' },
|
||
]
|
||
}
|
||
},
|
||
|
||
'password-changed': {
|
||
subject: 'Votre mot de passe a été modifié',
|
||
title: 'Mot de passe modifié',
|
||
greeting: '{{#if firstName}}Bonjour {{firstName}},{{/if}}',
|
||
mainMessage: 'Votre mot de passe a été modifié avec succès.',
|
||
closingMessage: 'Si vous n’êtes pas à l’origine de cette modification, <a href="{{supportUrl}}" style="color:#0B5FFF; text-decoration:none;">contactez le support</a> immédiatement.',
|
||
ctaText: 'Accès à l\'Espace Paie',
|
||
footerText: 'Vous recevez cet e-mail pour confirmer une modification de mot de passe.',
|
||
preheaderText: 'Mot de passe modifié · Vérifiez la sécurité de votre compte',
|
||
colors: {
|
||
headerColor: STANDARD_COLORS.HEADER,
|
||
titleColor: '#0F172A',
|
||
buttonColor: STANDARD_COLORS.BUTTON,
|
||
buttonTextColor: STANDARD_COLORS.BUTTON_TEXT,
|
||
cardBackgroundColor: '#FFFFFF',
|
||
cardBorder: '#E5E7EB',
|
||
cardTitleColor: '#0F172A',
|
||
alertIndicatorColor: '#F59E0B',
|
||
},
|
||
infoCard: [
|
||
{ label: 'Utilisateur', key: 'userEmail' },
|
||
{ label: 'Statut', key: 'status' },
|
||
],
|
||
detailsCard: {
|
||
title: 'Détails',
|
||
rows: [
|
||
{ label: 'Date', key: 'eventDate' },
|
||
{ label: 'Plateforme', key: 'platform' },
|
||
]
|
||
}
|
||
},
|
||
|
||
'twofa-enabled': {
|
||
subject: '2FA activée sur votre compte',
|
||
title: 'Double authentification activée',
|
||
greeting: '{{#if firstName}}Bonjour {{firstName}},{{/if}}',
|
||
mainMessage: 'La double authentification (2FA) est maintenant activée sur votre compte.',
|
||
ctaText: 'Gérer ma sécurité',
|
||
footerText: 'Vous recevez cet e-mail pour confirmer l’activation de la 2FA.',
|
||
preheaderText: '2FA activée · Sécurité renforcée',
|
||
colors: {
|
||
headerColor: STANDARD_COLORS.HEADER,
|
||
titleColor: '#0F172A',
|
||
buttonColor: STANDARD_COLORS.BUTTON,
|
||
buttonTextColor: STANDARD_COLORS.BUTTON_TEXT,
|
||
cardBackgroundColor: '#FFFFFF',
|
||
cardBorder: '#E5E7EB',
|
||
cardTitleColor: '#0F172A',
|
||
alertIndicatorColor: '#22C55E',
|
||
},
|
||
infoCard: [
|
||
{ label: 'Utilisateur', key: 'userEmail' },
|
||
{ label: 'Statut', key: 'status' },
|
||
],
|
||
detailsCard: {
|
||
title: 'Détails',
|
||
rows: [
|
||
{ label: 'Date', key: 'eventDate' },
|
||
{ label: 'Plateforme', key: 'platform' },
|
||
]
|
||
}
|
||
},
|
||
|
||
'twofa-disabled': {
|
||
subject: '2FA désactivée sur votre compte',
|
||
title: 'Double authentification désactivée',
|
||
greeting: '{{#if firstName}}Bonjour {{firstName}},{{/if}}',
|
||
mainMessage: 'La double authentification (2FA) a été désactivée sur votre compte.',
|
||
closingMessage: 'Nous recommandons de la réactiver pour une sécurité optimale. <a href="{{supportUrl}}" style="color:#0B5FFF; text-decoration:none;">Contactez le support</a> si ce n’était pas vous.',
|
||
ctaText: 'Gérer ma sécurité',
|
||
footerText: 'Vous recevez cet e-mail pour confirmer la désactivation de la 2FA.',
|
||
preheaderText: '2FA désactivée · Votre sécurité est réduite',
|
||
colors: {
|
||
headerColor: STANDARD_COLORS.HEADER,
|
||
titleColor: '#0F172A',
|
||
buttonColor: STANDARD_COLORS.BUTTON,
|
||
buttonTextColor: STANDARD_COLORS.BUTTON_TEXT,
|
||
cardBackgroundColor: '#FFFFFF',
|
||
cardBorder: '#E5E7EB',
|
||
cardTitleColor: '#0F172A',
|
||
alertIndicatorColor: STANDARD_COLORS.ALERT_RED,
|
||
},
|
||
infoCard: [
|
||
{ label: 'Utilisateur', key: 'userEmail' },
|
||
{ label: 'Statut', key: 'status' },
|
||
],
|
||
detailsCard: {
|
||
title: 'Détails',
|
||
rows: [
|
||
{ label: 'Date', key: 'eventDate' },
|
||
{ label: 'Plateforme', key: 'platform' },
|
||
]
|
||
}
|
||
},
|
||
'account-activation': {
|
||
subject: 'Activez votre espace Odentas Paie – {{organizationName}}',
|
||
title: 'Activez votre espace',
|
||
greeting: '{{#if firstName}}Bonjour {{firstName}},{{/if}}',
|
||
mainMessage: 'Bienvenue ! Vous avez été invité à rejoindre {{organizationName}} sur Odentas Paie.',
|
||
ctaText: 'Activer mon compte',
|
||
footerText: 'Vous recevez cet e-mail car un accès vous a été créé sur Odentas Paie.',
|
||
preheaderText: 'Activation de votre espace · {{organizationName}} · Activez maintenant',
|
||
colors: {
|
||
headerColor: STANDARD_COLORS.HEADER,
|
||
titleColor: '#0F172A',
|
||
buttonColor: STANDARD_COLORS.BUTTON,
|
||
buttonTextColor: STANDARD_COLORS.BUTTON_TEXT,
|
||
cardBackgroundColor: '#FFFFFF',
|
||
cardBorder: '#E5E7EB',
|
||
cardTitleColor: '#0F172A',
|
||
alertIndicatorColor: '#22C55E',
|
||
},
|
||
infoCard: [
|
||
{ label: 'Organisation', key: 'organizationName' },
|
||
{ label: 'Votre email', key: 'userEmail' },
|
||
{ label: 'Statut', key: 'status' },
|
||
],
|
||
detailsCard: {
|
||
title: 'Informations',
|
||
rows: [
|
||
{ label: 'Plateforme', key: 'platform' },
|
||
{ label: 'Action requise', key: 'actionRequired' },
|
||
]
|
||
}
|
||
},
|
||
|
||
'access-updated': {
|
||
subject: 'Vos habilitations ont été modifiées – {{organizationName}}',
|
||
title: 'Habilitation mise à jour',
|
||
greeting: '{{#if firstName}}Bonjour {{firstName}},{{/if}}',
|
||
mainMessage: 'Votre rôle au sein de {{organizationName}} a été modifié.',
|
||
ctaText: 'Voir vos accès',
|
||
footerText: 'Vous recevez cet e-mail suite à une modification de vos droits d’accès.',
|
||
preheaderText: 'Habilitation mise à jour · Nouveau rôle: {{newRole}}',
|
||
colors: {
|
||
headerColor: STANDARD_COLORS.HEADER,
|
||
titleColor: '#0F172A',
|
||
buttonColor: STANDARD_COLORS.BUTTON,
|
||
buttonTextColor: STANDARD_COLORS.BUTTON_TEXT,
|
||
cardBackgroundColor: '#FFFFFF',
|
||
cardBorder: '#E5E7EB',
|
||
cardTitleColor: '#0F172A',
|
||
alertIndicatorColor: '#F59E0B',
|
||
},
|
||
infoCard: [
|
||
{ label: 'Organisation', key: 'organizationName' },
|
||
{ label: 'Utilisateur', key: 'userEmail' },
|
||
],
|
||
detailsCard: {
|
||
title: 'Détails de la modification',
|
||
rows: [
|
||
{ label: 'Ancien rôle', key: 'oldRole' },
|
||
{ label: 'Nouveau rôle', key: 'newRole' },
|
||
{ label: 'Modifié par', key: 'updatedBy' },
|
||
{ label: 'Date', key: 'updateDate' },
|
||
]
|
||
}
|
||
},
|
||
|
||
'access-revoked': {
|
||
subject: 'Votre accès a été révoqué – {{organizationName}}',
|
||
title: 'Accès révoqué',
|
||
greeting: '{{#if firstName}}Bonjour {{firstName}},{{/if}}',
|
||
mainMessage: 'Votre accès à {{organizationName}} a été révoqué.',
|
||
closingMessage: 'Si vous pensez qu’il s’agit d’une erreur, <a href="{{supportUrl}}" style="color:#0B5FFF; text-decoration:none;">contactez le support</a>.',
|
||
ctaText: 'Contacter le support',
|
||
footerText: 'Vous recevez cet e-mail pour vous informer de la révocation de votre accès.',
|
||
preheaderText: 'Accès révoqué · Contactez le support en cas d’erreur',
|
||
colors: {
|
||
headerColor: STANDARD_COLORS.HEADER,
|
||
titleColor: '#0F172A',
|
||
buttonColor: STANDARD_COLORS.BUTTON,
|
||
buttonTextColor: STANDARD_COLORS.BUTTON_TEXT,
|
||
cardBackgroundColor: '#FFFFFF',
|
||
cardBorder: '#E5E7EB',
|
||
cardTitleColor: '#0F172A',
|
||
alertIndicatorColor: STANDARD_COLORS.ALERT_RED,
|
||
},
|
||
infoCard: [
|
||
{ label: 'Organisation', key: 'organizationName' },
|
||
{ label: 'Utilisateur', key: 'userEmail' },
|
||
{ label: 'Statut', key: 'status' },
|
||
],
|
||
detailsCard: {
|
||
title: 'Détails de la révocation',
|
||
rows: [
|
||
{ label: 'Date de révocation', key: 'revocationDate' },
|
||
{ label: 'Révoqué par', key: 'revokedBy' },
|
||
{ label: 'Raison', key: 'reason' },
|
||
]
|
||
}
|
||
},
|
||
'contract-created': {
|
||
subject: 'Nouveau CDDU créé - {{employeeName}}',
|
||
title: 'Nouveau contrat CDDU',
|
||
greeting: 'Bonjour {{userName}},',
|
||
mainMessage: 'Un nouveau contrat CDDU a été créé sur votre Espace Paie Odentas.',
|
||
ctaText: 'Voir le contrat',
|
||
footerText: 'Vous recevez cet e-mail car vous êtes client de Odentas, pour vous notifier d\'une action sur votre compte.',
|
||
preheaderText: 'Nouveau contrat créé · Consultez et téléchargez votre contrat',
|
||
colors: {
|
||
headerColor: STANDARD_COLORS.HEADER,
|
||
titleColor: '#0F172A',
|
||
buttonColor: STANDARD_COLORS.BUTTON,
|
||
buttonTextColor: STANDARD_COLORS.BUTTON_TEXT,
|
||
cardBackgroundColor: '#FFFFFF',
|
||
cardBorder: '#E5E7EB',
|
||
cardTitleColor: '#0F172A',
|
||
alertIndicatorColor: '#22C55E', // Vert pour création
|
||
},
|
||
infoCard: [
|
||
{ label: 'Votre structure', key: 'companyName' },
|
||
{ label: 'Votre code employeur', key: 'employerCode' },
|
||
{ label: 'Votre gestionnaire', key: 'handlerName' },
|
||
],
|
||
detailsCard: {
|
||
title: 'Détails du contrat',
|
||
rows: [
|
||
{ label: 'Salarié', key: 'employeeName' },
|
||
{ label: 'Poste', key: 'profession' },
|
||
{ label: 'Date de début', key: 'startDate' },
|
||
{ label: 'Référence', key: 'contractReference' }
|
||
]
|
||
}
|
||
},
|
||
|
||
'employee-created': {
|
||
subject: 'Nouveau salarié créé - {{employeeName}}',
|
||
title: 'Nouveau salarié créé',
|
||
greeting: 'Bonjour {{userName}},',
|
||
mainMessage: 'Un nouveau salarié a été créé avec succès dans votre Espace Paie Odentas.',
|
||
ctaText: 'Voir la fiche du salarié',
|
||
footerText: 'Vous recevez cet e-mail car vous êtes client de Odentas, pour vous notifier d\'une action sur votre compte.',
|
||
preheaderText: 'Nouveau salarié créé · Consultez la fiche du salarié',
|
||
colors: {
|
||
headerColor: STANDARD_COLORS.HEADER,
|
||
titleColor: '#0F172A',
|
||
buttonColor: STANDARD_COLORS.BUTTON,
|
||
buttonTextColor: STANDARD_COLORS.BUTTON_TEXT,
|
||
cardBackgroundColor: '#FFFFFF',
|
||
cardBorder: '#E5E7EB',
|
||
cardTitleColor: '#0F172A',
|
||
alertIndicatorColor: '#10B981', // Vert émeraude pour nouveau salarié
|
||
},
|
||
infoCard: [
|
||
{ label: 'Votre structure', key: 'companyName' },
|
||
{ label: 'Votre code employeur', key: 'employerCode' },
|
||
{ label: 'Votre gestionnaire', key: 'handlerName' },
|
||
],
|
||
detailsCard: {
|
||
title: 'Détails du salarié',
|
||
rows: [
|
||
{ label: 'Salarié', key: 'employeeName' },
|
||
{ label: 'Email', key: 'email' },
|
||
{ label: 'Matricule', key: 'matricule' },
|
||
]
|
||
}
|
||
},
|
||
|
||
'contract-updated': {
|
||
subject: 'CDDU modifié - {{employeeName}}',
|
||
title: 'Contrat CDDU modifié',
|
||
greeting: 'Bonjour {{userName}},',
|
||
mainMessage: 'Votre contrat CDDU a été modifié sur votre Espace Paie Odentas.',
|
||
ctaText: 'Voir les modifications',
|
||
footerText: 'Vous recevez cet e-mail car vous êtes client de Odentas, pour vous notifier d\'une action sur votre compte.',
|
||
preheaderText: 'Contrat modifié · Consultez les changements',
|
||
colors: {
|
||
headerColor: STANDARD_COLORS.HEADER,
|
||
titleColor: '#0F172A',
|
||
buttonColor: STANDARD_COLORS.BUTTON,
|
||
buttonTextColor: STANDARD_COLORS.BUTTON_TEXT,
|
||
cardBackgroundColor: '#FFFFFF',
|
||
cardBorder: '#E5E7EB',
|
||
cardTitleColor: '#0F172A',
|
||
alertIndicatorColor: '#F59E0B', // Orange pour modification
|
||
},
|
||
infoCard: [
|
||
{ label: 'Votre structure', key: 'companyName' },
|
||
{ label: 'Votre code employeur', key: 'employerCode' },
|
||
{ label: 'Votre gestionnaire', key: 'handlerName' },
|
||
],
|
||
detailsCard: {
|
||
title: 'Détails du contrat',
|
||
rows: [
|
||
{ label: 'Salarié', key: 'employeeName' },
|
||
{ label: 'Poste', key: 'profession' },
|
||
{ label: 'Date de début', key: 'startDate' },
|
||
{ label: 'Référence', key: 'contractReference' }
|
||
]
|
||
}
|
||
},
|
||
|
||
'contract-cancelled': {
|
||
subject: 'CDDU annulé - {{employeeName}}',
|
||
title: 'CDDU annulé',
|
||
greeting: 'Bonjour {{userName}},',
|
||
mainMessage: 'Votre contrat CDDU a été annulé.',
|
||
closingMessage: 'Pour votre sécurité, ne partagez jamais ce message. <br>Si vous n\'êtes pas à l\'origine de cette annulation, <a href="{{supportUrl}}" style="color:#0B5FFF; text-decoration:none;">contactez le support</a>.<br><br>L\'équipe Odentas vous remercie pour votre confiance.',
|
||
ctaText: 'Accès à l\'Espace Paie',
|
||
footerText: 'Vous recevez cet e-mail car vous êtes client de Odentas, pour vous notifier d\'une action sur votre compte.',
|
||
preheaderText: 'CDDU annulé pour {{employeeName}} · Réf {{contractReference}} · Plus d\'informations disponibles',
|
||
colors: {
|
||
headerColor: '#efc543',
|
||
titleColor: '#0F172A',
|
||
buttonColor: '#efc543',
|
||
buttonTextColor: '#000000',
|
||
cardBackgroundColor: '#FFFFFF',
|
||
cardBorder: '#E5E7EB',
|
||
cardTitleColor: '#0F172A',
|
||
alertIndicatorColor: STANDARD_COLORS.ALERT_RED,
|
||
},
|
||
infoCard: [
|
||
{ label: 'Votre structure', key: 'companyName' },
|
||
{ label: 'Votre code employeur', key: 'employerCode' },
|
||
{ label: 'Votre gestionnaire', key: 'handlerName' }, // Note: 'handlerName' is new
|
||
],
|
||
detailsCard: {
|
||
title: 'Détails du contrat',
|
||
rows: [
|
||
{ label: 'Référence', key: 'contractReference' },
|
||
{ label: 'Salarié', key: 'employeeName' },
|
||
{ label: 'Poste', key: 'profession' },
|
||
{ label: 'Date de début', key: 'startDate' },
|
||
{ label: 'Production', key: 'productionName' },
|
||
]
|
||
}
|
||
},
|
||
|
||
'invitation': {
|
||
subject: 'Invitation à rejoindre {{organizationName}}',
|
||
title: 'Vous êtes invité',
|
||
mainMessage: 'Vous avez été invité à rejoindre l\'organisation {{organizationName}} sur Odentas.',
|
||
ctaText: 'Accepter l\'invitation',
|
||
footerText: 'Vous recevez cet e-mail car vous avez été invité sur Odentas.',
|
||
preheaderText: 'Invitation reçue · Rejoignez votre équipe',
|
||
colors: {
|
||
headerColor: STANDARD_COLORS.HEADER,
|
||
titleColor: '#0F172A',
|
||
buttonColor: STANDARD_COLORS.BUTTON,
|
||
buttonTextColor: STANDARD_COLORS.BUTTON_TEXT,
|
||
cardBackgroundColor: '#F0F9FF',
|
||
cardBorder: '#BAE6FD',
|
||
cardTitleColor: '#0369A1'
|
||
}
|
||
},
|
||
|
||
'auto-declaration-invitation': {
|
||
subject: 'Complétez votre dossier d\'embauche - Documents requis',
|
||
title: 'Complétez votre dossier d\'embauche',
|
||
greeting: '{{#if firstName}}Bonjour {{firstName}},{{/if}}',
|
||
mainMessage: 'Dans le cadre de votre projet d\'embauche géré par les services d\'Odentas pour le compte de {{organizationName}}, nous vous invitons à compléter votre dossier.\n\nPour finaliser votre embauche, nous avons besoin que vous nous transmettiez certaines informations personnelles et justificatifs.',
|
||
closingMessage: 'Important : Ce lien d\'accès expire dans 7 jours. Pensez à compléter votre dossier avant cette date.<br><br>Besoin d\'aide ? N\'hésitez pas à nous contacter à <a href="mailto:paie@odentas.fr" style="color:#0B5FFF;">paie@odentas.fr</a> pour toute question.',
|
||
ctaText: 'Compléter mon dossier',
|
||
footerText: 'Ce lien est personnel et sécurisé. Ne le partagez avec personne d\'autre.<br><br>Vous recevez cet e-mail car votre employeur ou futur employeur est client de Odentas, pour vous notifier d\'une action sur votre contrat de travail ou votre projet d\'embauche avec cet employeur. Ce mail ne constitue pas une promesse d\'embauche.',
|
||
preheaderText: 'Dossier d\'embauche · Complétez vos informations',
|
||
colors: {
|
||
headerColor: STANDARD_COLORS.HEADER,
|
||
titleColor: '#0F172A',
|
||
buttonColor: '#059669',
|
||
buttonTextColor: '#FFFFFF',
|
||
cardBackgroundColor: '#F0F9FF',
|
||
cardBorder: '#E0F2FE',
|
||
cardTitleColor: '#0C4A6E'
|
||
},
|
||
infoCard: [
|
||
{ label: 'Votre employeur', key: 'organizationName' },
|
||
{ label: 'Votre matricule', key: 'matricule' }
|
||
]
|
||
},
|
||
|
||
'invoice': {
|
||
subject: 'Nouvelle facture - {{amount}}',
|
||
title: 'Nouvelle facture',
|
||
greeting: '{{#if firstName}}Bonjour {{firstName}},{{/if}}',
|
||
mainMessage: '{{{customMessage}}}',
|
||
closingMessage: 'Si vous avez des questions concernant cette facture, <a href="mailto:paie@odentas.fr" style="color:#0B5FFF; text-decoration:none;">contactez-nous à paie@odentas.fr</a>.<br><br>L\'équipe Odentas vous remercie pour votre confiance.',
|
||
ctaText: 'Voir la facture',
|
||
footerText: 'Vous recevez cet e-mail car vous êtes client de Odentas, pour vous notifier d\'une action sur votre compte.',
|
||
preheaderText: 'Nouvelle facture · Accédez à votre espace',
|
||
colors: {
|
||
headerColor: STANDARD_COLORS.HEADER,
|
||
titleColor: '#0F172A',
|
||
buttonColor: STANDARD_COLORS.BUTTON,
|
||
buttonTextColor: STANDARD_COLORS.BUTTON_TEXT,
|
||
cardBackgroundColor: '#FFFFFF',
|
||
cardBorder: '#E5E7EB',
|
||
cardTitleColor: '#0F172A',
|
||
alertIndicatorColor: '#22C55E'
|
||
},
|
||
infoCard: [
|
||
{ label: 'Votre structure', key: 'organizationName' },
|
||
{ label: 'Votre code employeur', key: 'employerCode' },
|
||
{ label: 'Votre gestionnaire', key: 'handlerName' }
|
||
],
|
||
detailsCard: {
|
||
title: 'Détails de la facture',
|
||
rows: [
|
||
{ label: 'Numéro', key: 'invoiceNumber' },
|
||
{ label: 'Montant', key: 'amount' },
|
||
{ label: 'Date de facture', key: 'invoiceDate' },
|
||
{ label: 'Prélèvement SEPA', key: 'sepaDate' }
|
||
]
|
||
},
|
||
bankCard: {
|
||
title: 'Coordonnées bancaires pour le virement',
|
||
conditionKey: 'invoiceType',
|
||
conditionValue: 'paie_mensuelle',
|
||
additionalConditionKey: 'paymentMethod',
|
||
additionalConditionValue: 'virement',
|
||
rows: [
|
||
{ label: 'Bénéficiaire', value: 'Odentas Media SAS' },
|
||
{ label: 'IBAN', value: 'FR7616958000014277434166014' },
|
||
{ label: 'BIC', value: 'QNTOFRP1XXX' },
|
||
{ label: 'Libellé', value: '{{invoiceNumber}}' }
|
||
],
|
||
disclaimer: 'Ce compte ne peut pas recevoir les virements de salaires de vos salariés.'
|
||
}
|
||
},
|
||
|
||
'signature-request': {
|
||
subject: 'Signature requise - {{employeeName}}',
|
||
title: 'Signature électronique requise',
|
||
mainMessage: 'Votre signature est requise pour finaliser le contrat.',
|
||
ctaText: 'Signer maintenant',
|
||
footerText: 'Vous recevez cet e-mail car votre signature est requise.',
|
||
preheaderText: 'Signature requise · Finalisez votre contrat',
|
||
colors: {
|
||
headerColor: STANDARD_COLORS.HEADER,
|
||
titleColor: '#0F172A',
|
||
buttonColor: STANDARD_COLORS.BUTTON,
|
||
buttonTextColor: STANDARD_COLORS.BUTTON_TEXT,
|
||
cardBackgroundColor: '#FFFFFF',
|
||
cardBorder: '#E5E7EB',
|
||
cardTitleColor: '#0F172A',
|
||
alertIndicatorColor: '#22C55E'
|
||
},
|
||
greeting: '{{#if firstName}}Bonjour {{firstName}},{{/if}}',
|
||
infoCard: [
|
||
{ label: 'Votre structure', key: 'organizationName' },
|
||
{ label: 'Votre code employeur', key: 'employerCode' },
|
||
{ label: 'Votre gestionnaire', key: 'handlerName' },
|
||
],
|
||
detailsCard: {
|
||
title: 'Détails de la signature',
|
||
rows: [
|
||
{ label: 'Document', key: 'documentType' },
|
||
{ label: 'Salarié', key: 'employeeName' },
|
||
{ label: 'Référence', key: 'contractReference' },
|
||
{ label: 'Statut', key: 'status' },
|
||
]
|
||
}
|
||
},
|
||
|
||
'signature-request-employer': {
|
||
subject: 'Signature requise – {{employeeName}}',
|
||
title: 'Signature électronique requise',
|
||
greeting: '{{#if firstName}}Bonjour {{firstName}},{{/if}}',
|
||
mainMessage: 'Un document nécessite votre signature électronique en tant qu’employeur.',
|
||
ctaText: 'Signer le document',
|
||
footerText: 'Vous recevez cet e-mail car votre signature est requise sur un document.',
|
||
preheaderText: 'Signature électronique requise · Signez en tant qu’employeur',
|
||
colors: {
|
||
headerColor: STANDARD_COLORS.HEADER,
|
||
titleColor: '#0F172A',
|
||
buttonColor: STANDARD_COLORS.BUTTON,
|
||
buttonTextColor: STANDARD_COLORS.BUTTON_TEXT,
|
||
cardBackgroundColor: '#FFFFFF',
|
||
cardBorder: '#E5E7EB',
|
||
cardTitleColor: '#0F172A',
|
||
alertIndicatorColor: '#22C55E',
|
||
},
|
||
infoCard: [
|
||
{ label: 'Votre structure', key: 'organizationName' },
|
||
{ label: 'Votre code employeur', key: 'employerCode' },
|
||
{ label: 'Votre gestionnaire', key: 'handlerName' },
|
||
],
|
||
detailsCard: {
|
||
title: 'Détails du document',
|
||
rows: [
|
||
{ label: 'Document', key: 'documentType' },
|
||
{ label: 'Salarié', key: 'employeeName' },
|
||
{ label: 'Référence', key: 'contractReference' },
|
||
{ label: 'Statut', key: 'status' },
|
||
]
|
||
}
|
||
},
|
||
|
||
'signature-request-employee': {
|
||
subject: 'Signature requise – {{documentType}}',
|
||
title: 'Signature électronique requise',
|
||
greeting: '{{#if firstName}}Bonjour {{firstName}},{{/if}}',
|
||
mainMessage: 'Ce rappel vous est envoyé par votre employeur, votre contrat de travail est en attente de signature.',
|
||
ctaText: 'Signer le document',
|
||
closingMessage: 'Nous restons à votre disposition à paie@odentas.fr si vous avez besoin d\'assistance.',
|
||
footerText: 'Vous recevez cet e-mail car votre employeur est client de Odentas, pour vous notifier d\'une action sur votre contrat de travail avec cet employeur.',
|
||
preheaderText: 'Signature électronique requise · Signez votre document',
|
||
colors: {
|
||
headerColor: STANDARD_COLORS.HEADER,
|
||
titleColor: '#0F172A',
|
||
buttonColor: STANDARD_COLORS.BUTTON,
|
||
buttonTextColor: STANDARD_COLORS.BUTTON_TEXT,
|
||
cardBackgroundColor: '#FFFFFF',
|
||
cardBorder: '#E5E7EB',
|
||
cardTitleColor: '#0F172A',
|
||
alertIndicatorColor: '#22C55E',
|
||
},
|
||
infoCard: [
|
||
{ label: 'Votre employeur', key: 'organizationName' },
|
||
{ label: 'Votre matricule', key: 'matricule' },
|
||
],
|
||
detailsCard: {
|
||
title: 'Détails du document',
|
||
rows: [
|
||
{ label: 'Référence', key: 'contractReference' },
|
||
{ label: 'Profession', key: 'profession' },
|
||
{ label: 'Date de début', key: 'startDate' },
|
||
{ label: 'Production', key: 'productionName' },
|
||
]
|
||
}
|
||
},
|
||
|
||
'bulk-signature-notification': {
|
||
subject: 'Contrats à signer – {{contractCount}} signature{{#if (gt contractCount 1)}}s{{/if}} en attente',
|
||
title: 'Contrats en attente de signature',
|
||
greeting: '{{#if firstName}}Bonjour {{firstName}},{{/if}}',
|
||
mainMessage: 'Vous avez {{contractCount}} contrat{{#if (gt contractCount 1)}}s{{/if}} en attente de signature électronique.<br><br>Pour consulter et signer vos contrats, cliquez sur le bouton ci-dessous :',
|
||
closingMessage: 'Si vous avez des questions, n\'hésitez pas à nous contacter à <a href="mailto:paie@odentas.fr" style="color:#0B5FFF; text-decoration:none;">paie@odentas.fr</a>.',
|
||
ctaText: 'Accéder aux signatures',
|
||
ctaUrl: 'https://paie.odentas.fr/signatures-electroniques',
|
||
footerText: 'Vous recevez cet e-mail car vous êtes client de Odentas, pour vous notifier d\'une action sur votre compte.',
|
||
preheaderText: 'Contrats à signer · {{contractCount}} signature{{#if (gt contractCount 1)}}s{{/if}} requise{{#if (gt contractCount 1)}}s{{/if}}',
|
||
colors: {
|
||
headerColor: STANDARD_COLORS.HEADER,
|
||
titleColor: '#0F172A',
|
||
buttonColor: STANDARD_COLORS.BUTTON,
|
||
buttonTextColor: STANDARD_COLORS.BUTTON_TEXT,
|
||
cardBackgroundColor: '#FFFFFF',
|
||
cardBorder: '#E5E7EB',
|
||
cardTitleColor: '#0F172A',
|
||
alertIndicatorColor: '#6366F1',
|
||
},
|
||
infoCard: [
|
||
{ label: 'Votre structure', key: 'organizationName' },
|
||
{ label: 'Votre code employeur', key: 'employerCode' },
|
||
{ label: 'Votre gestionnaire', key: 'handlerName' },
|
||
],
|
||
detailsCard: {
|
||
title: 'Signatures en attente',
|
||
rows: [
|
||
{ label: 'Nombre de contrats', key: 'contractCount' },
|
||
{ label: 'Statut', key: 'status' },
|
||
]
|
||
}
|
||
},
|
||
|
||
'salary-transfer-notification': {
|
||
subject: 'Nouvel appel à virement - {{periodLabel}}',
|
||
title: 'Nouvel appel à virement',
|
||
greeting: '{{#if firstName}}👋 Bonjour {{firstName}},{{/if}}',
|
||
mainMessage: 'Vous trouverez ci-dessous les détails du virement à nous transférer pour le paiement des rémunérations de vos salariés.<br><br>L\'appel à virement est disponible sur votre Espace Paie, en cliquant sur le bouton ci-dessous.',
|
||
closingMessage: 'Le montant correspond au cumul des salaires nets de la période concernée, après prélèvement à la source.<br><br>Dès réception du virement, nous procéderons sous 24 heures ouvrées aux versements individuels des salaires. Vos salariés et vous-même recevrez une notification.<br><br>Les cotisations (ainsi que le prélèvement à la source) sont prélevées directement sur votre compte bancaire par les organismes concernés.<br><br>N\'hésitez pas à répondre à cet e-mail si vous avez besoin d\'assistance.<br><br>Merci pour votre confiance,<br>L\'équipe Odentas.',
|
||
ctaText: 'Télécharger l\'Appel à Virement',
|
||
ctaUrl: 'https://paie.odentas.fr/virements-salaires/',
|
||
footerText: 'Vous recevez cet e-mail car vous êtes client de Odentas, pour vous notifier d\'une action sur votre compte.',
|
||
preheaderText: 'Appel à virement · {{periodLabel}}',
|
||
colors: {
|
||
headerColor: STANDARD_COLORS.HEADER,
|
||
titleColor: '#0F172A',
|
||
buttonColor: STANDARD_COLORS.BUTTON,
|
||
buttonTextColor: STANDARD_COLORS.BUTTON_TEXT,
|
||
cardBackgroundColor: '#F0F0F5',
|
||
cardBorder: '#E5E7EB',
|
||
cardTitleColor: '#0F172A',
|
||
alertIndicatorColor: '#22C55E',
|
||
},
|
||
infoCard: [
|
||
{ label: 'Votre structure', key: 'organizationName' },
|
||
{ label: 'Votre code employeur', key: 'employerCode' },
|
||
{ label: 'Votre gestionnaire', key: 'handlerName' },
|
||
],
|
||
detailsCard: {
|
||
title: 'Détails du virement',
|
||
rows: [
|
||
{ label: 'Montant à virer', key: 'totalAmount' },
|
||
{ label: 'Période concernée', key: 'periodLabel' },
|
||
]
|
||
},
|
||
bankCard: {
|
||
title: 'Nos coordonnées bancaires',
|
||
subtitle: 'Attention, le compte bancaire ci-dessous est uniquement destiné à la gestion des salaires.',
|
||
conditionKey: 'showBankInfo',
|
||
conditionValue: 'true',
|
||
rows: [
|
||
{ label: 'Bénéficiaire', value: 'ODENTAS MEDIA SAS' },
|
||
{ label: 'IBAN', value: 'FR76 1695 8000 0141 0850 9729 813' },
|
||
{ label: 'BIC', value: 'QNTOFRP1XXX' },
|
||
{ label: 'Référence', value: '{{transferReference}}' },
|
||
],
|
||
disclaimer: 'Ce compte bancaire est réservé à la réception des virements de salaires.'
|
||
}
|
||
},
|
||
|
||
'contribution-notification': {
|
||
subject: 'Échéance de cotisations - {{periodLabel}}',
|
||
title: 'Échéance de cotisations',
|
||
greeting: '{{#if firstName}}👋 Bonjour {{firstName}},{{/if}}',
|
||
mainMessage: '{{#if hasZeroContributions}}Nous vous informons qu\'en l\'absence de paie pour la période {{periodLabel}}, aucune cotisation ne vous sera prélevée par les caisses et organismes.{{else}}Nous vous rappelons que les prélèvements des cotisations sociales pour la période {{periodLabel}} auront lieu prochainement.{{/if}}',
|
||
closingMessage: 'Si vous avez des questions concernant ces cotisations, n\'hésitez pas à nous contacter en répondant à cet e-mail.<br><br>Cordialement,<br>L\'équipe Odentas.',
|
||
ctaText: 'Voir mes cotisations',
|
||
ctaUrl: 'https://paie.odentas.fr/cotisations',
|
||
footerText: 'Vous recevez cet e-mail car vous êtes client de Odentas, pour vous notifier d\'une action sur votre compte.',
|
||
preheaderText: 'Rappel de prélèvement · {{periodLabel}}',
|
||
colors: {
|
||
headerColor: STANDARD_COLORS.HEADER,
|
||
titleColor: '#0F172A',
|
||
buttonColor: STANDARD_COLORS.BUTTON,
|
||
buttonTextColor: STANDARD_COLORS.BUTTON_TEXT,
|
||
cardBackgroundColor: '#F0F0F5',
|
||
cardBorder: '#E5E7EB',
|
||
cardTitleColor: '#0F172A',
|
||
alertIndicatorColor: '#F59E0B',
|
||
},
|
||
infoCard: [
|
||
{ label: 'Votre structure', key: 'organizationName' },
|
||
{ label: 'Votre code employeur', key: 'employerCode' },
|
||
{ label: 'Votre gestionnaire', key: 'handlerName' },
|
||
],
|
||
detailsCard: {
|
||
title: 'Informations sur la période',
|
||
rows: [
|
||
{ label: 'Période concernée', key: 'periodLabel' },
|
||
{ label: 'Date de prélèvement théorique', key: 'collectionPeriod' },
|
||
]
|
||
}
|
||
},
|
||
|
||
'notification': {
|
||
subject: 'Notification - {{title}}',
|
||
title: 'Notification',
|
||
mainMessage: 'Vous avez une nouvelle notification.',
|
||
footerText: 'Vous recevez cet e-mail car vous êtes client de Odentas, pour vous notifier d\'une action sur votre compte.',
|
||
preheaderText: 'Nouvelle notification',
|
||
colors: {
|
||
headerColor: STANDARD_COLORS.HEADER,
|
||
titleColor: '#0F172A',
|
||
buttonColor: STANDARD_COLORS.BUTTON,
|
||
buttonTextColor: STANDARD_COLORS.BUTTON_TEXT,
|
||
cardBackgroundColor: '#F9FAFB',
|
||
cardBorder: '#E5E7EB',
|
||
cardTitleColor: '#374151'
|
||
}
|
||
},
|
||
|
||
'support-reply': {
|
||
subject: 'Nouvelle réponse à votre ticket support',
|
||
title: 'Réponse à votre ticket',
|
||
greeting: '{{#if firstName}}Bonjour {{firstName}},{{/if}}',
|
||
mainMessage: 'Vous avez reçu une réponse à votre ticket support.',
|
||
ctaText: 'Voir le ticket',
|
||
footerText: 'Vous recevez cet e-mail suite à une réponse de notre équipe support.',
|
||
preheaderText: 'Notre équipe support a répondu à votre ticket',
|
||
colors: {
|
||
headerColor: STANDARD_COLORS.HEADER,
|
||
titleColor: '#0F172A',
|
||
buttonColor: STANDARD_COLORS.BUTTON,
|
||
buttonTextColor: STANDARD_COLORS.BUTTON_TEXT,
|
||
cardBackgroundColor: '#FFFFFF',
|
||
cardBorder: '#E5E7EB',
|
||
cardTitleColor: '#0F172A',
|
||
},
|
||
infoCard: [
|
||
{ label: 'Votre structure', key: 'organizationName' },
|
||
{ label: 'Votre code employeur', key: 'employerCode' },
|
||
{ label: 'Votre gestionnaire', key: 'handlerName' },
|
||
],
|
||
detailsCard: {
|
||
title: 'Message',
|
||
rows: [
|
||
{ label: 'Répondu par', key: 'staffName' },
|
||
{ label: 'Sujet', key: 'ticketSubject' },
|
||
{ label: 'Réponse', key: 'staffMessage' },
|
||
]
|
||
}
|
||
},
|
||
|
||
'support-ticket-created': {
|
||
subject: '[SUPPORT] Nouveau ticket : {{ticketSubject}}',
|
||
title: '🎫 Nouveau ticket support',
|
||
greeting: 'Notification interne',
|
||
mainMessage: 'Un utilisateur a créé un nouveau ticket support.',
|
||
ctaText: 'Voir le ticket',
|
||
footerText: 'Notification automatique du système de support Odentas.',
|
||
preheaderText: 'Nouveau ticket support créé',
|
||
colors: {
|
||
headerColor: STANDARD_COLORS.HEADER,
|
||
titleColor: '#0F172A',
|
||
buttonColor: '#3B82F6', // Bleu pour les notifications internes
|
||
buttonTextColor: '#FFFFFF',
|
||
cardBackgroundColor: '#FFFFFF',
|
||
cardBorder: '#E5E7EB',
|
||
cardTitleColor: '#0F172A',
|
||
},
|
||
infoCard: [
|
||
{ label: 'Organisation', key: 'organizationName' },
|
||
{ label: 'Code employeur', key: 'employerCode' },
|
||
{ label: 'Créé par', key: 'userName' },
|
||
{ label: 'Email', key: 'userEmail' },
|
||
],
|
||
detailsCard: {
|
||
title: 'Détails du ticket',
|
||
rows: [
|
||
{ label: 'ID', key: 'ticketId' },
|
||
{ label: 'Sujet', key: 'ticketSubject' },
|
||
{ label: 'Catégorie', key: 'ticketCategory' },
|
||
{ label: 'Message', key: 'ticketMessage' },
|
||
]
|
||
}
|
||
},
|
||
|
||
'support-ticket-reply': {
|
||
subject: '[SUPPORT] Réponse au ticket : {{ticketSubject}}',
|
||
title: '💬 Réponse sur un ticket',
|
||
greeting: 'Notification interne',
|
||
mainMessage: 'Un utilisateur a répondu à un ticket support.',
|
||
ctaText: 'Voir le ticket',
|
||
footerText: 'Notification automatique du système de support Odentas.',
|
||
preheaderText: 'Nouvelle réponse sur un ticket support',
|
||
colors: {
|
||
headerColor: STANDARD_COLORS.HEADER,
|
||
titleColor: '#0F172A',
|
||
buttonColor: '#3B82F6', // Bleu pour les notifications internes
|
||
buttonTextColor: '#FFFFFF',
|
||
cardBackgroundColor: '#FFFFFF',
|
||
cardBorder: '#E5E7EB',
|
||
cardTitleColor: '#0F172A',
|
||
},
|
||
infoCard: [
|
||
{ label: 'Organisation', key: 'organizationName' },
|
||
{ label: 'Code employeur', key: 'employerCode' },
|
||
{ label: 'Répondu par', key: 'userName' },
|
||
{ label: 'Email', key: 'userEmail' },
|
||
],
|
||
detailsCard: {
|
||
title: 'Détails de la réponse',
|
||
rows: [
|
||
{ label: 'ID', key: 'ticketId' },
|
||
{ label: 'Sujet', key: 'ticketSubject' },
|
||
{ label: 'Statut', key: 'ticketStatus' },
|
||
{ label: 'Réponse', key: 'userMessage' },
|
||
]
|
||
}
|
||
}
|
||
};
|
||
|
||
// Register Handlebars helpers
|
||
Handlebars.registerHelper('gt', function(a: any, b: any) {
|
||
return a > b;
|
||
});
|
||
|
||
function replaceVariables(template: string, data: EmailDataV2): string {
|
||
const compiled = Handlebars.compile(template);
|
||
return compiled(data);
|
||
}
|
||
|
||
// Fonction pour traiter les retours à la ligne dans mainMessage
|
||
function processMainMessage(template: string, data: EmailDataV2): string {
|
||
const processed = replaceVariables(template, data);
|
||
// Convertir les \n en <br> pour l'affichage HTML
|
||
return processed.replace(/\n/g, '<br>');
|
||
}
|
||
|
||
export async function renderUniversalEmailV2(config: EmailConfigV2): Promise<{ subject: string; html: string; text: string; }> {
|
||
const templateConfig = EMAIL_TEMPLATES_V2[config.type];
|
||
if (!templateConfig) {
|
||
throw new Error(`Template not found for type: ${config.type}`);
|
||
}
|
||
|
||
// Enrichir les données avec des valeurs par défaut ou statiques
|
||
const data: EmailDataV2 = {
|
||
handlerName: 'Renaud BREVIERE-ABRAHAM',
|
||
supportUrl: 'mailto:paie@odentas.fr',
|
||
...config.data,
|
||
};
|
||
|
||
// Debug log pour les factures
|
||
if (config.type === 'invoice') {
|
||
console.log('[emailTemplateService] Invoice email data:', {
|
||
customMessage: data.customMessage,
|
||
firstName: data.firstName,
|
||
amount: data.amount,
|
||
invoiceNumber: data.invoiceNumber
|
||
});
|
||
}
|
||
|
||
const templateData = {
|
||
subject: replaceVariables(config.subject || templateConfig.subject, data),
|
||
title: replaceVariables(templateConfig.title, data),
|
||
greeting: templateConfig.greeting ? replaceVariables(templateConfig.greeting, data) : undefined,
|
||
mainMessage: processMainMessage(templateConfig.mainMessage, data),
|
||
closingMessage: templateConfig.closingMessage ? replaceVariables(templateConfig.closingMessage, data) : undefined,
|
||
ctaText: templateConfig.ctaText ? replaceVariables(templateConfig.ctaText, data) : undefined,
|
||
footerText: replaceVariables(templateConfig.footerText, data),
|
||
preheaderText: replaceVariables(templateConfig.preheaderText, data),
|
||
textFallback: `${replaceVariables(templateConfig.title, data)} - ${replaceVariables(templateConfig.mainMessage, data)}`,
|
||
ctaUrl: templateConfig.ctaUrl || (config.type === 'invoice' ? 'https://paie.odentas.fr/facturation' : data.ctaUrl),
|
||
logoUrl: 'https://newstaging.odentas.fr/wp-content/uploads/2025/08/Odentas-Logo-Bleu-Fond-Transparent-4-1.png',
|
||
showInfoCard: !!templateConfig.infoCard,
|
||
infoCardRows: templateConfig.infoCard?.map(field => ({
|
||
label: field.label,
|
||
value: data[field.key] ?? '—'
|
||
})),
|
||
showDetailsCard: !!templateConfig.detailsCard,
|
||
detailsCardTitle: templateConfig.detailsCard?.title,
|
||
detailsCardRows: templateConfig.detailsCard?.rows.map(field => ({
|
||
label: field.label,
|
||
value: data[field.key] ?? '—'
|
||
})),
|
||
// Logique pour la carte bancaire conditionnelle
|
||
showBankCard: templateConfig.bankCard ?
|
||
(data[templateConfig.bankCard.conditionKey] === templateConfig.bankCard.conditionValue &&
|
||
(!templateConfig.bankCard.additionalConditionKey ||
|
||
data[templateConfig.bankCard.additionalConditionKey] === templateConfig.bankCard.additionalConditionValue)) : false,
|
||
bankCardTitle: templateConfig.bankCard?.title,
|
||
bankCardRows: templateConfig.bankCard?.rows.map(field => ({
|
||
label: field.label,
|
||
value: replaceVariables(field.value, data)
|
||
})),
|
||
bankCardDisclaimer: templateConfig.bankCard?.disclaimer,
|
||
...templateConfig.colors,
|
||
};
|
||
|
||
const templatePath = join(process.cwd(), 'templates-mails', 'universal-template.html');
|
||
const htmlTemplateSource = readFileSync(templatePath, 'utf-8');
|
||
const handlebarsTemplate = Handlebars.compile(htmlTemplateSource);
|
||
const renderedHtml = handlebarsTemplate(templateData);
|
||
return { subject: templateData.subject, html: renderedHtml, text: templateData.textFallback };
|
||
}
|
||
|
||
export async function sendUniversalEmailV2(config: EmailConfigV2): Promise<string> {
|
||
console.log('🔍 [SES DEBUG] sendUniversalEmailV2 called with:', {
|
||
type: config.type,
|
||
toEmail: config.toEmail,
|
||
ccEmail: config.ccEmail,
|
||
toEmailType: typeof config.toEmail,
|
||
ccEmailType: typeof config.ccEmail,
|
||
subject: config.subject
|
||
});
|
||
|
||
// Validation des emails
|
||
const isValidEmail = (email: string) => {
|
||
return email && typeof email === 'string' && email.includes('@') && email.includes('.') && email.trim().length > 0;
|
||
};
|
||
|
||
// Valider l'email principal
|
||
if (!isValidEmail(config.toEmail)) {
|
||
console.error('🚨 [SES DEBUG] Invalid toEmail detected:', {
|
||
toEmail: config.toEmail,
|
||
type: typeof config.toEmail,
|
||
length: config.toEmail?.length,
|
||
isEmpty: config.toEmail === '',
|
||
isNull: config.toEmail === null,
|
||
isUndefined: config.toEmail === undefined
|
||
});
|
||
throw new Error(`Invalid email address: "${config.toEmail}"`);
|
||
}
|
||
|
||
// Valider l'email CC s'il existe
|
||
if (config.ccEmail && !isValidEmail(config.ccEmail)) {
|
||
console.warn(`Invalid CC email address, ignoring: "${config.ccEmail}"`);
|
||
config.ccEmail = undefined; // Supprimer l'email CC invalide
|
||
}
|
||
|
||
console.log('🔍 [SES DEBUG] Emails validated, proceeding with send:', {
|
||
toEmail: config.toEmail,
|
||
ccEmail: config.ccEmail
|
||
});
|
||
|
||
// Rendre l'email pour obtenir HTML/TXT et sujet
|
||
const rendered = await renderUniversalEmailV2(config);
|
||
|
||
// Créer le log d'email AVANT l'envoi
|
||
const logId = await emailLogger.logEmail({
|
||
senderEmail: process.env.AWS_SES_FROM || 'paie@odentas.fr',
|
||
recipientEmail: config.toEmail,
|
||
ccEmails: config.ccEmail ? [config.ccEmail] : undefined,
|
||
subject: rendered.subject,
|
||
htmlContent: rendered.html,
|
||
textContent: rendered.text,
|
||
emailType: config.type as EmailType,
|
||
templateName: 'universal-v2',
|
||
templateData: config.data,
|
||
emailStatus: 'sending',
|
||
organizationId: typeof config.data.organizationId === 'string' ? config.data.organizationId : undefined,
|
||
contractId: typeof config.data.contractId === 'string' ? config.data.contractId : undefined,
|
||
tags: {
|
||
template_version: 'v2',
|
||
email_system: 'universal'
|
||
},
|
||
context: {
|
||
subject_preview: rendered.subject.substring(0, 100),
|
||
has_cta: !!config.data.ctaUrl,
|
||
data_keys: Object.keys(config.data)
|
||
}
|
||
});
|
||
|
||
// Configuration SES
|
||
const sesClient = new SESClient({ region: process.env.AWS_REGION || 'eu-west-3' });
|
||
|
||
// Nettoyer l'adresse source pour AWS SES
|
||
const rawSource = process.env.AWS_SES_FROM || 'paie@odentas.fr';
|
||
const cleanSource = rawSource.replace(/^["']|["']$/g, ''); // Supprimer les guillemets externes
|
||
|
||
const params = {
|
||
Source: cleanSource,
|
||
Destination: {
|
||
ToAddresses: [config.toEmail] as string[],
|
||
...(config.ccEmail ? { CcAddresses: [config.ccEmail] as string[] } : {})
|
||
},
|
||
Message: {
|
||
Subject: { Data: rendered.subject, Charset: "UTF-8" },
|
||
Body: {
|
||
Html: { Data: rendered.html, Charset: "UTF-8" },
|
||
Text: { Data: rendered.text, Charset: "UTF-8" }
|
||
}
|
||
}
|
||
};
|
||
|
||
console.log('🔍 [SES DEBUG] Full SES params before send:', {
|
||
SourceRaw: process.env.AWS_SES_FROM,
|
||
SourceCleaned: params.Source,
|
||
ToAddresses: params.Destination.ToAddresses,
|
||
CcAddresses: params.Destination.CcAddresses || 'none',
|
||
Subject: params.Message.Subject.Data,
|
||
awsRegion: process.env.AWS_REGION || 'eu-west-3',
|
||
paramsDestination: JSON.stringify(params.Destination),
|
||
allAddresses: [...params.Destination.ToAddresses, ...(params.Destination.CcAddresses || [])]
|
||
});
|
||
|
||
try {
|
||
const command = new SendEmailCommand(params);
|
||
const response = await sesClient.send(command);
|
||
const messageId = (response as any)?.MessageId || '';
|
||
|
||
console.log(`Email sent successfully to ${config.toEmail} - Type: ${config.type} - MessageId: ${messageId}`);
|
||
|
||
// Mettre à jour le log avec le succès et l'ID SES
|
||
if (logId) {
|
||
await emailLogger.updateEmailLog(logId, {
|
||
emailStatus: 'sent',
|
||
sesMessageId: messageId,
|
||
sentAt: new Date()
|
||
});
|
||
}
|
||
|
||
return messageId;
|
||
} catch (error) {
|
||
console.error("Error sending email via SES:", error);
|
||
|
||
// Mettre à jour le log avec l'erreur
|
||
if (logId) {
|
||
await emailLogger.updateEmailLog(logId, {
|
||
emailStatus: 'failed',
|
||
failureReason: error instanceof Error ? error.message : 'Unknown error'
|
||
});
|
||
}
|
||
|
||
throw error;
|
||
}
|
||
} |