Files
yarmarka/templates/register.html
2026-03-13 19:58:52 +03:00

699 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">
<title>Регистрация | Rabota.Today</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, sans-serif;
}
body {
background: linear-gradient(145deg, #0b1c34 0%, #1a3650 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.register-container {
max-width: 600px;
width: 100%;
background: white;
border-radius: 48px;
box-shadow: 0 40px 80px rgba(0, 0, 0, 0.3);
overflow: hidden;
animation: slideUp 0.5s ease;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.register-header {
background: #0b1c34;
color: white;
padding: 40px;
text-align: center;
}
.register-header h1 {
font-size: 36px;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
gap: 15px;
margin-bottom: 10px;
}
.register-header h1 i {
color: #3b82f6;
background: rgba(255,255,255,0.1);
padding: 12px;
border-radius: 20px;
}
.register-header p {
color: #9bb8da;
font-size: 16px;
}
.register-form {
padding: 40px;
}
.role-selector {
display: flex;
gap: 16px;
margin-bottom: 30px;
background: #f0f7ff;
padding: 8px;
border-radius: 60px;
}
.role-option {
flex: 1;
border: none;
background: transparent;
padding: 16px 20px;
border-radius: 50px;
font-weight: 700;
font-size: 16px;
color: #385073;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
transition: 0.2s;
}
.role-option i {
font-size: 18px;
}
.role-option.active {
background: white;
color: #0b1c34;
box-shadow: 0 8px 20px rgba(0,40,80,0.1);
}
.input-group {
margin-bottom: 20px;
}
.input-group label {
display: flex;
align-items: center;
gap: 8px;
font-weight: 600;
color: #1f3f60;
margin-bottom: 8px;
font-size: 14px;
}
.input-group label i {
color: #3b82f6;
width: 20px;
}
.input-group input {
width: 100%;
padding: 16px 20px;
background: #f9fcff;
border: 2px solid #dee9f5;
border-radius: 30px;
font-size: 16px;
transition: 0.2s;
}
.input-group input:focus {
border-color: #3b82f6;
background: white;
outline: none;
box-shadow: 0 0 0 4px rgba(59,130,246,0.1);
}
.input-group input.error {
border-color: #ef4444;
background: #fef2f2;
}
.input-row {
display: flex;
gap: 15px;
}
.input-row .input-group {
flex: 1;
}
.password-requirements {
background: #f0f7ff;
border-radius: 20px;
padding: 15px 20px;
margin: 20px 0;
font-size: 13px;
}
.password-requirements p {
color: #1f3f60;
font-weight: 600;
margin-bottom: 10px;
display: flex;
align-items: center;
gap: 8px;
}
.requirement {
color: #4f7092;
margin: 5px 0;
display: flex;
align-items: center;
gap: 8px;
}
.requirement i {
width: 16px;
font-size: 12px;
}
.requirement.valid {
color: #10b981;
}
.requirement.valid i {
color: #10b981;
}
.requirement.invalid {
color: #ef4444;
}
.requirement.invalid i {
color: #ef4444;
}
.terms {
display: flex;
align-items: center;
gap: 10px;
margin: 25px 0;
font-size: 14px;
color: #4f7092;
}
.terms input[type="checkbox"] {
width: 18px;
height: 18px;
cursor: pointer;
}
.terms a {
color: #3b82f6;
text-decoration: none;
font-weight: 600;
}
.terms a:hover {
text-decoration: underline;
}
.btn-register {
width: 100%;
background: #0f2b4f;
color: white;
border: none;
padding: 18px;
border-radius: 40px;
font-weight: 700;
font-size: 18px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
transition: 0.2s;
margin-bottom: 20px;
}
.btn-register:hover:not(:disabled) {
background: #1b3f6b;
transform: scale(1.02);
}
.btn-register:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.login-link {
text-align: center;
color: #4f7092;
font-size: 15px;
}
.login-link a {
color: #3b82f6;
text-decoration: none;
font-weight: 600;
margin-left: 5px;
}
.login-link a:hover {
text-decoration: underline;
}
.error-message {
background: #fee2e2;
color: #b91c1c;
padding: 15px 20px;
border-radius: 30px;
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 10px;
font-size: 14px;
}
.success-message {
background: #d1fae5;
color: #065f46;
padding: 15px 20px;
border-radius: 30px;
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 10px;
font-size: 14px;
}
.loader {
display: inline-block;
width: 20px;
height: 20px;
border: 3px solid rgba(255,255,255,0.3);
border-radius: 50%;
border-top-color: white;
animation: spin 1s ease-in-out infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.footer {
text-align: center;
padding: 20px 40px;
background: #f8fafc;
border-top: 1px solid #dee9f5;
font-size: 13px;
color: #4f7092;
}
.footer a {
color: #3b82f6;
text-decoration: none;
}
@media (max-width: 600px) {
.input-row {
flex-direction: column;
gap: 0;
}
.role-selector {
flex-direction: column;
gap: 8px;
}
}
</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" style="display: none;">
<i class="fas fa-exclamation-circle"></i>
<span id="errorText"></span>
</div>
<div id="successMessage" class="success-message" style="display: none;">
<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> ФИО *</label>
<input type="text" id="fullName" placeholder="Иванов Иван Иванович" required>
</div>
<div class="input-group">
<label><i class="fas fa-envelope"></i> Email *</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> Телефон *</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</label>
<input type="text" id="telegram" placeholder="@username">
</div>
</div>
<div class="input-group">
<label><i class="fas fa-lock"></i> Пароль *</label>
<input type="password" id="password" placeholder="Минимум 6 символов" required>
</div>
<div class="input-group">
<label><i class="fas fa-lock"></i> Подтверждение пароля *</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>
<!-- Согласие с условиями -->
<div class="terms">
<input type="checkbox" id="terms" required>
<label for="terms">
Я принимаю <a href="#" onclick="showTerms()">условия использования</a>
и даю согласие на обработку персональных данных
</label>
</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">
© 2026 Rabota.Today - Ярмарка вакансий.
<a href="/">На главную</a>
</div>
</div>
<!-- Модальное окно с условиями использования -->
<div id="termsModal" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); align-items: center; justify-content: center; z-index: 2000;">
<div style="background: white; max-width: 500px; width: 90%; border-radius: 40px; padding: 40px; max-height: 80vh; overflow-y: auto;">
<h3 style="margin-bottom: 20px; color: #0b1c34;">Условия использования</h3>
<div style="color: #1f3f60; line-height: 1.6; margin-bottom: 30px;">
<p>1. Регистрируясь на платформе, вы подтверждаете, что предоставленная информация является достоверной.</p>
<p>2. Администрация платформы имеет право удалять аккаунты, нарушающие правила этики и законодательства РФ.</p>
<p>3. Запрещено размещение вакансий, противоречащих законодательству РФ.</p>
<p>4. Запрещено размещение резюме с недостоверной информацией.</p>
<p>5. Платформа не несет ответственности за достоверность информации, размещенной пользователями.</p>
<p>6. Персональные данные обрабатываются в соответствии с политикой конфиденциальности.</p>
</div>
<button class="btn-register" onclick="closeTerms()" style="margin-bottom: 0;">Закрыть</button>
</div>
</div>
<script>
const API_BASE_URL = 'http://localhost:8000/api';
// Переключение роли
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);
// Показать сообщение об ошибке
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 = '<span class="loader"></span><span>Регистрация...</span>';
registerBtn.disabled = true;
} else {
registerBtn.innerHTML = '<span>Зарегистрироваться</span><i class="fas fa-arrow-right"></i>';
registerBtn.disabled = false;
}
}
// Обработка регистрации
async function handleRegister(event) {
event.preventDefault();
// Получаем значения полей
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() || null;
const password = document.getElementById('password').value;
const confirmPassword = document.getElementById('confirmPassword').value;
const termsChecked = document.getElementById('terms').checked;
// Определяем роль
const role = document.getElementById('roleEmployeeBtn').classList.contains('active') ? 'employee' : 'employer';
// Валидация
if (!fullName) {
showError('Введите ФИО');
document.getElementById('fullName').focus();
return;
}
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 = phone.replace(/\D/g, '');
if (phoneDigits.length < 10) {
showError('Введите корректный телефон (минимум 10 цифр)');
document.getElementById('phone').focus();
return;
}
if (password.length < 6) {
showError('Пароль должен содержать минимум 6 символов');
document.getElementById('password').focus();
return;
}
if (password !== confirmPassword) {
showError('Пароли не совпадают');
document.getElementById('confirmPassword').focus();
return;
}
if (!termsChecked) {
showError('Необходимо принять условия использования');
return;
}
// Подготавливаем данные для отправки
const userData = {
full_name: fullName,
email: email,
phone: phone,
telegram: telegram,
password: password,
role: role
};
setLoading(true);
try {
const response = await fetch(`${API_BASE_URL}/register`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(userData)
});
const data = await response.json();
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) {
showError(error.message);
setLoading(false);
}
}
// Функции для модального окна с условиями
function showTerms() {
document.getElementById('termsModal').style.display = 'flex';
return false;
}
function closeTerms() {
document.getElementById('termsModal').style.display = 'none';
}
// Закрытие модалки по клику вне окна
window.onclick = function(event) {
const modal = document.getElementById('termsModal');
if (event.target === modal) {
modal.style.display = 'none';
}
};
// Автоматическое форматирование телефона (простой вариант)
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] : '');
}
});
// Проверяем, может пользователь уже залогинен
if (localStorage.getItem('accessToken')) {
window.location.href = '/profile';
}
</script>
</body>
</html>