feat(admin): add email change flow with confirmation for users

- add `ConfirmEmailChangePage.client.js` for email change token confirmation
- add `emailChange.js` core utility to generate and verify email change tokens
- add `EmailChangeConfirmEmail.js` and `EmailChangeNotifyEmail.js` email templates
- update `UserEditModal` to handle email changes with password verification for self-edits
- update `ProfilePage` to support email change initiation
- update `UsersPage` to pass `currentUserId` to `UserEditModal`
- add email change API endpoints in `auth/api.js` and `auth/email.js`
- register `ConfirmEmailChangePage` in `AdminPage.client.js`
This commit is contained in:
2026-04-24 15:04:36 -04:00
parent f31b97cff4
commit 66c862cf73
10 changed files with 623 additions and 21 deletions
@@ -0,0 +1,63 @@
import { Section, Text } from "@react-email/components";
import { BaseLayout } from "@zen/core/email/templates";
const VARIANTS = {
pending: {
preview: (name) => `Demande de modification de courriel ${name}`,
title: 'Demande de modification de courriel',
body: (name) => `Une demande de modification de l'adresse courriel associée à votre compte ${name} a été initiée.`,
note: "Si vous n'êtes pas à l'origine de cette demande, contactez immédiatement notre équipe de support. Votre adresse actuelle reste active jusqu'à confirmation.",
},
changed: {
preview: (name) => `Votre adresse courriel a été modifiée ${name}`,
title: 'Adresse courriel modifiée',
body: (name) => `L'adresse courriel de votre compte ${name} a été modifiée par un administrateur.`,
note: "Si vous n'êtes pas à l'origine de cette modification, contactez immédiatement notre équipe de support.",
},
admin_new: {
preview: (name) => `Votre compte est maintenant associé à cette adresse ${name}`,
title: 'Adresse courriel associée à votre compte',
body: (name) => `Votre adresse courriel est maintenant associée à un compte ${name}. Cette modification a été effectuée par un administrateur.`,
note: "Si vous n'avez pas été informé de cette modification, contactez notre équipe de support.",
},
};
export const EmailChangeNotifyEmail = ({ oldEmail, newEmail, variant = 'changed', companyName }) => {
const msg = VARIANTS[variant] || VARIANTS.changed;
return (
<BaseLayout
preview={msg.preview(companyName)}
title={msg.title}
companyName={companyName}
supportSection={true}
>
<Text className="text-[14px] leading-[24px] text-neutral-600 mt-[4px] mb-[24px]">
{msg.body(companyName)}
</Text>
<Section style={{ border: '1px solid #E5E5E5' }} className="bg-neutral-100 rounded-[12px] p-[20px] my-[24px]">
{oldEmail && variant !== 'admin_new' && (
<>
<Text className="text-[12px] font-medium text-neutral-400 m-0 mb-[4px] uppercase tracking-wider">
Ancienne adresse
</Text>
<Text className="text-[14px] font-medium text-neutral-900 m-0 mb-[12px]">
{oldEmail}
</Text>
</>
)}
<Text className="text-[12px] font-medium text-neutral-400 m-0 mb-[4px] uppercase tracking-wider">
Nouvelle adresse
</Text>
<Text className="text-[14px] font-medium text-neutral-900 m-0">
{newEmail}
</Text>
</Section>
<Text className="text-[12px] leading-[20px] text-neutral-400 m-0">
{msg.note}
</Text>
</BaseLayout>
);
};