Files
yarmarka/templates/register.html
2026-03-20 12:41:20 +03:00

698 lines
24 KiB
HTML
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.
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=yes">
<title>Регистрация | МП.Ярмарка</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
}
body {
background: linear-gradient(145deg, #0b1c34 0%, #1a3650 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 10px;
}
.register-container {
max-width: 500px;
width: 100%;
background: white;
border-radius: 32px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
overflow: hidden;
}
.register-header {
background: #0b1c34;
color: white;
padding: 30px 20px;
text-align: center;
}
.register-header h1 {
font-size: 28px;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
margin-bottom: 5px;
}
.register-header h1 i {
color: #3b82f6;
background: rgba(255,255,255,0.1);
padding: 8px;
border-radius: 16px;
font-size: 24px;
}
.register-header p {
color: #9bb8da;
font-size: 14px;
}
.register-form {
padding: 30px 20px;
}
.role-selector {
display: flex;
gap: 10px;
margin-bottom: 25px;
background: #f0f7ff;
padding: 6px;
border-radius: 40px;
}
.role-option {
flex: 1;
border: none;
background: transparent;
padding: 12px 10px;
border-radius: 40px;
font-weight: 600;
font-size: 14px;
color: #385073;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 5px;
transition: 0.2s;
-webkit-tap-highlight-color: transparent;
}
.role-option i {
font-size: 16px;
}
.role-option.active {
background: white;
color: #0b1c34;
box-shadow: 0 4px 10px rgba(0,40,80,0.1);
}
.input-group {
margin-bottom: 15px;
}
.input-group label {
display: flex;
align-items: center;
gap: 5px;
font-weight: 600;
color: #1f3f60;
margin-bottom: 5px;
font-size: 13px;
}
.input-group label i {
color: #3b82f6;
width: 16px;
font-size: 14px;
}
.required-star {
color: #ef4444;
margin-left: 4px;
font-size: 14px;
}
.input-group input {
width: 100%;
padding: 14px 16px;
background: #f9fcff;
border: 2px solid #dee9f5;
border-radius: 30px;
font-size: 15px;
transition: 0.2s;
-webkit-appearance: none;
appearance: none;
}
.input-group input:focus {
border-color: #3b82f6;
background: white;
outline: none;
box-shadow: 0 0 0 3px rgba(59,130,246,0.1);
}
.input-group input.error {
border-color: #ef4444;
background: #fef2f2;
}
.input-row {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.input-row .input-group {
flex: 1;
min-width: 140px;
}
.btn-register {
width: 100%;
background: #0f2b4f;
color: white;
border: none;
padding: 16px;
border-radius: 40px;
font-weight: 700;
font-size: 16px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
transition: 0.2s;
margin: 20px 0 15px;
-webkit-tap-highlight-color: transparent;
}
.btn-register:active {
transform: scale(0.98);
background: #1b3f6b;
}
.btn-register:disabled {
opacity: 0.5;
pointer-events: none;
}
.login-link {
text-align: center;
color: #4f7092;
font-size: 14px;
}
.login-link a {
color: #3b82f6;
text-decoration: none;
font-weight: 600;
padding: 5px;
}
.error-message {
background: #fee2e2;
color: #b91c1c;
padding: 12px 16px;
border-radius: 30px;
margin-bottom: 15px;
display: none;
align-items: center;
gap: 8px;
font-size: 13px;
}
.success-message {
background: #d1fae5;
color: #065f46;
padding: 12px 16px;
border-radius: 30px;
margin-bottom: 15px;
display: none;
align-items: center;
gap: 8px;
font-size: 13px;
}
.password-requirements {
background: #f0f7ff;
border-radius: 20px;
padding: 12px 16px;
margin: 15px 0;
font-size: 12px;
}
.password-requirements p {
color: #1f3f60;
font-weight: 600;
margin-bottom: 8px;
display: flex;
align-items: center;
gap: 5px;
}
.requirement {
color: #4f7092;
margin: 4px 0;
display: flex;
align-items: center;
gap: 5px;
}
.requirement.valid {
color: #10b981;
}
.requirement.valid i {
color: #10b981;
}
.requirement.invalid {
color: #ef4444;
}
.requirement.invalid i {
color: #ef4444;
}
.footer {
text-align: center;
padding: 20px 30px;
background: #f8fafc;
border-top: 1px solid #dee9f5;
font-size: 13px;
color: #4f7092;
}
.footer a {
color: #3b82f6;
text-decoration: none;
}
@media (max-width: 380px) {
.input-row {
flex-direction: column;
}
.role-option {
font-size: 13px;
padding: 10px 5px;
}
}
</style>
</head>
<body>
<div class="register-container">
<div class="register-header">
<h1>
<i class="fas fa-briefcase"></i>
МП.Ярмарка
</h1>
<p>Регистрация на ярмарке вакансий</p>
</div>
<div class="register-form">
<div id="errorMessage" class="error-message">
<i class="fas fa-exclamation-circle"></i>
<span id="errorText"></span>
</div>
<div id="successMessage" class="success-message">
<i class="fas fa-check-circle"></i>
<span id="successText"></span>
</div>
<div class="role-selector">
<button class="role-option active" id="roleEmployeeBtn" type="button">
<i class="fas fa-user"></i> Соискатель
</button>
<button class="role-option" id="roleEmployerBtn" type="button">
<i class="fas fa-building"></i> Работодатель
</button>
</div>
<form id="registerForm" onsubmit="handleRegister(event)">
<div class="input-group">
<label>
<i class="fas fa-user-circle"></i>
ФИО <span class="required-star">*</span>
</label>
<input type="text" id="fullName" placeholder="Иванов Иван Иванович" required>
</div>
<div class="input-group">
<label>
<i class="fas fa-envelope"></i>
Email <span class="required-star">*</span>
</label>
<input type="email" id="email" placeholder="ivan@example.com" required>
</div>
<div class="input-row">
<div class="input-group">
<label>
<i class="fas fa-phone-alt"></i>
Телефон <span class="required-star">*</span>
</label>
<input type="tel" id="phone" placeholder="+7 (999) 123-45-67" required>
</div>
<div class="input-group">
<label>
<i class="fab fa-telegram-plane"></i>
Telegram <span class="required-star">*</span>
</label>
<input type="text" id="telegram" placeholder="@username" required>
</div>
</div>
<div class="input-group">
<label>
<i class="fas fa-lock"></i>
Пароль <span class="required-star">*</span>
</label>
<input type="password" id="password" placeholder="Минимум 6 символов" required>
</div>
<div class="input-group">
<label>
<i class="fas fa-lock"></i>
Подтверждение <span class="required-star">*</span>
</label>
<input type="password" id="confirmPassword" placeholder="Повторите пароль" required>
</div>
<!-- Требования к паролю -->
<div class="password-requirements" id="passwordRequirements">
<p><i class="fas fa-shield-alt"></i> Требования к паролю:</p>
<div class="requirement" id="reqLength">
<i class="far fa-circle"></i> Минимум 6 символов
</div>
<div class="requirement" id="reqMatch">
<i class="far fa-circle"></i> Пароли совпадают
</div>
</div>
<button type="submit" class="btn-register" id="registerBtn">
<span>Зарегистрироваться</span>
<i class="fas fa-arrow-right"></i>
</button>
</form>
<div class="login-link">
Уже есть аккаунт?
<a href="/login">Войти</a>
</div>
</div>
<div class="footer">
© 2024 Rabota.Today - Ярмарка вакансий.
<a href="/">На главную</a>
</div>
</div>
<script>
const API_BASE_URL = window.location.protocol + '//' + window.location.host + '/api';
let isSubmitting = false;
// Переключение роли
document.getElementById('roleEmployeeBtn').addEventListener('click', () => {
document.getElementById('roleEmployeeBtn').classList.add('active');
document.getElementById('roleEmployerBtn').classList.remove('active');
});
document.getElementById('roleEmployerBtn').addEventListener('click', () => {
document.getElementById('roleEmployerBtn').classList.add('active');
document.getElementById('roleEmployeeBtn').classList.remove('active');
});
// Валидация пароля в реальном времени
const passwordInput = document.getElementById('password');
const confirmInput = document.getElementById('confirmPassword');
function validatePassword() {
const password = passwordInput.value;
const confirm = confirmInput.value;
const reqLength = document.getElementById('reqLength');
const reqMatch = document.getElementById('reqMatch');
// Проверка длины
if (password.length >= 6) {
reqLength.className = 'requirement valid';
reqLength.innerHTML = '<i class="fas fa-check-circle"></i> Минимум 6 символов ✓';
} else {
reqLength.className = 'requirement invalid';
reqLength.innerHTML = '<i class="fas fa-times-circle"></i> Минимум 6 символов';
}
// Проверка совпадения
if (password && confirm && password === confirm) {
reqMatch.className = 'requirement valid';
reqMatch.innerHTML = '<i class="fas fa-check-circle"></i> Пароли совпадают ✓';
} else if (confirm) {
reqMatch.className = 'requirement invalid';
reqMatch.innerHTML = '<i class="fas fa-times-circle"></i> Пароли совпадают';
} else {
reqMatch.className = 'requirement';
reqMatch.innerHTML = '<i class="far fa-circle"></i> Пароли совпадают';
}
}
passwordInput.addEventListener('input', validatePassword);
confirmInput.addEventListener('input', validatePassword);
// Функция для проверки корректности Telegram username
function validateTelegram(telegram) {
if (!telegram) return false;
// Telegram username может начинаться с @ или без него
// Допустимые символы: буквы, цифры, подчеркивание
const cleanTelegram = telegram.replace('@', '');
return /^[a-zA-Z0-9_]{5,32}$/.test(cleanTelegram);
}
// Функция для форматирования телефона
function formatPhone(phone) {
// Удаляем все нецифровые символы
let digits = phone.replace(/\D/g, '');
// Если номер начинается с 8 или 7, нормализуем
if (digits.length === 11) {
if (digits.startsWith('8')) {
digits = '7' + digits.substring(1);
}
} else if (digits.length === 10) {
digits = '7' + digits;
}
return digits;
}
// Показать сообщение об ошибке
function showError(message) {
const errorDiv = document.getElementById('errorMessage');
const errorText = document.getElementById('errorText');
errorText.textContent = message;
errorDiv.style.display = 'flex';
setTimeout(() => {
errorDiv.style.display = 'none';
}, 5000);
}
// Показать сообщение об успехе
function showSuccess(message) {
const successDiv = document.getElementById('successMessage');
const successText = document.getElementById('successText');
successText.textContent = message;
successDiv.style.display = 'flex';
setTimeout(() => {
successDiv.style.display = 'none';
}, 3000);
}
// Показать состояние загрузки
function setLoading(isLoading) {
const registerBtn = document.getElementById('registerBtn');
if (isLoading) {
registerBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Регистрация...';
registerBtn.disabled = true;
isSubmitting = true;
} else {
registerBtn.innerHTML = '<span>Зарегистрироваться</span><i class="fas fa-arrow-right"></i>';
registerBtn.disabled = false;
isSubmitting = false;
}
}
// Обработка регистрации
async function handleRegister(event) {
event.preventDefault();
if (isSubmitting) return;
// Получаем значения полей
const fullName = document.getElementById('fullName').value.trim();
const email = document.getElementById('email').value.trim();
const phone = document.getElementById('phone').value.trim();
const telegram = document.getElementById('telegram').value.trim();
const password = document.getElementById('password').value;
const confirmPassword = document.getElementById('confirmPassword').value;
// Определяем роль
const role = document.getElementById('roleEmployeeBtn').classList.contains('active') ? 'employee' : 'employer';
// Валидация ФИО
if (!fullName) {
showError('Введите ФИО');
document.getElementById('fullName').focus();
return;
}
// Валидация email
if (!email) {
showError('Введите email');
document.getElementById('email').focus();
return;
}
if (!email.includes('@') || !email.includes('.')) {
showError('Введите корректный email');
document.getElementById('email').focus();
return;
}
// Валидация телефона
if (!phone) {
showError('Введите номер телефона');
document.getElementById('phone').focus();
return;
}
const phoneDigits = formatPhone(phone);
if (phoneDigits.length !== 11) {
showError('Введите корректный номер телефона (10 или 11 цифр)');
document.getElementById('phone').focus();
return;
}
// Валидация Telegram
if (!telegram) {
showError('Введите Telegram username');
document.getElementById('telegram').focus();
return;
}
if (!validateTelegram(telegram)) {
showError('Telegram username должен содержать от 5 до 32 символов (буквы, цифры, _)');
document.getElementById('telegram').focus();
return;
}
// Валидация пароля
if (password.length < 6) {
showError('Пароль должен содержать минимум 6 символов');
document.getElementById('password').focus();
return;
}
if (password !== confirmPassword) {
showError('Пароли не совпадают');
document.getElementById('confirmPassword').focus();
return;
}
// Форматируем телефон для отправки
const formattedPhone = '+' + phoneDigits;
// Форматируем Telegram (добавляем @ если нет)
let formattedTelegram = telegram;
if (!telegram.startsWith('@')) {
formattedTelegram = '@' + telegram;
}
// Подготавливаем данные для отправки
const userData = {
full_name: fullName,
email: email,
phone: formattedPhone,
telegram: formattedTelegram,
password: password,
role: role
};
console.log('📤 Отправка данных регистрации:', {
...userData,
password: '***скрыто***'
});
setLoading(true);
try {
const response = await fetch(`${API_BASE_URL}/register`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify(userData)
});
const data = await response.json();
console.log('📥 Ответ сервера:', data);
if (!response.ok) {
throw new Error(data.detail || 'Ошибка регистрации');
}
// Успешная регистрация
showSuccess('Регистрация успешна! Перенаправляем...');
// Сохраняем токен
localStorage.setItem('accessToken', data.access_token);
localStorage.setItem('userId', data.user_id);
localStorage.setItem('userRole', data.role);
localStorage.setItem('userName', data.full_name);
// Перенаправляем в профиль через секунду
setTimeout(() => {
window.location.href = '/profile';
}, 1500);
} catch (error) {
console.error('❌ Ошибка регистрации:', error);
showError(error.message);
setLoading(false);
}
}
// Автоматическое форматирование телефона
document.getElementById('phone').addEventListener('input', function(e) {
let x = e.target.value.replace(/\D/g, '').match(/(\d{0,1})(\d{0,3})(\d{0,3})(\d{0,2})(\d{0,2})/);
if (x) {
e.target.value = !x[2] ? x[1] :
'+7 (' + x[2] + ') ' + x[3] + (x[4] ? '-' + x[4] : '') + (x[5] ? '-' + x[5] : '');
}
});
// Автоматическое добавление @ в Telegram
document.getElementById('telegram').addEventListener('blur', function(e) {
let value = e.target.value.trim();
if (value && !value.startsWith('@')) {
e.target.value = '@' + value.replace(/[^a-zA-Z0-9_]/g, '');
}
});
// Ограничение ввода для Telegram (только буквы, цифры, _)
document.getElementById('telegram').addEventListener('input', function(e) {
let value = e.target.value;
// Если начинается с @, разрешаем @ только в начале
if (value.startsWith('@')) {
e.target.value = '@' + value.substring(1).replace(/[^a-zA-Z0-9_]/g, '');
} else {
e.target.value = value.replace(/[^a-zA-Z0-9_]/g, '');
}
});
// Проверка существующей сессии
if (localStorage.getItem('accessToken')) {
window.location.href = '/profile';
}
</script>
</body>
</html>