diff --git a/frontend/src/pages/admin/UsersSection.jsx b/frontend/src/pages/admin/UsersSection.jsx
index 8b1faaa..8478595 100644
--- a/frontend/src/pages/admin/UsersSection.jsx
+++ b/frontend/src/pages/admin/UsersSection.jsx
@@ -265,60 +265,42 @@ function InviteUserModal({ open, onClose, onSent }) {
}
// ── Modale renvoi invitation ───────────────────────────────────────────────
-function ResendInviteModal({ user, onClose, onSent }) {
+function ResendInviteModal({ user, onClose, onSuccess, onError }) {
const [loading, setLoading] = useState(false);
const [err, setErr] = useState(null);
- const [sent, setSent] = useState(false);
- const handleClose = () => { setSent(false); setErr(null); onClose(); };
+ const handleClose = () => { setErr(null); onClose(); };
const handleSend = async () => {
setLoading(true); setErr(null);
try {
await api.post('/admin/invitations', { email: user.email, role: user.role });
- setSent(true);
- onSent?.();
- } catch (e) { setErr(e.message); }
- finally { setLoading(false); }
+ // Ferme d'abord, puis notifie le parent — évite le re-render concurrent
+ handleClose();
+ onSuccess?.(user.email);
+ } catch (e) {
+ setErr(e.message);
+ } finally { setLoading(false); }
};
const identity = user?.display_name ? `${user.display_name} (${user.email})` : user?.email;
return (
Fermer
- ) : (
+ footer={
<>
>
- )}
+ }
>
- {sent ? (
-
-
-
Invitation renvoyée !
-
- Un nouveau lien a été envoyé à {user?.email}.
- L'ancien lien a été révoqué. Le nouveau est valable 7 jours.
-
-
- ) : (
- <>
- {err && {err}
}
-
- Renvoyer un nouveau lien d'invitation à {identity} ?
-
L'ancien lien sera révoqué et remplacé par un nouveau valable 7 jours.
-
- >
- )}
+ {err && {err}
}
+
+ Renvoyer un nouveau lien d'invitation à {identity} ?
+
L'ancien lien sera révoqué et remplacé par un nouveau valable 7 jours.
+
);
}
@@ -396,6 +378,7 @@ export default function UsersSection({ currentUserId }) {
const [showCreate, setShowCreate] = useState(false);
const [showInvite, setShowInvite] = useState(false);
const [resendTarget, setResendTarget] = useState(null);
+ const [banner, setBanner] = useState(null); // { ok, msg }
const [search, setSearch] = useState('');
const [filterStatus, setFilterStatus] = useState('all');
const [filterRole, setFilterRole] = useState('all');
@@ -445,6 +428,12 @@ export default function UsersSection({ currentUserId }) {
`Marquer l'email de ${u.display_name || u.email} comme vérifié ?`,
async () => { await api.patch(`/admin/users/${u.id}/verify-email`, {}); load(); }, 'Confirmer');
const resendInvitation = u => setResendTarget(u);
+ const resendVerifEmail = async u => {
+ try {
+ await api.post('/auth/resend-verification', { email: u.email });
+ setBanner({ ok: true, msg: `Email de vérification renvoyé à ${u.email}.` });
+ } catch (e) { setBanner({ ok: false, msg: e.message }); }
+ };
const setStatus = (u, status) => {
const labels = { deactivated: 'Désactiver', active: 'Réactiver', locked: 'Verrouiller' };
confirm(`${labels[status]} le compte`, `${labels[status]} le compte de ${u.display_name || u.email} ?`,
@@ -532,6 +521,15 @@ export default function UsersSection({ currentUserId }) {
<>
+ {/* Bannière succès/erreur */}
+ {banner && (
+
setBanner(null)}
+ style={{ margin: '12px 20px 0', borderRadius: 8 }}
+ />
+ )}
+
{/* En-tête */}
@@ -668,10 +666,11 @@ export default function UsersSection({ currentUserId }) {
background: 'var(--surface)', border: '1px solid var(--border)',
borderRadius: 8, boxShadow: '0 4px 20px rgba(0,0,0,0.15)', padding: '4px 0', minWidth: 200,
}}>
- } label={openMenu.user.role === 'admin' ? '→ Utilisateur' : '→ Admin'} onClick={() => { setOpenMenu(null); toggleRole(openMenu.user); }} />
+ } label={openMenu.user.role === 'admin' ? 'Rétrograder en utilisateur' : 'Promouvoir en admin'} onClick={() => { setOpenMenu(null); toggleRole(openMenu.user); }} />
{!openMenu.user.email_verified && <>
} label="Renvoyer l'invitation" onClick={() => { setOpenMenu(null); resendInvitation(openMenu.user); }} />
} label="Vérifier l'email" onClick={() => { setOpenMenu(null); verifyEmail(openMenu.user); }} />
+ } label="Nouvelle demande de vérification" onClick={() => { setOpenMenu(null); resendVerifEmail(openMenu.user); }} />
>}
{openMenu.user.id !== currentUserId && <>
{(!openMenu.user.status || openMenu.user.status === 'active') && } label="Désactiver" onClick={() => { setOpenMenu(null); setStatus(openMenu.user, 'deactivated'); }} />}
@@ -693,7 +692,10 @@ export default function UsersSection({ currentUserId }) {
setResendTarget(null)}
- onSent={() => load()}
+ onSuccess={email => {
+ load();
+ setBanner({ ok: true, msg: `Invitation renvoyée à ${email}. Le lien est valable 7 jours.` });
+ }}
/>