Files
no-copy-frontend/src/app/ui/forms/reset-password-form.tsx
T

216 lines
5.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
import styles from '@/app/styles/module/login.module.scss'
import { useActionState, useEffect, useRef, useState, ChangeEvent } from 'react';
import { useTranslations } from 'next-intl';
import { confirmPassword, FormStateConfirmPassword } from '@/app/actions/auth';
import { useSearchParams } from 'next/navigation';
import { IconEye } from '@/app/ui/icons/icons';
import { SignupFormSchema } from '@/app/actions/definitions';
import * as z from 'zod';
export default function ResetPasswordForm() {
const [state, formAction, isPending] = useActionState(
confirmPassword,
undefined,
);
const passwordRef = useRef<HTMLInputElement>(null);
const [showPassword, setShowPassword] = useState(false);
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
const [formState, setFormState] = useState<FormStateConfirmPassword | undefined>(undefined);
const t = useTranslations('Login-register-form');
const searchParams = useSearchParams();
const email = searchParams.get('email');
useEffect(() => {
if (state) {
setFormState(state);
}
}, [state])
function showPassowrd(target: 'password' | 'confirm-password') {
if (target === 'password') {
setShowPassword(!showPassword);
} else {
setShowConfirmPassword(!showConfirmPassword);
}
}
function validateField(
fieldName: string,
value: string,
) {
switch (fieldName) {
case 'confirm_password':
const baseSchema = SignupFormSchema.shape.confirm_password;
const baseResult = baseSchema.safeParse(value);
if (!baseResult.success) {
return baseResult;
}
const currentPassword = passwordRef.current?.value || '';
if (value !== currentPassword) {
return {
success: false,
error: new z.ZodError([
{
code: z.ZodIssueCode.custom,
message: 'repeat-password',
path: ['confirm_password']
}
])
};
}
return { success: true, data: value };
case 'password':
const schema = SignupFormSchema.shape[fieldName];
return schema.safeParse(value);
default:
throw new Error(`Unknown field: ${fieldName}`);
}
}
function onChangeHandler(e: ChangeEvent<HTMLInputElement>): void {
const fieldName = e.target.name;
if (fieldName !== 'phone') {
e.target.value = e.target.value.replace(/([^a-zA-Zа-яёА-ЯЁ0-9@.!#$%&"'*+/=?^_{|}~\-\s])/g, '');
}
const validatedField = validateField(fieldName, e.target.value);
if (validatedField.success) {
setFormState(prevState => {
if (!prevState?.error || !prevState?.error[fieldName]) {
return prevState;
}
const { [fieldName]: _, ...newError } = prevState.error;
return {
...prevState,
error: newError
};
});
}
}
return (
<form action={formAction}>
<div className={`${styles['form-group']}`}>
<input
type="hidden"
id="email"
name="email"
className={`${styles['form-input']}`}
placeholder={t('enter-email')}
defaultValue={email ? email : ''}
/>
</div>
<div className={`${styles['form-group']}`}>
<label className={`${styles['form-label']} ${styles['required']}`}>
{t('recovery-code')}
</label>
<input
type="readOnly"
id="verify-token"
name="verify-token"
className={`${styles['form-input']}`}
placeholder={t('recovery-code')}
defaultValue={formState?.previousState?.verifyToken ? formState?.previousState?.verifyToken : ''}
/>
</div>
<div className={`${styles['form-group']}`}>
<label className={`${styles['form-label']} ${styles['required']}`}>
{t('password')}
</label>
<div
className={`${styles['password-wrapper']}`}
>
<input
ref={passwordRef}
type={showPassword ? "text" : "password"}
id="password"
name="password"
className={`${styles['form-input']} ${styles['password']}`}
placeholder={t('password-placeholder')}
defaultValue={formState?.previousState?.password ? formState?.previousState?.password : ''}
onChange={onChangeHandler}
/>
<button
onClick={() => {
showPassowrd('password')
}}
type="button"
className={`show-password-button ${showPassword ? 'show' : ''}`}
>
<IconEye />
</button>
</div>
{formState?.error?.password && (
<p className="text-sm text-red-500">
{
formState?.error?.password.split('&').map((e, index) => {
return (
<span key={index}>
{t(e)}
<br />
</span>
)
})
}
</p>
)}
</div>
<div className={`${styles['form-group']}`}>
<label className={`${styles['form-label']} ${styles['required']}`}>
{t('confirm-password')}
</label>
<div className={`${styles['password-wrapper']}`}>
<input
type={showConfirmPassword ? "text" : "password"}
id="confirm_password"
name="confirm_password"
className={`${styles['form-input']} ${styles['password']}`}
placeholder={t('repeat-password')}
defaultValue={formState?.previousState?.confirm_password ? formState?.previousState?.confirm_password : ''}
onChange={onChangeHandler}
/>
<button
onClick={() => {
showPassowrd('confirm-password');
}}
type="button"
className={`show-password-button ${showConfirmPassword ? 'show' : ''}`}
>
<IconEye />
</button>
</div>
{formState?.error?.confirm_password && (
<p className="text-sm text-red-500">
{t(formState?.error?.confirm_password)}
</p>
)}
</div>
{formState?.error?.server && (
<p className={`${styles['form-error']}`}>
{t(formState?.error?.server)}
</p>
)}
<button
type="submit"
className={`${styles['btn']}`}
disabled={isPending}
>
{t('change-password')}
{isPending && (
<div className="loading-animation">
<div className="global-spinner"></div>
</div>
)}
</button>
</form>
)
}