feat(#110): refus dossier propagé aux co-parents + mail aligné

- `refuseUser` charge tous les parents du même numéro de dossier, leur
  applique le même token_reprise (7 jours), trace une validation refus
  par compte et envoie un mail de refus à chacun. Le refus devient une
  action dossier : peu importe quel parent ouvre ensuite le lien.
- Échec d'envoi mail loggé sans bloquer les autres destinataires.
- Mail de refus aligné sur le look des autres templates (titre/bouton
  verts, bloc motif gestionnaire en encart, échappement HTML cohérent,
  appUrl normalisée).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-13 19:44:30 +02:00
co-authored by Cursor
parent b6e83bf9ef
commit 9eb62ddd13
2 changed files with 72 additions and 33 deletions
+16 -8
View File
@@ -214,21 +214,29 @@ export class MailService {
token: string,
): Promise<void> {
const appName = this.configService.get<string>('app_name', "P'titsPas");
const appUrl = this.configService.get<string>('app_url', 'https://app.ptits-pas.fr');
const appUrl = (this.configService.get<string>('app_url', 'https://app.ptits-pas.fr') || '').replace(/\/+$/, '');
const repriseLink = `${appUrl}/reprise?token=${encodeURIComponent(token)}`;
const safe = (s: string) =>
(s || '')
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
const subject = `Votre dossier compléments demandés`;
const commentBlock = comment
? `<p><strong>Message du gestionnaire :</strong></p><p>${comment.replace(/</g, '&lt;').replace(/>/g, '&gt;')}</p>`
: '';
const commentText = safe(comment || 'Le gestionnaire vous demande de compléter votre dossier avant une nouvelle validation.');
const html = `
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<h2 style="color: #333;">Bonjour ${prenom} ${nom},</h2>
<p>Votre dossier d'inscription sur <strong>${appName}</strong> n'a pas pu être validé en l'état.</p>
${commentBlock}
<h2 style="color: #4CAF50;">Bonjour ${safe(prenom)} ${safe(nom)},</h2>
<p>Votre dossier d'inscription sur <strong>${safe(appName)}</strong> n'a pas pu être validé en l'état.</p>
<div style="background-color: #F4FBF5; border-left: 4px solid #4CAF50; padding: 12px 16px; margin: 20px 0;">
<p style="margin: 0 0 8px 0;"><strong>Message du gestionnaire :</strong></p>
<p style="margin: 0;">${commentText}</p>
</div>
<p>Vous pouvez corriger les éléments indiqués et soumettre à nouveau votre dossier en cliquant sur le lien ci-dessous.</p>
<div style="text-align: center; margin: 30px 0;">
<a href="${repriseLink}" style="background-color: #2196F3; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; font-weight: bold;">Reprendre mon dossier</a>
<a href="${repriseLink}" style="background-color: #4CAF50; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; font-weight: bold;">Reprendre mon dossier</a>
</div>
<p style="color: #666; font-size: 12px;">Ce lien est valable 7 jours. Si vous n'avez pas demandé cette reprise, vous pouvez ignorer cet email.</p>
<hr style="border: 1px solid #eee; margin: 20px 0;">