- Updated password validation regex in ChangePasswordCommandValidator, RegisterCommandValidator, RegisterForm, and ChangePasswordForm to use Unicode property escapes for uppercase letters, ensuring consistent validation rules across both backend and frontend components.
69 lines
2.5 KiB
TypeScript
69 lines
2.5 KiB
TypeScript
import { zodResolver } from '@hookform/resolvers/zod'
|
|
import { useForm } from 'react-hook-form'
|
|
import { useTranslation } from 'react-i18next'
|
|
import { z } from 'zod'
|
|
import { toast } from '@/shared/ui/toast-store'
|
|
import { Button } from '@/shared/ui/button'
|
|
import { Input } from '@/shared/ui/input'
|
|
import { Label } from '@/shared/ui/label'
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/shared/ui/card'
|
|
import { HttpError } from '@/shared/api/client'
|
|
import { changePassword } from '@/features/auth/api'
|
|
|
|
const schema = z.object({
|
|
currentPassword: z.string().min(1),
|
|
newPassword: z
|
|
.string()
|
|
.min(8)
|
|
.regex(/^(?=.*\p{Lu})(?=.*\d).+$/u),
|
|
})
|
|
|
|
type FormValues = z.infer<typeof schema>
|
|
|
|
export function ChangePasswordForm() {
|
|
const { t } = useTranslation()
|
|
const {
|
|
register,
|
|
handleSubmit,
|
|
reset,
|
|
formState: { errors, isSubmitting },
|
|
} = useForm<FormValues>({ resolver: zodResolver(schema) })
|
|
|
|
const onSubmit = async (values: FormValues) => {
|
|
try {
|
|
await changePassword(values.currentPassword, values.newPassword)
|
|
toast.success(t('settings.passwordChanged'))
|
|
reset()
|
|
} catch (error) {
|
|
const message =
|
|
error instanceof HttpError && error.status === 400 ? t('settings.currentPasswordInvalid') : t('auth.genericError')
|
|
toast.error(message)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="text-base">{t('settings.changePassword')}</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<form onSubmit={handleSubmit(onSubmit)} className="flex flex-col gap-4">
|
|
<div className="flex flex-col gap-1.5">
|
|
<Label htmlFor="currentPassword">{t('settings.currentPassword')}</Label>
|
|
<Input id="currentPassword" type="password" autoComplete="current-password" {...register('currentPassword')} />
|
|
{errors.currentPassword && <p className="text-sm text-red-500">{t('settings.currentPasswordRequired')}</p>}
|
|
</div>
|
|
<div className="flex flex-col gap-1.5">
|
|
<Label htmlFor="newPassword">{t('settings.newPassword')}</Label>
|
|
<Input id="newPassword" type="password" autoComplete="new-password" {...register('newPassword')} />
|
|
{errors.newPassword && <p className="text-sm text-red-500">{t('auth.passwordHint')}</p>}
|
|
</div>
|
|
<Button type="submit" disabled={isSubmitting} className="self-start">
|
|
{t('settings.changePassword')}
|
|
</Button>
|
|
</form>
|
|
</CardContent>
|
|
</Card>
|
|
)
|
|
}
|