Add password reset functionality for admin users: implement ResetPassword endpoint, update AdminUserEndpoints to include password reset logic, and enhance user interface for password management. Update translations for new features and ensure proper error handling in the reset process.
This commit is contained in:
1 parent
d44aa44d95
commit
1f6fa6f1ae
30 files changed
+1755
-58
No files matched your search
@@ -5,13 +5,21 @@ import { HttpError } from '@/shared/api/client'
|
||||
import { Badge } from '@/shared/ui/badge'
|
||||
import { Button } from '@/shared/ui/button'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/shared/ui/card'
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/shared/ui/dialog'
|
||||
import { Input } from '@/shared/ui/input'
|
||||
import { Label } from '@/shared/ui/label'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/shared/ui/select'
|
||||
import { toast } from '@/shared/ui/toast-store'
|
||||
import type { UserSummaryDto } from '@/shared/api/types'
|
||||
import { changeUserRole } from '@/features/admin/roles/api'
|
||||
import { listRoles } from '@/features/admin/roles/api'
|
||||
import { blockUser, createUser, deleteUser, listUsers, unblockUser } from './api'
|
||||
import { blockUser, createUser, deleteUser, listUsers, resetUserPassword, unblockUser } from './api'
|
||||
|
||||
const PAGE_SIZE = 20
|
||||
|
||||
@@ -24,6 +32,7 @@ export function UsersPanel() {
|
||||
const [newUserName, setNewUserName] = useState('')
|
||||
const [newPassword, setNewPassword] = useState('')
|
||||
const [newRoleId, setNewRoleId] = useState('')
|
||||
const [resetTarget, setResetTarget] = useState<UserSummaryDto | null>(null)
|
||||
|
||||
const { data: roles } = useQuery({ queryKey: ['admin', 'roles'], queryFn: listRoles })
|
||||
const { data, isLoading } = useQuery({
|
||||
@@ -214,6 +223,9 @@ export function UsersPanel() {
|
||||
{t('admin.users.block')}
|
||||
</Button>
|
||||
)}
|
||||
<Button size="sm" variant="outline" onClick={() => setResetTarget(user)}>
|
||||
{t('admin.users.resetPassword')}
|
||||
</Button>
|
||||
<Button size="sm" variant="destructive" onClick={() => deleteMutation.mutate(user.id)}>
|
||||
{t('common.delete')}
|
||||
</Button>
|
||||
@@ -238,6 +250,68 @@ export function UsersPanel() {
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{resetTarget && (
|
||||
<ResetPasswordDialog
|
||||
user={resetTarget}
|
||||
onClose={() => setResetTarget(null)}
|
||||
onError={onError}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function ResetPasswordDialog({
|
||||
user,
|
||||
onClose,
|
||||
onError,
|
||||
}: {
|
||||
user: UserSummaryDto
|
||||
onClose: () => void
|
||||
onError: (e: unknown) => void
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
const [password, setPassword] = useState('')
|
||||
|
||||
const reset = useMutation({
|
||||
mutationFn: () => resetUserPassword(user.id, password),
|
||||
onSuccess: () => {
|
||||
toast.success(t('admin.users.passwordReset'))
|
||||
onClose()
|
||||
},
|
||||
onError,
|
||||
})
|
||||
|
||||
return (
|
||||
<Dialog open onOpenChange={(open) => !open && onClose()}>
|
||||
<DialogContent className="max-w-sm">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t('admin.users.resetPasswordFor', { name: user.userName })}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Label>{t('admin.users.newPassword')}</Label>
|
||||
<Input
|
||||
type="password"
|
||||
autoFocus
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">{t('admin.users.passwordHint')}</p>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" size="sm" onClick={onClose}>
|
||||
{t('common.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
disabled={password.length < 8 || reset.isPending}
|
||||
onClick={() => reset.mutate()}
|
||||
>
|
||||
{t('common.save')}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
@@ -36,3 +36,10 @@ export function unblockUser(id: string) {
|
||||
export function deleteUser(id: string) {
|
||||
return apiRequest<void>(`/admin/users/${id}`, { method: 'DELETE' })
|
||||
}
|
||||
|
||||
export function resetUserPassword(id: string, newPassword: string) {
|
||||
return apiRequest<void>(`/admin/users/${id}/password`, {
|
||||
method: 'POST',
|
||||
body: { newPassword },
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user