Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Start Suite - Login</title> | |
| <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet"> | |
| <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> | |
| <link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='icon.png') }}"> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Audiowide&family=Lexend:wght@100..900&display=swap'); | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| font-family: "Lexend", sans-serif; | |
| } | |
| body { | |
| font-family: 'Roboto', sans-serif; | |
| background: #4285f4; | |
| min-height: 100vh; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| color: #333; | |
| overflow: hidden; | |
| position: relative; | |
| } | |
| /* Rain Effect Styles */ | |
| .rain-container { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| pointer-events: none; | |
| z-index: 1; | |
| } | |
| .rain-drop { | |
| position: absolute; | |
| background: linear-gradient(to bottom, rgba(255, 255, 255, 0.6), rgba(255, 255, 255, 0.2)); | |
| border-radius: 50px; | |
| animation: fall linear infinite; | |
| opacity: 0.7; | |
| } | |
| @keyframes fall { | |
| 0% { | |
| transform: translateY(-20px); | |
| opacity: 1; | |
| } | |
| 100% { | |
| transform: translateY(100vh); | |
| opacity: 0.3; | |
| } | |
| } | |
| .auth-container { | |
| background: rgba(255, 255, 255, 0.95); | |
| border-radius: 20px; | |
| padding: 40px; | |
| width: 100%; | |
| max-width: 400px; | |
| box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); | |
| backdrop-filter: blur(10px); | |
| position: relative; | |
| z-index: 2; | |
| } | |
| .auth-header { | |
| text-align: center; | |
| margin-bottom: 30px; | |
| } | |
| .auth-title { | |
| font-size: 2rem; | |
| font-weight: 700; | |
| color: #333; | |
| margin-bottom: 10px; | |
| } | |
| .auth-subtitle { | |
| color: #666; | |
| font-size: 1rem; | |
| } | |
| .auth-form { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 20px; | |
| } | |
| .form-group { | |
| position: relative; | |
| } | |
| .form-input { | |
| width: calc(100% - 42px); | |
| padding: 15px 20px; | |
| border: 2px solid #e1e5e9; | |
| border-radius: 10px; | |
| font-size: 1rem; | |
| transition: all 0.3s ease; | |
| background: white; | |
| } | |
| .form-input:focus { | |
| outline: none; | |
| border-color: #667eea; | |
| box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); | |
| } | |
| .form-input::placeholder { | |
| color: #999; | |
| } | |
| .checkbox-group { | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| margin-top: 5px; | |
| } | |
| .checkbox-input { | |
| width: 18px; | |
| height: 18px; | |
| accent-color: #667eea; | |
| cursor: pointer; | |
| } | |
| .checkbox-label { | |
| font-size: 0.9rem; | |
| color: #666; | |
| cursor: pointer; | |
| user-select: none; | |
| } | |
| .auth-btn { | |
| padding: 15px 30px; | |
| border: none; | |
| border-radius: 10px; | |
| font-size: 1rem; | |
| font-weight: 500; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| background: #667eea; | |
| color: white; | |
| } | |
| .auth-btn:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3); | |
| } | |
| .auth-btn:active { | |
| transform: translateY(0); | |
| } | |
| .auth-toggle { | |
| text-align: center; | |
| margin-top: 20px; | |
| } | |
| .auth-toggle a { | |
| color: #667eea; | |
| text-decoration: none; | |
| font-weight: 500; | |
| cursor: pointer; | |
| } | |
| .auth-toggle a:hover { | |
| text-decoration: underline; | |
| } | |
| .error-message { | |
| color: #e74c3c; | |
| font-size: 0.9rem; | |
| margin-top: 10px; | |
| text-align: center; | |
| } | |
| .success-message { | |
| color: #27ae60; | |
| font-size: 0.9rem; | |
| margin-top: 10px; | |
| text-align: center; | |
| } | |
| .hidden { | |
| display: none; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="rain-container" id="rainContainer"></div> | |
| <div class="auth-container"> | |
| <div class="auth-header"> | |
| <h1 class="auth-title">Start Suite</h1> | |
| <p class="auth-subtitle">Your personalized dashboard</p> | |
| </div> | |
| <form class="auth-form" id="loginForm"> | |
| <div class="form-group"> | |
| <input type="text" class="form-input" id="username" placeholder="Username" required> | |
| </div> | |
| <div class="form-group"> | |
| <input type="password" class="form-input" id="password" placeholder="Password" required> | |
| </div> | |
| <div class="form-group" id="rememberMeGroup"> | |
| <div class="checkbox-group"> | |
| <input type="checkbox" class="checkbox-input" id="rememberMe" checked> | |
| <label for="rememberMe" class="checkbox-label">Remember me for 30 days</label> | |
| </div> | |
| </div> | |
| <button type="submit" class="auth-btn" id="authBtn">Login</button> | |
| <div class="error-message hidden" id="errorMessage"></div> | |
| <div class="success-message hidden" id="successMessage"></div> | |
| </form> | |
| <div class="auth-toggle"> | |
| <span id="toggleText">Don't have an account? </span> | |
| <a href="#" id="toggleLink">Sign up</a> | |
| </div> | |
| </div> | |
| <script> | |
| let isLoginMode = true; | |
| const form = document.getElementById('loginForm'); | |
| const authBtn = document.getElementById('authBtn'); | |
| const toggleLink = document.getElementById('toggleLink'); | |
| const toggleText = document.getElementById('toggleText'); | |
| const errorMessage = document.getElementById('errorMessage'); | |
| const successMessage = document.getElementById('successMessage'); | |
| const rememberMeGroup = document.getElementById('rememberMeGroup'); | |
| const rememberMeCheckbox = document.getElementById('rememberMe'); | |
| function showError(message) { | |
| errorMessage.textContent = message; | |
| errorMessage.classList.remove('hidden'); | |
| successMessage.classList.add('hidden'); | |
| } | |
| function showSuccess(message) { | |
| successMessage.textContent = message; | |
| successMessage.classList.remove('hidden'); | |
| errorMessage.classList.add('hidden'); | |
| } | |
| function hideMessages() { | |
| errorMessage.classList.add('hidden'); | |
| successMessage.classList.add('hidden'); | |
| } | |
| toggleLink.addEventListener('click', (e) => { | |
| e.preventDefault(); | |
| isLoginMode = !isLoginMode; | |
| if (isLoginMode) { | |
| authBtn.textContent = 'Login'; | |
| toggleText.textContent = "Don't have an account? "; | |
| toggleLink.textContent = 'Sign up'; | |
| rememberMeGroup.style.display = 'block'; // Show remember me for login | |
| } else { | |
| authBtn.textContent = 'Sign Up'; | |
| toggleText.textContent = 'Already have an account? '; | |
| toggleLink.textContent = 'Login'; | |
| rememberMeGroup.style.display = 'none'; // Hide remember me for signup | |
| } | |
| hideMessages(); | |
| }); | |
| form.addEventListener('submit', async (e) => { | |
| e.preventDefault(); | |
| const username = document.getElementById('username').value; | |
| const password = document.getElementById('password').value; | |
| const rememberMe = rememberMeCheckbox.checked; | |
| if (!username || !password) { | |
| showError('Please fill in all fields'); | |
| return; | |
| } | |
| const endpoint = isLoginMode ? '/login' : '/register'; | |
| const requestBody = { username, password }; | |
| // Add rememberMe flag for login requests | |
| if (isLoginMode) { | |
| requestBody.rememberMe = rememberMe; | |
| } | |
| try { | |
| const response = await fetch(endpoint, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify(requestBody) | |
| }); | |
| const data = await response.json(); | |
| if (response.ok) { | |
| showSuccess(data.message); | |
| setTimeout(() => { | |
| window.location.href = '/'; | |
| }, 1000); | |
| } else { | |
| showError(data.error); | |
| } | |
| } catch (error) { | |
| showError('An error occurred. Please try again.'); | |
| } | |
| }); | |
| // Rain Effect | |
| class RainEffect { | |
| constructor() { | |
| this.rainContainer = document.getElementById('rainContainer'); | |
| this.rainDrops = []; | |
| this.maxDrops = 100; | |
| this.init(); | |
| } | |
| init() { | |
| this.createRain(); | |
| this.animateRain(); | |
| } | |
| createRain() { | |
| for (let i = 0; i < this.maxDrops; i++) { | |
| this.createRainDrop(); | |
| } | |
| } | |
| createRainDrop() { | |
| const drop = document.createElement('div'); | |
| drop.className = 'rain-drop'; | |
| // Random horizontal position | |
| const x = Math.random() * window.innerWidth; | |
| // Random size variation | |
| const size = Math.random() * 0.8 + 0.2; | |
| drop.style.width = `${2 * size}px`; | |
| drop.style.height = `${20 * size}px`; | |
| // Random speed (duration) | |
| const duration = Math.random() * 2 + 1; // 1-3 seconds | |
| drop.style.animationDuration = `${duration}s`; | |
| // Random delay | |
| const delay = Math.random() * 2; | |
| drop.style.animationDelay = `${delay}s`; | |
| // Position the drop | |
| drop.style.left = `${x}px`; | |
| drop.style.top = '-20px'; | |
| this.rainContainer.appendChild(drop); | |
| this.rainDrops.push(drop); | |
| } | |
| animateRain() { | |
| // Clean up and recreate drops periodically | |
| setInterval(() => { | |
| this.rainDrops.forEach(drop => { | |
| const rect = drop.getBoundingClientRect(); | |
| if (rect.top > window.innerHeight) { | |
| // Reset the drop to the top with new random position | |
| drop.style.left = `${Math.random() * window.innerWidth}px`; | |
| drop.style.top = '-20px'; | |
| // Randomize properties again | |
| const size = Math.random() * 0.8 + 0.2; | |
| drop.style.width = `${2 * size}px`; | |
| drop.style.height = `${20 * size}px`; | |
| const duration = Math.random() * 2 + 1; | |
| drop.style.animationDuration = `${duration}s`; | |
| } | |
| }); | |
| }, 100); | |
| } | |
| } | |
| // Initialize rain effect when the page loads | |
| new RainEffect(); | |
| </script> | |
| </body> | |
| </html> |