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,45 @@
import { Button, Section, Text, Link } from "@react-email/components";
import { BaseLayout } from "@zen/core/email/templates";
export const EmailChangeConfirmEmail = ({ confirmUrl, newEmail, companyName }) => (
<BaseLayout
preview={`Confirmez votre nouvelle adresse courriel ${companyName}`}
title="Confirmez votre nouvelle adresse courriel"
companyName={companyName}
supportSection={true}
>
<Text className="text-[14px] leading-[24px] text-neutral-600 mt-[4px] mb-[24px]">
Une demande de modification d'adresse courriel a été effectuée sur votre compte{' '}
<span className="font-medium text-neutral-900">{companyName}</span>. Cliquez sur le bouton ci-dessous pour confirmer votre nouvelle adresse.
</Text>
<Section style={{ border: '1px solid #E5E5E5' }} className="bg-neutral-100 rounded-[12px] p-[20px] my-[24px]">
<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>
<Section className="mt-[28px] mb-[32px]">
<Button
href={confirmUrl}
className="bg-neutral-900 rounded-[8px] text-white font-medium px-[20px] py-[11px] no-underline text-[13px]"
>
Confirmer mon adresse courriel
</Button>
</Section>
<Text className="text-[12px] leading-[20px] text-neutral-400 m-0">
Ce lien expire dans 24 heures. Si vous n'êtes pas à l'origine de cette demande, ignorez ce message — votre adresse actuelle restera inchangée.
</Text>
<Text className="text-[12px] leading-[20px] text-neutral-400 m-0 mt-[8px]">
Lien :{' '}
<Link href={confirmUrl} className="text-neutral-400 underline break-all">
{confirmUrl}
</Link>
</Text>
</BaseLayout>
);