- Créé sendInvitationWithActivationEmail() pour unifier les invitations - Modifié /api/staff/users/invite pour utiliser generateLink + email - Modifié /api/access/nouveau pour envoyer email d'activation - Modifié /api/access POST pour remplacer pending_invites par système direct - Template account-activation mis à jour : * Titre 'Activez votre compte' * Encart avec infos : invitant (statut), organisation, niveau d'accès * Message de contact formaté comme autres emails * Renommage 'Odentas Paie' → 'Espace Paie Odentas' - Fix page /activate : délai 100ms pour hash fragment + redirection 1s - Liens d'activation forcés vers paie.odentas.fr (tests depuis localhost) - Messages UI cohérents : 'Invitation envoyée' au lieu de 'Compte créé'
650 lines
No EOL
16 KiB
TypeScript
650 lines
No EOL
16 KiB
TypeScript
// lib/emailMigrationHelpers.ts
|
||
import { sendUniversalEmailV2, EmailDataV2 } from "./emailTemplateService";
|
||
|
||
/**
|
||
* Envoie un email d'invitation avec activation de compte (système unifié)
|
||
* Utilisé pour les nouvelles invitations d'utilisateurs (staff ou clients)
|
||
*/
|
||
export async function sendInvitationWithActivationEmail(
|
||
toEmail: string,
|
||
data: {
|
||
firstName?: string;
|
||
organizationName: string;
|
||
activationUrl: string;
|
||
role?: string;
|
||
inviterName?: string;
|
||
inviterStatus?: string;
|
||
}
|
||
) {
|
||
// Créer le champ combiné "Nom (Statut)"
|
||
const inviterNameWithStatus = data.inviterName
|
||
? data.inviterStatus
|
||
? `${data.inviterName} (${data.inviterStatus})`
|
||
: data.inviterName
|
||
: undefined;
|
||
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
organizationName: data.organizationName,
|
||
userEmail: toEmail,
|
||
platform: 'Espace Paie Odentas',
|
||
ctaUrl: data.activationUrl,
|
||
role: data.role,
|
||
inviterNameWithStatus,
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'account-activation',
|
||
toEmail,
|
||
subject: `Activez votre compte – ${data.organizationName}`,
|
||
data: emailData,
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Migration helper pour les emails d'invitation (ancienne version, maintenir pour compatibilité)
|
||
*/
|
||
export async function sendInvitationEmail(
|
||
toEmail: string,
|
||
data: {
|
||
firstName: string;
|
||
organizationName: string;
|
||
email: string;
|
||
role: string;
|
||
actionLink: string;
|
||
}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
organizationName: data.organizationName,
|
||
email: data.email,
|
||
role: data.role,
|
||
ctaUrl: data.actionLink
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'invitation',
|
||
toEmail,
|
||
subject: 'Votre accès à l\'Espace Paie Odentas',
|
||
data: emailData
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Migration helper pour les emails de facture
|
||
*/
|
||
export async function sendInvoiceEmail(
|
||
toEmail: string,
|
||
ccEmail: string | null,
|
||
data: {
|
||
firstName: string;
|
||
organizationName?: string;
|
||
employerCode?: string;
|
||
invoiceNumber: string;
|
||
amount: string;
|
||
customMessage: string;
|
||
ctaUrl?: string;
|
||
invoiceDate?: string;
|
||
sepaDate?: string;
|
||
paymentMethod?: string;
|
||
invoiceType?: string;
|
||
siteName?: string;
|
||
}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
organizationName: data.organizationName,
|
||
employerCode: data.employerCode,
|
||
invoiceNumber: data.invoiceNumber,
|
||
amount: data.amount,
|
||
customMessage: data.customMessage,
|
||
invoiceDate: data.invoiceDate,
|
||
sepaDate: data.sepaDate,
|
||
ctaUrl: data.ctaUrl,
|
||
paymentMethod: data.paymentMethod,
|
||
invoiceType: data.invoiceType,
|
||
siteName: data.siteName
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'invoice',
|
||
toEmail,
|
||
ccEmail: ccEmail || undefined,
|
||
subject: `Votre facture n°${data.invoiceNumber} est disponible`,
|
||
data: emailData
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Migration helper pour les emails de signature électronique
|
||
*/
|
||
export async function sendSignatureRequestEmail(
|
||
toEmail: string,
|
||
data: {
|
||
firstName?: string;
|
||
organizationName: string;
|
||
employerCode?: string;
|
||
employeeName?: string;
|
||
documentType?: string;
|
||
contractReference?: string;
|
||
status?: string;
|
||
signatureLink: string;
|
||
}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
organizationName: data.organizationName,
|
||
employerCode: data.employerCode,
|
||
employeeName: data.employeeName,
|
||
documentType: data.documentType,
|
||
contractReference: data.contractReference,
|
||
status: data.status || 'En attente',
|
||
ctaUrl: data.signatureLink
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'signature-request',
|
||
toEmail,
|
||
subject: 'Demande de signature électronique',
|
||
data: emailData
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Demande de signature pour l'employeur (style universel)
|
||
*/
|
||
export async function sendEmployerSignatureRequestEmail(
|
||
toEmail: string,
|
||
data: {
|
||
firstName?: string;
|
||
organizationName: string;
|
||
employerCode?: string;
|
||
employeeName?: string;
|
||
documentType?: string;
|
||
contractReference?: string;
|
||
status?: string;
|
||
signatureLink: string;
|
||
}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
organizationName: data.organizationName,
|
||
employerCode: data.employerCode,
|
||
employeeName: data.employeeName,
|
||
documentType: data.documentType,
|
||
contractReference: data.contractReference,
|
||
status: data.status || 'En attente',
|
||
ctaUrl: data.signatureLink
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'signature-request-employer',
|
||
toEmail,
|
||
subject: 'Demande de signature électronique',
|
||
data: emailData
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Demande de signature pour le salarié (style universel)
|
||
*/
|
||
export async function sendEmployeeSignatureRequestEmail(
|
||
toEmail: string,
|
||
data: {
|
||
firstName?: string;
|
||
organizationName: string;
|
||
employerCode?: string;
|
||
documentType?: string;
|
||
contractReference?: string;
|
||
status?: string;
|
||
signatureLink: string;
|
||
}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
organizationName: data.organizationName,
|
||
employerCode: data.employerCode,
|
||
documentType: data.documentType,
|
||
contractReference: data.contractReference,
|
||
status: data.status || 'En attente',
|
||
ctaUrl: data.signatureLink
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'signature-request-employee',
|
||
toEmail,
|
||
subject: 'Demande de signature électronique',
|
||
data: emailData
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Migration helper pour les emails de rappel de signature
|
||
*/
|
||
export async function sendSignatureReminderEmail(
|
||
toEmail: string,
|
||
data: {
|
||
firstName: string;
|
||
organizationName: string;
|
||
contractReference: string;
|
||
signatureLink: string;
|
||
}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
organizationName: data.organizationName,
|
||
contractReference: data.contractReference,
|
||
ctaUrl: data.signatureLink
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'notification',
|
||
toEmail,
|
||
subject: `[Rappel] Signez votre contrat ${data.organizationName}`,
|
||
data: emailData
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Migration helper pour les emails de réinitialisation de mot de passe
|
||
*/
|
||
export async function sendPasswordResetEmail(
|
||
toEmail: string,
|
||
data: {
|
||
resetLink: string;
|
||
}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
ctaUrl: data.resetLink
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'notification',
|
||
toEmail,
|
||
subject: 'Réinitialisation de votre mot de passe',
|
||
data: emailData
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Migration helper pour les emails OTP
|
||
*/
|
||
export async function sendOTPEmail(
|
||
toEmail: string,
|
||
data: {
|
||
code: string;
|
||
}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
alertMessage: `Votre code de vérification : <strong style="font-size: 24px; color: #004aad;">${data.code}</strong>`
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'notification',
|
||
toEmail,
|
||
subject: 'Votre code de vérification',
|
||
data: emailData
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Migration helper pour les emails d'accès accordé
|
||
*/
|
||
export async function sendAccessGrantedEmail(
|
||
toEmail: string,
|
||
data: {
|
||
firstName: string;
|
||
organizationName: string;
|
||
role: string;
|
||
}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
organizationName: data.organizationName,
|
||
role: data.role
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'notification',
|
||
toEmail,
|
||
subject: 'Accès accordé',
|
||
data: emailData
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Migration helper pour les emails d'accès révoqué
|
||
*/
|
||
export async function sendAccessRevokedEmail(
|
||
toEmail: string,
|
||
data: {
|
||
firstName: string;
|
||
organizationName: string;
|
||
userEmail?: string;
|
||
revocationDate?: string;
|
||
revokedBy?: string;
|
||
reason?: string;
|
||
}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
organizationName: data.organizationName,
|
||
userEmail: data.userEmail || toEmail,
|
||
revocationDate: data.revocationDate,
|
||
revokedBy: data.revokedBy,
|
||
reason: data.reason,
|
||
status: 'Révoqué',
|
||
ctaUrl: 'mailto:paie@odentas.fr'
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'access-revoked',
|
||
toEmail,
|
||
subject: 'Accès révoqué',
|
||
data: emailData
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Activation de compte (via lien d'invitation Supabase)
|
||
*/
|
||
export async function sendAccountActivationEmail(
|
||
toEmail: string,
|
||
data: {
|
||
firstName?: string;
|
||
organizationName: string;
|
||
activationUrl: string;
|
||
}
|
||
) {
|
||
// Délégué vers le helper unifié en conservant la compatibilité
|
||
await sendInvitationWithActivationEmail(toEmail, {
|
||
firstName: data.firstName,
|
||
organizationName: data.organizationName,
|
||
activationUrl: data.activationUrl,
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Habilitation modifiée (rôle changé)
|
||
*/
|
||
export async function sendAccessUpdatedEmail(
|
||
toEmail: string,
|
||
data: {
|
||
firstName?: string;
|
||
organizationName: string;
|
||
oldRole?: string;
|
||
newRole: string;
|
||
updatedBy?: string;
|
||
updateDate?: string;
|
||
ctaUrl?: string;
|
||
}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
organizationName: data.organizationName,
|
||
userEmail: toEmail,
|
||
oldRole: data.oldRole,
|
||
newRole: data.newRole,
|
||
updatedBy: data.updatedBy,
|
||
updateDate: data.updateDate,
|
||
ctaUrl: data.ctaUrl || `${process.env.NEXT_PUBLIC_BASE_URL || 'https://paie.odentas.fr'}/vos-acces`,
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'access-updated',
|
||
toEmail,
|
||
subject: `Vos habilitations ont été modifiées – ${data.organizationName}`,
|
||
data: emailData,
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Sécurité: mot de passe créé (premier mot de passe)
|
||
*/
|
||
export async function sendPasswordCreatedEmail(
|
||
toEmail: string,
|
||
data: {
|
||
firstName?: string;
|
||
eventDate?: string;
|
||
platform?: string;
|
||
} = {}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
userEmail: toEmail,
|
||
status: 'Créé',
|
||
eventDate: data.eventDate || new Date().toLocaleString('fr-FR'),
|
||
platform: data.platform || 'Odentas Paie',
|
||
ctaUrl: `${process.env.NEXT_PUBLIC_BASE_URL || 'https://paie.odentas.fr'}/securite`,
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'password-created',
|
||
toEmail,
|
||
subject: 'Mot de passe créé sur votre compte',
|
||
data: emailData,
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Sécurité: mot de passe modifié
|
||
*/
|
||
export async function sendPasswordChangedEmail(
|
||
toEmail: string,
|
||
data: {
|
||
firstName?: string;
|
||
eventDate?: string;
|
||
platform?: string;
|
||
} = {}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
userEmail: toEmail,
|
||
status: 'Modifié',
|
||
eventDate: data.eventDate || new Date().toLocaleString('fr-FR'),
|
||
platform: data.platform || 'Espace Paie Odentas',
|
||
ctaUrl: 'https://paie.odentas.fr',
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'password-changed',
|
||
toEmail,
|
||
subject: 'Votre mot de passe a été modifié',
|
||
data: emailData,
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Sécurité: 2FA activée
|
||
*/
|
||
export async function sendTwoFaEnabledEmail(
|
||
toEmail: string,
|
||
data: { firstName?: string; eventDate?: string; platform?: string } = {}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
userEmail: toEmail,
|
||
status: '2FA désactivée',
|
||
eventDate: data.eventDate || new Date().toLocaleString('fr-FR'),
|
||
platform: data.platform || 'Odentas Paie',
|
||
ctaUrl: `${process.env.NEXT_PUBLIC_BASE_URL || 'https://paie.odentas.fr'}/compte/securite`,
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'twofa-disabled',
|
||
toEmail,
|
||
subject: '2FA désactivée sur votre compte',
|
||
data: emailData,
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Migration helper pour les emails de demande de mandat SEPA
|
||
*/
|
||
export async function sendSepaMandateRequestEmail(
|
||
toEmail: string,
|
||
ccEmail: string | null,
|
||
data: {
|
||
firstName: string;
|
||
organizationName: string;
|
||
employerCode?: string;
|
||
mandateLink: string;
|
||
}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
organizationName: data.organizationName,
|
||
employerCode: data.employerCode,
|
||
handlerName: 'Renaud BREVIERE-ABRAHAM',
|
||
ctaUrl: data.mandateLink
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'sepa-mandate-request',
|
||
toEmail,
|
||
ccEmail: ccEmail || undefined,
|
||
subject: 'Signez votre mandat de prélèvement SEPA',
|
||
data: emailData
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Support: Notification interne de création de ticket
|
||
*/
|
||
export async function sendInternalTicketCreatedEmail(
|
||
data: {
|
||
ticketId: string;
|
||
ticketSubject: string;
|
||
ticketCategory: string;
|
||
ticketMessage: string;
|
||
userName: string;
|
||
userEmail: string;
|
||
organizationName?: string;
|
||
employerCode?: string;
|
||
}
|
||
) {
|
||
// Convertir les sauts de ligne en <br> pour l'affichage HTML
|
||
const formattedMessage = data.ticketMessage.replace(/\n/g, '<br>');
|
||
|
||
const emailData: EmailDataV2 = {
|
||
ticketId: data.ticketId,
|
||
ticketSubject: data.ticketSubject,
|
||
ticketCategory: data.ticketCategory,
|
||
ticketMessage: formattedMessage,
|
||
userName: data.userName,
|
||
userEmail: data.userEmail,
|
||
organizationName: data.organizationName || 'Non définie',
|
||
employerCode: data.employerCode || 'Non défini',
|
||
ctaUrl: `${process.env.NEXT_PUBLIC_BASE_URL || 'https://paie.odentas.fr'}/staff/tickets/${data.ticketId}`,
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'support-ticket-created',
|
||
toEmail: 'paie@odentas.fr',
|
||
subject: `[SUPPORT] Nouveau ticket : ${data.ticketSubject}`,
|
||
data: emailData,
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Support: Notification interne de réponse utilisateur
|
||
*/
|
||
export async function sendInternalTicketReplyEmail(
|
||
data: {
|
||
ticketId: string;
|
||
ticketSubject: string;
|
||
ticketStatus: string;
|
||
userMessage: string;
|
||
userName: string;
|
||
userEmail: string;
|
||
organizationName?: string;
|
||
employerCode?: string;
|
||
}
|
||
) {
|
||
// Convertir les sauts de ligne en <br> pour l'affichage HTML
|
||
const formattedMessage = data.userMessage.replace(/\n/g, '<br>');
|
||
|
||
const emailData: EmailDataV2 = {
|
||
ticketId: data.ticketId,
|
||
ticketSubject: data.ticketSubject,
|
||
ticketStatus: data.ticketStatus,
|
||
userMessage: formattedMessage,
|
||
userName: data.userName,
|
||
userEmail: data.userEmail,
|
||
organizationName: data.organizationName || 'Non définie',
|
||
employerCode: data.employerCode || 'Non défini',
|
||
ctaUrl: `${process.env.NEXT_PUBLIC_BASE_URL || 'https://paie.odentas.fr'}/staff/tickets/${data.ticketId}`,
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'support-ticket-reply',
|
||
toEmail: 'paie@odentas.fr',
|
||
subject: `[SUPPORT] Réponse au ticket : ${data.ticketSubject}`,
|
||
data: emailData,
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Support: Notification de réponse à un ticket
|
||
*/
|
||
export async function sendSupportReplyEmail(
|
||
toEmail: string,
|
||
data: {
|
||
firstName?: string;
|
||
ticketId: string;
|
||
ticketSubject: string;
|
||
staffName: string;
|
||
staffMessage: string;
|
||
organizationName?: string;
|
||
employerCode?: string;
|
||
}
|
||
) {
|
||
// Formater le nom du staff : première lettre en majuscule + [Staff Odentas]
|
||
const formattedStaffName = data.staffName.charAt(0).toUpperCase() + data.staffName.slice(1) + ' [Staff Odentas]';
|
||
|
||
// Convertir les sauts de ligne en <br> pour l'affichage HTML
|
||
const formattedMessage = data.staffMessage.replace(/\n/g, '<br>');
|
||
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
ticketId: data.ticketId,
|
||
ticketSubject: data.ticketSubject,
|
||
staffName: formattedStaffName,
|
||
staffMessage: formattedMessage,
|
||
organizationName: data.organizationName || 'Non définie',
|
||
employerCode: data.employerCode || 'Non défini',
|
||
handlerName: 'Renaud BREVIERE-ABRAHAM',
|
||
ctaUrl: `${process.env.NEXT_PUBLIC_BASE_URL || 'https://paie.odentas.fr'}/support/${data.ticketId}`,
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'support-reply',
|
||
toEmail,
|
||
subject: `Nouvelle réponse à votre ticket: ${data.ticketSubject}`,
|
||
data: emailData,
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Sécurité: 2FA désactivée
|
||
*/
|
||
export async function sendTwoFaDisabledEmail(
|
||
toEmail: string,
|
||
data: { firstName?: string; eventDate?: string; platform?: string } = {}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
userEmail: toEmail,
|
||
status: '2FA désactivée',
|
||
eventDate: data.eventDate || new Date().toLocaleString('fr-FR'),
|
||
platform: data.platform || 'Odentas Paie',
|
||
ctaUrl: `${process.env.NEXT_PUBLIC_BASE_URL || 'https://paie.odentas.fr'}/compte/securite`,
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'twofa-disabled',
|
||
toEmail,
|
||
subject: '2FA désactivée sur votre compte',
|
||
data: emailData,
|
||
});
|
||
} |