Public Access
feat(password): add reset and change password pages
/reset-password page for the email request and the new password, ChangePasswordCard in settings, password reset button on the admin users page, 'Forgot password?' link on the login page, de/en texts.
This commit is contained in:
1 parent
f71e8f7aff
commit
6627a8db66
19 files changed
+565
-25
No files matched your search
@@ -1,6 +1,6 @@
|
||||
import {useCallback, useEffect, useState} from 'react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {Ban, Check, ChevronLeft, ChevronRight, Loader2, Search, ShieldCheck, Trash2, X} from 'lucide-react';
|
||||
import {Ban, Check, ChevronLeft, ChevronRight, KeyRound, Loader2, Search, ShieldCheck, Trash2, X} from 'lucide-react';
|
||||
import {toast} from 'sonner';
|
||||
|
||||
import {Badge} from '@/components/ui/badge';
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
import {Input} from '@/components/ui/input';
|
||||
import {Label} from '@/components/ui/label';
|
||||
import PageContainer from '../components/PageContainer.jsx';
|
||||
import {decideUser, deleteUnconfirmedUser, deleteUser, fetchUsers, setUserBlocked} from '../utils/admin.js';
|
||||
import {decideUser, deleteUnconfirmedUser, deleteUser, fetchUsers, sendPasswordReset, setUserBlocked} from '../utils/admin.js';
|
||||
import {formatDate} from '../utils/format.js';
|
||||
import useAbortPrevious from '../hooks/useAbortPrevious.js';
|
||||
|
||||
@@ -31,6 +31,7 @@ const REFUSAL_KEY = {
|
||||
TARGET_IS_PENDING: 'ADMIN.ERROR_TARGET_IS_PENDING',
|
||||
TARGET_IS_UNCONFIRMED: 'ADMIN.ERROR_TARGET_IS_UNCONFIRMED',
|
||||
NOT_UNCONFIRMED: 'ADMIN.ERROR_NOT_UNCONFIRMED',
|
||||
NOT_ACTIVE: 'ADMIN.ERROR_NOT_ACTIVE',
|
||||
};
|
||||
|
||||
const formatDateOrNull = (value, locale) => (value ? formatDate(value, locale) : null);
|
||||
@@ -47,7 +48,7 @@ export default function AdminUsersPage() {
|
||||
const [pendingOnly, setPendingOnly] = useState(false);
|
||||
const [result, setResult] = useState(null);
|
||||
const [status, setStatus] = useState('loading');
|
||||
// {kind: 'block' | 'delete' | 'deleteUnconfirmed' | 'accept' | 'deny', user}
|
||||
// {kind: 'block' | 'delete' | 'deleteUnconfirmed' | 'accept' | 'deny' | 'passwordReset', user}
|
||||
const [pending, setPending] = useState(null);
|
||||
const [typedEmail, setTypedEmail] = useState('');
|
||||
const [busy, setBusy] = useState(false);
|
||||
@@ -105,6 +106,11 @@ export default function AdminUsersPage() {
|
||||
: {title: t('ADMIN.BLOCK_TITLE', {email}), text: t('ADMIN.BLOCK_TEXT'), action: 'ADMIN.BLOCK', done: 'ADMIN.DONE_BLOCK'}),
|
||||
run: () => setUserBlocked(pending.user.id, !pending.user.blocked),
|
||||
},
|
||||
passwordReset: {
|
||||
title: t('ADMIN.PASSWORD_RESET_TITLE', {email}), text: t('ADMIN.PASSWORD_RESET_TEXT'),
|
||||
action: 'ADMIN.SEND_PASSWORD_RESET', done: 'ADMIN.DONE_PASSWORD_RESET',
|
||||
run: () => sendPasswordReset(pending.user.id),
|
||||
},
|
||||
accept: {
|
||||
title: t('ADMIN.ACCEPT_TITLE', {email}), text: t('ADMIN.ACCEPT_TEXT'), action: 'ADMIN.ACCEPT',
|
||||
done: 'ADMIN.DONE_ACCEPT', run: () => decideUser(pending.user.id, true),
|
||||
@@ -217,6 +223,12 @@ export default function AdminUsersPage() {
|
||||
)}
|
||||
{user.role !== 'PLATFORM_ADMIN' && !user.deletedAt && user.status !== 'PENDING' && user.status !== 'UNCONFIRMED' && (
|
||||
<div className="flex gap-2">
|
||||
{user.status === 'ACTIVE' && !user.blocked && (
|
||||
<Button type="button" variant="outline" size="sm"
|
||||
onClick={() => setPending({kind: 'passwordReset', user})}>
|
||||
<KeyRound/>{t('ADMIN.SEND_PASSWORD_RESET')}
|
||||
</Button>
|
||||
)}
|
||||
<Button type="button" variant="outline" size="sm"
|
||||
onClick={() => setPending({kind: 'block', user})}>
|
||||
{user.blocked ? <ShieldCheck/> : <Ban/>}
|
||||
|
||||
Reference in new issue
Block a user