Создана система регистрации пользователя

This commit is contained in:
Web-serfer 2026-04-15 18:08:26 +05:00
parent 13754eecc3
commit 229826acc3
10 changed files with 1332 additions and 40 deletions

View file

@ -172,7 +172,7 @@ import { SITE_URL } from '@constants';
<div class="modal-content">
<h2 class="modal-title">Политика обработки персональных данных</h2>
<div class="privacy-text">
<p>Настоящая политика обработки персональных данных (далее — Политика) определяет порядок обработки персональных данных пользователей сайта avtourist.ru.</p>
<p>Настоящая политика обработки персональных данных (далее — Политика) определяет порядок обработки персональных данных пользователей сайта avtourist-surgut.ru.</p>
<h3>1. Общие положения</h3>
<p>1.1. Обработка персональных данных осуществляется на основе принципов законности, справедливости и конфиденциальности.</p>
@ -533,6 +533,73 @@ import { SITE_URL } from '@constants';
box-shadow: 0 4px 12px rgba(206, 159, 64, 0.4);
}
/* Success message */
.success-message {
text-align: center;
padding: 2rem 1rem;
}
.success-icon {
width: 80px;
height: 80px;
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
margin-bottom: 1.5rem;
}
.success-icon svg {
color: white;
}
.success-message h3 {
color: #1e3050;
font-size: 1.5rem;
font-weight: 700;
margin: 0 0 1rem;
}
.success-message p {
color: #64748b;
font-size: 1rem;
line-height: 1.6;
margin: 0 0 0.75rem;
}
.success-message .hint {
color: #94a3b8;
font-size: 0.875rem;
}
.success-message + .auth-footer {
display: none;
}
.success-message .btn-submit {
display: block;
margin-top: 1.5rem;
text-decoration: none;
width: 100%;
box-sizing: border-box;
text-align: center;
background: linear-gradient(135deg, #eac26e 0%, #ce9f40 100%);
color: #ffffff;
border: none;
border-radius: 8px;
padding: 0.75rem;
font-size: 0.95rem;
font-weight: 700;
cursor: pointer;
transition: all 0.3s ease;
}
.success-message .btn-submit:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(206, 159, 64, 0.4);
}
/* Кнопка отправки */
.btn-submit {
background: linear-gradient(135deg, #eac26e 0%, #ce9f40 100%);
@ -552,6 +619,12 @@ import { SITE_URL } from '@constants';
box-shadow: 0 8px 20px rgba(206, 159, 64, 0.4);
}
.btn-submit:disabled {
opacity: 0.7;
cursor: not-allowed;
transform: none;
}
.btn-submit:active {
transform: translateY(0);
}
@ -623,13 +696,11 @@ import { SITE_URL } from '@constants';
}
function validateFirstName(value: string): boolean {
const regex = /^[а-яА-ЯёЁ\-]+$/;
return regex.test(value);
return /^[а-яА-ЯёЁ\-]+$/.test(value);
}
function validateLastName(value: string): boolean {
const regex = /^[а-яА-ЯёЁ\-]+$/;
return regex.test(value);
return /^[а-яА-ЯёЁ\-]+$/.test(value);
}
function validateEmail(value: string): boolean {
@ -650,6 +721,7 @@ import { SITE_URL } from '@constants';
document.querySelectorAll('.toggle-password').forEach(button => {
button.addEventListener('click', () => {
const targetId = button.getAttribute('data-target');
if (!targetId) return;
const input = document.getElementById(targetId) as HTMLInputElement;
if (input) {
const isPassword = input.type === 'password';
@ -660,9 +732,10 @@ import { SITE_URL } from '@constants';
});
firstNameInput?.addEventListener('input', () => {
const value = firstNameInput.value.replace(/[^а-яА-ЯёЁ\-]/g, '');
const value = firstNameInput.value.replace(/[^\w\u0400-\u04FF\-]/gi, '');
firstNameInput.value = value;
if (firstNameInput.value && !validateFirstName(firstNameInput.value)) {
const trimmed = value ? value.trim() : '';
if (trimmed.length > 0 && !validateFirstName(trimmed)) {
showError(firstNameInput, 'Используйте только русские буквы');
} else {
clearError(firstNameInput);
@ -670,9 +743,10 @@ import { SITE_URL } from '@constants';
});
lastNameInput?.addEventListener('input', () => {
const value = lastNameInput.value.replace(/[^а-яА-ЯёЁ\-]/g, '');
const value = lastNameInput.value.replace(/[^\w\u0400-\u04FF\-]/gi, '');
lastNameInput.value = value;
if (lastNameInput.value && !validateLastName(lastNameInput.value)) {
const trimmed = value ? value.trim() : '';
if (trimmed.length > 0 && !validateLastName(trimmed)) {
showError(lastNameInput, 'Используйте только русские буквы');
} else {
clearError(lastNameInput);
@ -751,21 +825,27 @@ import { SITE_URL } from '@constants';
return;
}
const name = formData.get('firstName') as string;
const firstName = formData.get('firstName') as string;
const lastName = formData.get('lastName') as string;
const email = formData.get('email') as string;
const email = (formData.get('email') as string) || '';
const phone = formData.get('phone') as string;
const password = formData.get('password') as string;
const confirmPassword = formData.get('confirmPassword') as string;
let hasErrors = false;
if (!validateFirstName(firstName)) {
if (!firstName || typeof firstName !== 'string' || !firstName.trim()) {
showError(firstNameInput, 'Введите имя');
hasErrors = true;
} else if (!validateFirstName(firstName)) {
showError(firstNameInput, 'Введите имя (только русские буквы)');
hasErrors = true;
}
if (!validateLastName(lastName)) {
if (!lastName || typeof lastName !== 'string' || !lastName.trim()) {
showError(lastNameInput, 'Введите фамилию');
hasErrors = true;
} else if (!validateLastName(lastName)) {
showError(lastNameInput, 'Введите фамилию (только русские буквы)');
hasErrors = true;
}
@ -794,7 +874,53 @@ import { SITE_URL } from '@constants';
return;
}
console.log('Регистрация:', { firstName, lastName, email, phone, password });
// Отправка данных на сервер
const submitBtn = form.querySelector('.btn-submit') as HTMLButtonElement;
submitBtn.disabled = true;
submitBtn.textContent = 'Регистрация...';
try {
const response = await fetch('/api/auth/sign-up', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ firstName, lastName, email, phone, password }),
});
const result = await response.json();
if (result.success) {
// Показываем success message и скрываем footer
form.innerHTML = `
<div class="success-message" style="text-align: center; padding: 1rem 0;">
<div class="success-icon" style="width: 80px; height: 80px; background: linear-gradient(135deg, #10b981 0%, #059669 100%); border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; margin-bottom: 1.5rem;">
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path>
<polyline points="22 4 12 14.01 9 11.01"></polyline>
</svg>
</div>
<h3 style="color: #1e3050; font-size: 1.5rem; font-weight: 700; margin: 0 0 1rem;">Регистрация успешна!</h3>
<p style="color: #64748b; font-size: 1rem; line-height: 1.6; margin: 0 0 0.75rem;">На ваш email <strong>${email || ''}</strong> отправлена ссылка для подтверждения регистрации.</p>
<p style="color: #94a3b8; font-size: 0.875rem;">Проверьте почту и перейдите по ссылке для активации аккаунта.</p>
</div>
`;
// Скрываем footer с ссылкой на вход
const authFooter = document.querySelector('.auth-footer') as HTMLElement;
if (authFooter) {
authFooter.style.display = 'none';
}
} else {
showError(emailInput, result.error || 'Ошибка регистрации');
submitBtn.disabled = false;
submitBtn.textContent = 'Зарегистрироваться';
}
} catch (err) {
showError(emailInput, 'Ошибка соединения');
submitBtn.disabled = false;
submitBtn.textContent = 'Зарегистрироваться';
}
});
// Модальное окно политики