refactor(mail): #28 un seul template email parent (principal + co-parent)

Made-with: Cursor
This commit is contained in:
MARTIN Julien 2026-04-17 18:37:47 +02:00
parent 94d9428c43
commit 7590c95f06
6 changed files with 9 additions and 39 deletions

View File

@ -44,8 +44,7 @@ describe('MailService (ticket #28)', () => {
}); });
it.each<[ValidationAccountEmailKind, string]>([ it.each<[ValidationAccountEmailKind, string]>([
['parent_primary', 'Compte validé'], ['parent', 'Compte parent validé'],
['parent_coparent', 'Co-parent'],
['am', 'Inscription validée'], ['am', 'Inscription validée'],
])('sendValidatedAccountPasswordSetupEmail (%s) utilise Handlebars + lien token', async (kind, subjectPart) => { ])('sendValidatedAccountPasswordSetupEmail (%s) utilise Handlebars + lien token', async (kind, subjectPart) => {
const token = '11111111-2222-3333-4444-555555555555'; const token = '11111111-2222-3333-4444-555555555555';

View File

@ -5,7 +5,7 @@ import * as Handlebars from 'handlebars';
import { AppConfigService } from '../config/config.service'; import { AppConfigService } from '../config/config.service';
/** Ticket #28 — quel template d'email envoyer après validation de compte */ /** Ticket #28 — quel template d'email envoyer après validation de compte */
export type ValidationAccountEmailKind = 'parent_primary' | 'parent_coparent' | 'am'; export type ValidationAccountEmailKind = 'parent' | 'am';
@Injectable() @Injectable()
export class MailService { export class MailService {
@ -18,8 +18,7 @@ export class MailService {
private templateBasename(kind: ValidationAccountEmailKind): string { private templateBasename(kind: ValidationAccountEmailKind): string {
const map: Record<ValidationAccountEmailKind, string> = { const map: Record<ValidationAccountEmailKind, string> = {
parent_primary: 'account-validated-parent-primary', parent: 'account-validated-parent',
parent_coparent: 'account-validated-parent-coparent',
am: 'account-validated-am', am: 'account-validated-am',
}; };
return map[kind]; return map[kind];
@ -67,8 +66,7 @@ export class MailService {
const html = this.getCompiledTemplate(kind)(data) as string; const html = this.getCompiledTemplate(kind)(data) as string;
const subjects: Record<ValidationAccountEmailKind, string> = { const subjects: Record<ValidationAccountEmailKind, string> = {
parent_primary: `${appName} — Compte validé : créez votre mot de passe`, parent: `${appName} — Compte parent validé : créez votre mot de passe`,
parent_coparent: `${appName} — Co-parent : créez votre mot de passe`,
am: `${appName} — Inscription validée : créez votre mot de passe`, am: `${appName} — Inscription validée : créez votre mot de passe`,
}; };

View File

@ -1,14 +0,0 @@
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<h2 style="color: #4CAF50;">Bonjour {{prenom}} {{nom}},</h2>
<p>Le dossier d'inscription sur <strong>{{appName}}</strong> auquel vous êtes rattaché·e en tant que <strong>co-parent</strong> a été <strong>validé</strong>.</p>
{{#if numeroDossier}}
<p><strong>Numéro de dossier :</strong> {{numeroDossier}}</p>
{{/if}}
<p>Vous devez maintenant <strong>définir votre mot de passe</strong> pour accéder à l'application avec votre propre compte.</p>
<div style="text-align: center; margin: 30px 0;">
<a href="{{{createPasswordUrl}}}" style="background-color: #4CAF50; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; font-weight: bold;">Créer mon mot de passe</a>
</div>
<p style="color: #666; font-size: 13px;">Si le bouton ne fonctionne pas, copiez ce lien dans votre navigateur :<br /><span style="word-break: break-all;">{{{createPasswordUrl}}}</span></p>
<hr style="border: 1px solid #eee; margin: 20px 0;" />
<p style="color: #666; font-size: 12px;">Cet email a été envoyé automatiquement par {{appName}}. Merci de ne pas y répondre.</p>
</div>

View File

@ -1,10 +1,10 @@
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;"> <div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<h2 style="color: #4CAF50;">Bonjour {{prenom}} {{nom}},</h2> <h2 style="color: #4CAF50;">Bonjour {{prenom}} {{nom}},</h2>
<p>Votre demande d'inscription sur <strong>{{appName}}</strong> a été <strong>validée</strong>.</p> <p>Votre compte parent sur <strong>{{appName}}</strong> a été <strong>validé</strong>.</p>
{{#if numeroDossier}} {{#if numeroDossier}}
<p><strong>Numéro de dossier :</strong> {{numeroDossier}}</p> <p><strong>Numéro de dossier :</strong> {{numeroDossier}}</p>
{{/if}} {{/if}}
<p>En tant que <strong>demandeur principal</strong>, vous devez maintenant <strong>définir votre mot de passe</strong> pour accéder à l'application.</p> <p>Pour accéder à l'application, veuillez <strong>définir votre mot de passe</strong> en cliquant sur le bouton ci-dessous.</p>
<div style="text-align: center; margin: 30px 0;"> <div style="text-align: center; margin: 30px 0;">
<a href="{{{createPasswordUrl}}}" style="background-color: #4CAF50; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; font-weight: bold;">Créer mon mot de passe</a> <a href="{{{createPasswordUrl}}}" style="background-color: #4CAF50; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; font-weight: bold;">Créer mon mot de passe</a>
</div> </div>

View File

@ -286,20 +286,8 @@ export class UserService {
savedUser.token_creation_mdp_expire_le = exp; savedUser.token_creation_mdp_expire_le = exp;
await this.usersRepository.save(savedUser); await this.usersRepository.save(savedUser);
let kind: ValidationAccountEmailKind = 'am'; const kind: ValidationAccountEmailKind =
if (savedUser.role === RoleType.ASSISTANTE_MATERNELLE) { savedUser.role === RoleType.ASSISTANTE_MATERNELLE ? 'am' : 'parent';
kind = 'am';
} else if (savedUser.numero_dossier) {
const parentsDossier = await this.usersRepository.find({
where: { numero_dossier: savedUser.numero_dossier, role: RoleType.PARENT },
order: { cree_le: 'ASC' },
});
const premier = parentsDossier[0];
kind =
premier && premier.id !== savedUser.id ? 'parent_coparent' : 'parent_primary';
} else {
kind = 'parent_primary';
}
await this.mailService.sendValidatedAccountPasswordSetupEmail( await this.mailService.sendValidatedAccountPasswordSetupEmail(
{ {

View File

@ -600,11 +600,10 @@ Installer et configurer Nodemailer pour l'envoi d'emails.
Créer les templates d'emails pour la validation des comptes (avec lien création MDP). Créer les templates d'emails pour la validation des comptes (avec lien création MDP).
**Tâches** : **Tâches** :
- [x] Template Parent 1 (création MDP) — `account-validated-parent-primary.hbs` - [x] Template **Parent** (création MDP, parent principal ou co-parent) — `account-validated-parent.hbs`
- [x] URL app : Lire depuis `ConfigService.get('app_url')` (lien `/create-password?token=…`) - [x] URL app : Lire depuis `ConfigService.get('app_url')` (lien `/create-password?token=…`)
- [x] Nom app : Lire depuis `ConfigService.get('app_name')` - [x] Nom app : Lire depuis `ConfigService.get('app_name')`
- [x] Expéditeur : via `sendEmail` (email_from_name / email_from_address) - [x] Expéditeur : via `sendEmail` (email_from_name / email_from_address)
- [x] Template Parent 2 (co-parent création MDP) — `account-validated-parent-coparent.hbs`
- [x] Template AM (création MDP) — `account-validated-am.hbs` - [x] Template AM (création MDP) — `account-validated-am.hbs`
- [x] Intégration Handlebars + copie `.hbs` dans `dist` (`nest-cli.json` assets) - [x] Intégration Handlebars + copie `.hbs` dans `dist` (`nest-cli.json` assets)
- [x] Envoi déclenché dans `UserService.validateUser` (parent / AM sans mot de passe) + renouvellement expiration token - [x] Envoi déclenché dans `UserService.validateUser` (parent / AM sans mot de passe) + renouvellement expiration token