/
Rayven2035
/
Web
Обзор
Документация
Войти
/
Rayven2035
/
Web
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
registration.html
578 строк
29 KB
Rayven2035
create: profile.html, README.md, registration.html, Web-приложение.html, Мобильное приложение.html, Пользовательское_соглашение.html, Презентация сайта (deepseek).html
25 май 2026, 10:13
Верифицирован
25 май 2026, 10:13
c5d93ff
Код
Авторство
О чём код?
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"> <title>Логисмарт — Вход в систему</title> <!-- Tailwind CSS + Font Awesome --> <script src="https://cdn.tailwindcss.com"></script> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css"> <link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,300;14..32,400;14..32,500;14..32,600;14..32,700;14..32,800&display=swap" rel="stylesheet"> <style> * { font-family: 'Inter', sans-serif; } body { background: linear-gradient(135deg, #0f2b3d 0%, #1b4a6e 100%); min-height: 100vh; } .auth-card { background: rgba(255,255,255,0.98); backdrop-filter: blur(10px); transition: all 0.3s ease; } .input-field { transition: all 0.2s ease; border: 1px solid #e2e8f0; } .input-field:focus { border-color: #3b82f6; box-shadow: 0 0 0 3px rgba(59,130,246,0.1); outline: none; } .input-field.error { border-color: #ef4444; background-color: #fef2f2; } .btn-primary { background: linear-gradient(95deg, #2563eb, #1e40af); transition: all 0.2s; } .btn-primary:hover { transform: translateY(-2px); box-shadow: 0 10px 20px -5px rgba(37,99,235,0.4); } .btn-primary:active { transform: translateY(0); } .toggle-btn { transition: all 0.2s; } .toggle-btn.active { background: #3b82f6; color: white; border-color: #3b82f6; } .error-message { animation: shake 0.3s ease-in-out; } @keyframes shake { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-5px); } 75% { transform: translateX(5px); } } .success-check { animation: checkmark 0.4s ease-in-out; } @keyframes checkmark { 0% { transform: scale(0); opacity: 0; } 50% { transform: scale(1.2); } 100% { transform: scale(1); opacity: 1; } } .password-strength { height: 4px; transition: all 0.3s ease; } .glass-effect { background: rgba(255,255,255,0.1); backdrop-filter: blur(20px); } </style> </head> <body class="flex items-center justify-center p-4"> <div class="w-full max-w-md"> <!-- Логотип и заголовок --> <div class="text-center mb-8"> <div class="inline-flex items-center justify-center w-20 h-20 bg-white/10 backdrop-blur-md rounded-2xl mb-4 shadow-lg"> <i class="fas fa-route text-white text-4xl"></i> </div> <h1 class="text-3xl font-bold text-white mb-2">Логисмарт</h1> <p class="text-white/70 text-sm">Умная логистика с искусственным интеллектом</p> </div> <!-- Карточка авторизации/регистрации --> <div class="auth-card rounded-2xl shadow-2xl overflow-hidden"> <!-- Переключатель режимов --> <div class="flex border-b border-gray-100"> <button id="loginTabBtn" class="toggle-btn flex-1 py-4 text-center font-semibold transition-all active"> <i class="fas fa-sign-in-alt mr-2"></i> Вход </button> <button id="registerTabBtn" class="toggle-btn flex-1 py-4 text-center font-semibold text-gray-500 transition-all"> <i class="fas fa-user-plus mr-2"></i> Регистрация </button> </div> <!-- Форма входа --> <div id="loginForm" class="p-6 space-y-5"> <div> <label class="block text-sm font-medium text-gray-700 mb-2"> <i class="fas fa-envelope text-gray-400 mr-1"></i> Email </label> <input type="email" id="loginEmail" placeholder="ivan@example.com" class="input-field w-full px-4 py-3 rounded-xl bg-gray-50 focus:bg-white"> <div id="loginEmailError" class="text-xs text-red-500 mt-1 hidden"></div> </div> <div> <label class="block text-sm font-medium text-gray-700 mb-2"> <i class="fas fa-lock text-gray-400 mr-1"></i> Пароль </label> <div class="relative"> <input type="password" id="loginPassword" placeholder="••••••••" class="input-field w-full px-4 py-3 rounded-xl bg-gray-50 focus:bg-white"> <button type="button" id="toggleLoginPassword" class="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600"> <i class="fas fa-eye"></i> </button> </div> <div id="loginPasswordError" class="text-xs text-red-500 mt-1 hidden"></div> </div> <div class="flex items-center justify-between"> <label class="flex items-center gap-2 text-sm text-gray-600"> <input type="checkbox" id="rememberMe" class="rounded border-gray-300 text-blue-600 focus:ring-blue-500"> <span>Запомнить меня</span> </label> <a href="#" id="forgotPassword" class="text-sm text-blue-600 hover:text-blue-700">Забыли пароль?</a> </div> <button id="loginBtn" class="btn-primary w-full text-white py-3 rounded-xl font-semibold"> <i class="fas fa-arrow-right-to-bracket mr-2"></i> Войти </button> <div class="relative my-6"> <div class="absolute inset-0 flex items-center"> <div class="w-full border-t border-gray-200"></div> </div> <div class="relative flex justify-center text-sm"> <span class="px-4 bg-white text-gray-500">Или продолжить с</span> </div> </div> <div class="grid grid-cols-2 gap-3"> <button class="flex items-center justify-center gap-2 border border-gray-200 rounded-xl py-2.5 hover:bg-gray-50 transition"> <i class="fab fa-google text-red-500"></i> <span class="text-sm">Google</span> </button> <button class="flex items-center justify-center gap-2 border border-gray-200 rounded-xl py-2.5 hover:bg-gray-50 transition"> <i class="fab fa-github text-gray-800"></i> <span class="text-sm">GitHub</span> </button> </div> </div> <!-- Форма регистрации --> <div id="registerForm" class="p-6 space-y-4 hidden"> <div> <label class="block text-sm font-medium text-gray-700 mb-2"> <i class="fas fa-user text-gray-400 mr-1"></i> Имя </label> <input type="text" id="regName" placeholder="Иван Иванов" class="input-field w-full px-4 py-3 rounded-xl bg-gray-50 focus:bg-white"> <div id="regNameError" class="text-xs text-red-500 mt-1 hidden"></div> </div> <div> <label class="block text-sm font-medium text-gray-700 mb-2"> <i class="fas fa-envelope text-gray-400 mr-1"></i> Email </label> <input type="email" id="regEmail" placeholder="ivan@example.com" class="input-field w-full px-4 py-3 rounded-xl bg-gray-50 focus:bg-white"> <div id="regEmailError" class="text-xs text-red-500 mt-1 hidden"></div> </div> <div> <label class="block text-sm font-medium text-gray-700 mb-2"> <i class="fas fa-lock text-gray-400 mr-1"></i> Пароль </label> <div class="relative"> <input type="password" id="regPassword" placeholder="Минимум 6 символов" class="input-field w-full px-4 py-3 rounded-xl bg-gray-50 focus:bg-white"> <button type="button" id="toggleRegPassword" class="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600"> <i class="fas fa-eye"></i> </button> </div> <div class="mt-2"> <div class="password-strength w-full bg-gray-200 rounded-full overflow-hidden"> <div id="passwordStrengthBar" class="h-1 w-0 transition-all duration-300"></div> </div> <p id="passwordStrengthText" class="text-xs text-gray-500 mt-1"></p> </div> <div id="regPasswordError" class="text-xs text-red-500 mt-1 hidden"></div> </div> <div> <label class="block text-sm font-medium text-gray-700 mb-2"> <i class="fas fa-check-circle text-gray-400 mr-1"></i> Подтвердите пароль </label> <div class="relative"> <input type="password" id="regConfirmPassword" placeholder="Повторите пароль" class="input-field w-full px-4 py-3 rounded-xl bg-gray-50 focus:bg-white"> <button type="button" id="toggleRegConfirmPassword" class="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600"> <i class="fas fa-eye"></i> </button> </div> <div id="regConfirmError" class="text-xs text-red-500 mt-1 hidden"></div> </div> <label class="flex items-center gap-2 text-sm text-gray-600"> <input type="checkbox" id="agreeTerms" class="rounded border-gray-300 text-blue-600 focus:ring-blue-500"> <span>Я соглашаюсь с <a href="#" class="text-blue-600">условиями использования</a></span> </label> <button id="registerBtn" class="btn-primary w-full text-white py-3 rounded-xl font-semibold"> <i class="fas fa-user-plus mr-2"></i> Зарегистрироваться </button> <p class="text-center text-xs text-gray-500"> Регистрируясь, вы подтверждаете, что вам есть 18 лет </p> </div> </div> <!-- Дополнительная информация --> <div class="text-center mt-6 text-white/50 text-xs"> <p>© 2026 Логисмарт — AI-диспетчер для грузоперевозок</p> <p class="mt-1">Версия 2.0 | Защищено шифрованием 256-bit</p> </div> </div> <!-- Уведомления Toast --> <div id="toastContainer" class="fixed bottom-4 right-4 z-50 space-y-2"></div> <script> // ========== API CONFIG ========== const API_BASE_URL = 'http://localhost:8000'; async function apiRegister(name, email, password) { const response = await fetch(`${API_BASE_URL}/register`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name, email, password }) }); if (!response.ok) { const error = await response.json(); throw new Error(error.detail || 'Registration failed'); } return await response.json(); } async function apiLogin(email, password) { const formData = new FormData(); formData.append('username', email); formData.append('password', password); const response = await fetch(`${API_BASE_URL}/token`, { method: 'POST', body: formData }); if (!response.ok) { const error = await response.json(); throw new Error(error.detail || 'Login failed'); } return await response.json(); } async function apiGetCurrentUser(token) { const response = await fetch(`${API_BASE_URL}/users/me`, { headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) throw new Error('Unauthorized'); return await response.json(); } // ========== LOCAL STORAGE HELPERS (for token storage) ========== let currentUser = null; function checkSession() { const token = localStorage.getItem('logismart_token'); if (token) { try { const payload = JSON.parse(atob(token.split('.')[1])); if (payload.exp * 1000 > Date.now()) { return true; } else { localStorage.removeItem('logismart_token'); localStorage.removeItem('logismart_user'); } } catch(e) {} } return false; } function createSession(token, user, remember = false) { localStorage.setItem('logismart_token', token); localStorage.setItem('logismart_user', JSON.stringify(user)); } function redirectToApp() { localStorage.setItem('logismart_auth', 'true'); showToast('Успешный вход! Перенаправление...', 'success'); setTimeout(() => { window.location.href = 'app.html'; }, 1000); } // ========== ВАЛИДАЦИЯ ========== function validateEmail(email) { const re = /^[^\s@]+@([^\s@.,]+\.)+[^\s@.,]{2,}$/; return re.test(email); } function validatePasswordStrength(password) { let strength = 0; if (password.length >= 6) strength++; if (password.length >= 10) strength++; if (/[A-Z]/.test(password)) strength++; if (/[0-9]/.test(password)) strength++; if (/[^A-Za-z0-9]/.test(password)) strength++; return { strength: Math.min(strength, 4), label: ['Очень слабый', 'Слабый', 'Средний', 'Хороший', 'Отличный'][strength] }; } function updatePasswordStrength(password) { const bar = document.getElementById('passwordStrengthBar'); const text = document.getElementById('passwordStrengthText'); if (!bar || !text) return; const { strength, label } = validatePasswordStrength(password); const colors = ['#ef4444', '#f59e0b', '#eab308', '#3b82f6', '#10b981']; const widths = ['0%', '25%', '50%', '75%', '100%']; bar.style.width = widths[strength]; bar.style.backgroundColor = colors[strength]; text.textContent = password ? `Надёжность: ${label}` : ''; text.className = `text-xs mt-1 ${strength >= 3 ? 'text-green-600' : strength >= 2 ? 'text-yellow-600' : 'text-red-500'}`; return strength >= 2; } // ========== ПОКАЗ УВЕДОМЛЕНИЙ ========== function showToast(message, type = 'success') { const container = document.getElementById('toastContainer'); const toast = document.createElement('div'); const icons = { success: '<i class="fas fa-check-circle"></i>', error: '<i class="fas fa-exclamation-circle"></i>', warning: '<i class="fas fa-triangle-exclamation"></i>', info: '<i class="fas fa-info-circle"></i>' }; const bgColors = { success: 'bg-green-500', error: 'bg-red-500', warning: 'bg-amber-500', info: 'bg-blue-500' }; toast.className = `${bgColors[type]} text-white px-5 py-3 rounded-xl shadow-lg flex items-center gap-2 text-sm transform transition-all duration-300 translate-x-full`; toast.innerHTML = `${icons[type]} ${message}`; container.appendChild(toast); setTimeout(() => { toast.classList.remove('translate-x-full'); toast.classList.add('translate-x-0'); }, 10); setTimeout(() => { toast.classList.remove('translate-x-0'); toast.classList.add('translate-x-full'); setTimeout(() => toast.remove(), 300); }, 3000); } // ========== ОБРАБОТЧИКИ ФОРМ ========== // Вход async function handleLogin() { const email = document.getElementById('loginEmail').value.trim(); const password = document.getElementById('loginPassword').value; const remember = document.getElementById('rememberMe').checked; document.getElementById('loginEmailError').classList.add('hidden'); document.getElementById('loginPasswordError').classList.add('hidden'); document.getElementById('loginEmail').classList.remove('error'); document.getElementById('loginPassword').classList.remove('error'); let hasError = false; if (!email) { document.getElementById('loginEmailError').textContent = 'Введите email'; document.getElementById('loginEmailError').classList.remove('hidden'); document.getElementById('loginEmail').classList.add('error'); hasError = true; } else if (!validateEmail(email)) { document.getElementById('loginEmailError').textContent = 'Введите корректный email'; document.getElementById('loginEmailError').classList.remove('hidden'); document.getElementById('loginEmail').classList.add('error'); hasError = true; } if (!password) { document.getElementById('loginPasswordError').textContent = 'Введите пароль'; document.getElementById('loginPasswordError').classList.remove('hidden'); document.getElementById('loginPassword').classList.add('error'); hasError = true; } if (hasError) return; try { const data = await apiLogin(email, password); const user = { id: 0, name: email, email: email }; createSession(data.access_token, user, remember); showToast(`Добро пожаловать, ${email}!`, 'success'); redirectToApp(); } catch (error) { showToast(error.message || 'Неверный email или пароль', 'error'); document.getElementById('loginPassword').classList.add('error'); } } // Регистрация async function handleRegister() { const name = document.getElementById('regName').value.trim(); const email = document.getElementById('regEmail').value.trim(); const password = document.getElementById('regPassword').value; const confirmPassword = document.getElementById('regConfirmPassword').value; const agreeTerms = document.getElementById('agreeTerms').checked; const errorFields = ['regName', 'regEmail', 'regPassword', 'regConfirm']; errorFields.forEach(f => { const el = document.getElementById(`${f}Error`); if (el) el.classList.add('hidden'); const input = document.getElementById(f); if (input) input.classList.remove('error'); }); let hasError = false; if (!name) { document.getElementById('regNameError').textContent = 'Введите ваше имя'; document.getElementById('regNameError').classList.remove('hidden'); document.getElementById('regName').classList.add('error'); hasError = true; } else if (name.length < 2) { document.getElementById('regNameError').textContent = 'Имя должно содержать минимум 2 символа'; document.getElementById('regNameError').classList.remove('hidden'); document.getElementById('regName').classList.add('error'); hasError = true; } if (!email) { document.getElementById('regEmailError').textContent = 'Введите email'; document.getElementById('regEmailError').classList.remove('hidden'); document.getElementById('regEmail').classList.add('error'); hasError = true; } else if (!validateEmail(email)) { document.getElementById('regEmailError').textContent = 'Введите корректный email'; document.getElementById('regEmailError').classList.remove('hidden'); document.getElementById('regEmail').classList.add('error'); hasError = true; } if (!password) { document.getElementById('regPasswordError').textContent = 'Введите пароль'; document.getElementById('regPasswordError').classList.remove('hidden'); document.getElementById('regPassword').classList.add('error'); hasError = true; } else if (password.length < 6) { document.getElementById('regPasswordError').textContent = 'Пароль должен содержать минимум 6 символов'; document.getElementById('regPasswordError').classList.remove('hidden'); document.getElementById('regPassword').classList.add('error'); hasError = true; } if (password !== confirmPassword) { document.getElementById('regConfirmError').textContent = 'Пароли не совпадают'; document.getElementById('regConfirmError').classList.remove('hidden'); document.getElementById('regConfirmPassword').classList.add('error'); hasError = true; } if (!agreeTerms) { showToast('Подтвердите согласие с условиями использования', 'warning'); hasError = true; } if (hasError) return; try { await apiRegister(name, email, password); showToast('Регистрация успешна! Теперь войдите в систему', 'success'); switchToLogin(); document.getElementById('regName').value = ''; document.getElementById('regEmail').value = ''; document.getElementById('regPassword').value = ''; document.getElementById('regConfirmPassword').value = ''; document.getElementById('agreeTerms').checked = false; updatePasswordStrength(''); } catch (error) { showToast(error.message || 'Ошибка регистрации', 'error'); } } // Переключение между формами function switchToLogin() { document.getElementById('loginForm').classList.remove('hidden'); document.getElementById('registerForm').classList.add('hidden'); document.getElementById('loginTabBtn').classList.add('active', 'bg-blue-600', 'text-white'); document.getElementById('loginTabBtn').classList.remove('text-gray-500'); document.getElementById('registerTabBtn').classList.remove('active', 'bg-blue-600', 'text-white'); document.getElementById('registerTabBtn').classList.add('text-gray-500'); } function switchToRegister() { document.getElementById('loginForm').classList.add('hidden'); document.getElementById('registerForm').classList.remove('hidden'); document.getElementById('registerTabBtn').classList.add('active', 'bg-blue-600', 'text-white'); document.getElementById('registerTabBtn').classList.remove('text-gray-500'); document.getElementById('loginTabBtn').classList.remove('active', 'bg-blue-600', 'text-white'); document.getElementById('loginTabBtn').classList.add('text-gray-500'); } // Переключение видимости пароля function togglePassword(inputId, btnId) { const input = document.getElementById(inputId); const btn = document.getElementById(btnId); if (input.type === 'password') { input.type = 'text'; btn.innerHTML = '<i class="fas fa-eye-slash"></i>'; } else { input.type = 'password'; btn.innerHTML = '<i class="fas fa-eye"></i>'; } } // Восстановление пароля (демо) function handleForgotPassword() { showToast('Инструкции по восстановлению отправлены на вашу почту', 'info'); } // ========== ИНИЦИАЛИЗАЦИЯ ========== function init() { if (checkSession()) { window.location.href = 'app.html'; return; } // Привязка событий document.getElementById('loginBtn').addEventListener('click', handleLogin); document.getElementById('registerBtn').addEventListener('click', handleRegister); document.getElementById('loginTabBtn').addEventListener('click', switchToLogin); document.getElementById('registerTabBtn').addEventListener('click', switchToRegister); document.getElementById('forgotPassword').addEventListener('click', (e) => { e.preventDefault(); handleForgotPassword(); }); // Переключение видимости пароля document.getElementById('toggleLoginPassword').addEventListener('click', () => togglePassword('loginPassword', 'toggleLoginPassword')); document.getElementById('toggleRegPassword').addEventListener('click', () => togglePassword('regPassword', 'toggleRegPassword')); document.getElementById('toggleRegConfirmPassword').addEventListener('click', () => togglePassword('regConfirmPassword', 'toggleRegConfirmPassword')); // Проверка сложности пароля при вводе document.getElementById('regPassword').addEventListener('input', (e) => { updatePasswordStrength(e.target.value); }); // Вход по Enter const loginPassword = document.getElementById('loginPassword'); loginPassword.addEventListener('keypress', (e) => { if (e.key === 'Enter') handleLogin(); }); const loginEmail = document.getElementById('loginEmail'); loginEmail.addEventListener('keypress', (e) => { if (e.key === 'Enter') handleLogin(); }); } // Запуск init(); </script> </body> </html>