add input mask, edit valitation and validation message
This commit is contained in:
+14
-10
@@ -83,7 +83,7 @@ export async function authorization(
|
|||||||
let parsed = await response.json();
|
let parsed = await response.json();
|
||||||
await createSession(parsed.token, parsed.email);
|
await createSession(parsed.token, parsed.email);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Something went wrong. Status from server ${response.status}`);
|
throw (`Запрос завершился с ошибкой: ${response.status}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -113,6 +113,7 @@ type FormState = {
|
|||||||
confirm_password: string;
|
confirm_password: string;
|
||||||
phone: string;
|
phone: string;
|
||||||
company: string;
|
company: string;
|
||||||
|
agree: string;
|
||||||
}
|
}
|
||||||
error: Record<string, string>
|
error: Record<string, string>
|
||||||
};
|
};
|
||||||
@@ -127,12 +128,15 @@ export async function registration(
|
|||||||
const confirm_password = formData.get('confirm_password') as string || '';
|
const confirm_password = formData.get('confirm_password') as string || '';
|
||||||
const phone = formData.get('phone') as string || '';
|
const phone = formData.get('phone') as string || '';
|
||||||
const company = formData.get('company') as string || '';
|
const company = formData.get('company') as string || '';
|
||||||
|
const agree = formData.get('agree') as string || '';
|
||||||
|
|
||||||
const validatedFields = SignupFormSchema.safeParse({
|
const validatedFields = SignupFormSchema.safeParse({
|
||||||
full_name,
|
full_name,
|
||||||
email,
|
email,
|
||||||
|
phone: phone.replace(/[-\(\)\s]/g, ''),
|
||||||
password,
|
password,
|
||||||
confirm_password,
|
confirm_password,
|
||||||
|
agree,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!validatedFields.success) {
|
if (!validatedFields.success) {
|
||||||
@@ -155,17 +159,18 @@ export async function registration(
|
|||||||
password,
|
password,
|
||||||
confirm_password,
|
confirm_password,
|
||||||
phone,
|
phone,
|
||||||
company
|
company,
|
||||||
|
agree
|
||||||
},
|
},
|
||||||
error: errors
|
error: errors
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { full_name, email, password } = validatedFields.data;
|
const { full_name, email, password, phone } = validatedFields.data;
|
||||||
const response = await fetch(`${API_BASE_URL}/v1/api/auth/register`, {
|
const response = await fetch(`${API_BASE_URL}/v1/api/auth/register`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({ fullName: full_name, email, password, phone, company }),
|
body: JSON.stringify({ fullName: full_name, email, password, companyName: company, phone: phone}),
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"Accept": "application/json"
|
"Accept": "application/json"
|
||||||
@@ -176,14 +181,12 @@ export async function registration(
|
|||||||
let parsed = await response.json();
|
let parsed = await response.json();
|
||||||
await createSession(parsed.token, email);
|
await createSession(parsed.token, email);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`
|
throw (`${response.status}`);
|
||||||
-error-1. status: ${response.status}
|
|
||||||
`);
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error) {
|
if (error) {
|
||||||
const errors: Record<string, string> = {}
|
const errors: Record<string, string> = {};
|
||||||
errors['server'] = `-error-2 -> ${error}`
|
errors['server'] = `Запрос завершился с ошибкой: ${error}`
|
||||||
|
|
||||||
return {
|
return {
|
||||||
previousState: {
|
previousState: {
|
||||||
@@ -192,7 +195,8 @@ export async function registration(
|
|||||||
password,
|
password,
|
||||||
confirm_password,
|
confirm_password,
|
||||||
phone,
|
phone,
|
||||||
company
|
company,
|
||||||
|
agree
|
||||||
},
|
},
|
||||||
error: errors
|
error: errors
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ export const SignupFormSchema = z
|
|||||||
.min(3, { error: 'Имя должно содержать не менее 3 символов.' })
|
.min(3, { error: 'Имя должно содержать не менее 3 символов.' })
|
||||||
.trim(),
|
.trim(),
|
||||||
email: z.email({ error: 'Пожалуйста, введите правильный адрес электронной почты.' }).trim(),
|
email: z.email({ error: 'Пожалуйста, введите правильный адрес электронной почты.' }).trim(),
|
||||||
|
phone: z
|
||||||
|
.string()
|
||||||
|
.min(12, { error: 'Пожалуйста, введите телефонный номер' }),
|
||||||
password: z
|
password: z
|
||||||
.string()
|
.string()
|
||||||
.min(8, { error: 'Длина пароля должна быть не менее 8 символов.' })
|
.min(8, { error: 'Длина пароля должна быть не менее 8 символов.' })
|
||||||
@@ -30,7 +33,10 @@ export const SignupFormSchema = z
|
|||||||
.regex(/[0-9]/, { error: 'Пароль должен содержать как минимум 1 цифру.' })
|
.regex(/[0-9]/, { error: 'Пароль должен содержать как минимум 1 цифру.' })
|
||||||
.trim(),
|
.trim(),
|
||||||
confirm_password: z
|
confirm_password: z
|
||||||
|
.string(),
|
||||||
|
agree: z
|
||||||
.string()
|
.string()
|
||||||
|
.min(2, { error: 'Вы должны ознакомиться с условиями использования и политикой конфиденциальности' })
|
||||||
})
|
})
|
||||||
.refine((data) => data.password === data.confirm_password, {
|
.refine((data) => data.password === data.confirm_password, {
|
||||||
message: 'Повторите пароль',
|
message: 'Повторите пароль',
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useState, ChangeEvent } from 'react';
|
||||||
|
import styles from '@/app/styles/login.module.scss';
|
||||||
|
|
||||||
|
interface PhoneInputProps {
|
||||||
|
phoneState: string | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PhoneInput({ phoneState }: PhoneInputProps) {
|
||||||
|
const [phone, setPhone] = useState(phoneState ? phoneState : '');
|
||||||
|
|
||||||
|
const formatPhoneNumber = (value: string, isBackspace: boolean): string => {
|
||||||
|
if (value === '+') {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const digitsOnly = value.replace(/\D/g, '');
|
||||||
|
|
||||||
|
if (!digitsOnly) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const limitedDigits = digitsOnly.slice(0, 11);
|
||||||
|
const countryCode = limitedDigits.startsWith('8') ? '8' : '7';
|
||||||
|
const phoneNumber = limitedDigits.startsWith('8') || limitedDigits.startsWith('7')
|
||||||
|
? limitedDigits.slice(1)
|
||||||
|
: limitedDigits;
|
||||||
|
|
||||||
|
let formatted = `+${countryCode}`;
|
||||||
|
|
||||||
|
if (phoneNumber.length > 0) {
|
||||||
|
formatted += ` (${phoneNumber.slice(0, 3)}`;
|
||||||
|
}
|
||||||
|
if (phoneNumber.length >= 3) {
|
||||||
|
formatted += `) ${phoneNumber.slice(3, 6)}`;
|
||||||
|
}
|
||||||
|
if (phoneNumber.length >= 6) {
|
||||||
|
formatted += `-${phoneNumber.slice(6, 8)}`;
|
||||||
|
}
|
||||||
|
if (phoneNumber.length >= 8) {
|
||||||
|
formatted += `-${phoneNumber.slice(8, 10)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBackspace) {
|
||||||
|
const lastChar = formatted[formatted.length - 1];
|
||||||
|
const specialChars = ['-', '(', ')', ' '];
|
||||||
|
|
||||||
|
if (specialChars.includes(lastChar)) {
|
||||||
|
if (lastChar === ' ' && formatted[formatted.length - 2] === ')') {
|
||||||
|
return formatted.slice(0, -2);
|
||||||
|
}
|
||||||
|
return formatted.slice(0, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatted;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChange = (e: ChangeEvent<HTMLInputElement>): void => {
|
||||||
|
const input = e.target.value;
|
||||||
|
//@ts-ignore
|
||||||
|
const isBackspace = e.nativeEvent.inputType === 'deleteContentBackward';
|
||||||
|
const formatted = formatPhoneNumber(input, isBackspace);
|
||||||
|
setPhone(formatted);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
type="phone"
|
||||||
|
id="phone"
|
||||||
|
name="phone"
|
||||||
|
className={`${styles['form-input']}`}
|
||||||
|
placeholder="+7 (999) 123-45-67"
|
||||||
|
value={phone}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
import styles from '@/app/styles/login.module.scss';
|
import styles from '@/app/styles/login.module.scss';
|
||||||
import { useActionState } from 'react';
|
import { useActionState } from 'react';
|
||||||
import { registration } from '@/app/actions/auth';
|
import { registration } from '@/app/actions/auth';
|
||||||
|
import PhoneInput from '@/app/ui/inputs/phone-input';
|
||||||
|
|
||||||
export default function RegisterForm() {
|
export default function RegisterForm() {
|
||||||
const [state, formAction, isPending] = useActionState(
|
const [state, formAction, isPending] = useActionState(
|
||||||
@@ -48,15 +49,11 @@ export default function RegisterForm() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className={`${styles['form-group']}`}>
|
<div className={`${styles['form-group']}`}>
|
||||||
<label className={`${styles['form-label']}`}>Телефон</label>
|
<label className={`${styles['form-label']} ${styles['required']}`}>Телефон</label>
|
||||||
<input
|
<PhoneInput phoneState={state?.previousState?.phone} />
|
||||||
type="phone"
|
{state?.error?.phone && (
|
||||||
id="phone"
|
<p className="text-sm text-red-500">{state?.error?.phone}</p>
|
||||||
name="phone"
|
)}
|
||||||
className={`${styles['form-input']}`}
|
|
||||||
placeholder="+7 (999) 123-45-67"
|
|
||||||
defaultValue={state?.previousState?.phone ? state?.previousState?.phone : ''}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={`${styles['form-group']}`}>
|
<div className={`${styles['form-group']}`}>
|
||||||
@@ -103,11 +100,22 @@ export default function RegisterForm() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={`${styles['form-checkbox']}`} style={{ marginBottom: "20px" }}>
|
<div className={`${styles['form-checkbox']}`}>
|
||||||
<input type="checkbox" id="remember" name="remember" />
|
<input
|
||||||
<label>Я соглашаюсь с <a href="forgot-password" >условиями использования и политикой конфиденциальности</a></label>
|
type="checkbox"
|
||||||
|
id="agree"
|
||||||
|
name="agree"
|
||||||
|
defaultChecked={state?.previousState?.agree ? true : false}
|
||||||
|
/>
|
||||||
|
<label htmlFor="agree">Я соглашаюсь с <a href="/" >условиями использования и политикой конфиденциальности</a></label>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" className={`${styles['btn']}`}>Создать аккаунт</button>
|
{state?.error?.agree && (
|
||||||
</form>
|
<p className="text-sm text-red-500">{state?.error?.agree}</p>
|
||||||
|
)}
|
||||||
|
{state?.error?.server && (
|
||||||
|
<p className="text-sm text-red-500">{state?.error?.server}</p>
|
||||||
|
)}
|
||||||
|
<button type="submit" className={`${styles['btn']}`} style={{ marginTop: "20px" }}>Создать аккаунт</button>
|
||||||
|
</form >
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user