Files
urllists/static/templates/login.html
T
2025-11-30 12:19:31 +00:00

223 lines
7.5 KiB
HTML

{% extends "base.html" %}
{% block title %}Login - {{ app_name }}{% endblock %}
{% block content %}
<div class="min-h-screen flex items-center justify-center -mt-8">
<div class="w-full max-w-md">
<div class="card p-6 md:p-8">
<h2 class="text-3xl font-bold mb-2 text-center">Sign In</h2>
<p class="text-center text-slate-400 mb-6">Access your URL lists</p>
<div id="errorMsg" class="hidden mb-4 alert alert-error"></div>
<div id="successMsg" class="hidden mb-4 alert alert-success"></div>
<form id="loginForm" class="space-y-4" novalidate>
<div class="form-group">
<label for="email" class="form-label">Email Address</label>
<input
type="email"
name="email"
id="email"
required
class="form-input"
placeholder="you@example.com"
autocomplete="email"
maxlength="254"
>
<span id="emailError" class="text-xs text-red-400 mt-1 hidden"></span>
</div>
<div class="form-group">
<label for="password" class="form-label">Password</label>
<input
type="password"
name="password"
id="password"
required
class="form-input"
placeholder="••••••••"
autocomplete="current-password"
maxlength="128"
>
<span id="passwordError" class="text-xs text-red-400 mt-1 hidden"></span>
</div>
<div class="flex items-center">
<input
type="checkbox"
name="remember"
id="remember"
class="w-4 h-4 bg-slate-700 border border-slate-600 rounded cursor-pointer"
>
<label for="remember" class="ml-2 text-sm text-slate-400 cursor-pointer">
Remember me
</label>
</div>
<button
type="submit"
class="btn-primary w-full mt-6"
id="submitBtn"
>
Sign In
</button>
</form>
<div class="mt-6 text-center">
<p class="text-slate-400">
Don't have an account?
<a href="{{ url_for('register') }}" class="text-sky-400 hover:text-sky-300 font-semibold transition-colors">
Sign up
</a>
</p>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
if (isAuthenticated()) {
window.location.href = "{{ url_for('dashboard') }}";
}
});
const loginForm = document.getElementById('loginForm');
const errorMsg = document.getElementById('errorMsg');
const successMsg = document.getElementById('successMsg');
const submitBtn = document.getElementById('submitBtn');
function showError(message) {
errorMsg.textContent = message;
errorMsg.classList.remove('hidden');
successMsg.classList.add('hidden');
}
function showSuccess(message) {
successMsg.textContent = message;
successMsg.classList.remove('hidden');
errorMsg.classList.add('hidden');
}
function clearMessages() {
errorMsg.classList.add('hidden');
successMsg.classList.add('hidden');
}
function validateEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email) && email.length <= 254;
}
function validateForm() {
clearMessages();
const email = document.getElementById('email').value.trim();
const password = document.getElementById('password').value;
const emailError = document.getElementById('emailError');
const passwordError = document.getElementById('passwordError');
let isValid = true;
// Validate email
if (!email) {
emailError.textContent = 'Email is required';
emailError.classList.remove('hidden');
isValid = false;
} else if (!validateEmail(email)) {
emailError.textContent = 'Please enter a valid email address';
emailError.classList.remove('hidden');
isValid = false;
} else {
emailError.classList.add('hidden');
}
// Validate password
if (!password) {
passwordError.textContent = 'Password is required';
passwordError.classList.remove('hidden');
isValid = false;
} else if (password.length < 8) {
passwordError.textContent = 'Password must be at least 8 characters';
passwordError.classList.remove('hidden');
isValid = false;
} else {
passwordError.classList.add('hidden');
}
return isValid;
}
loginForm.addEventListener('submit', async (e) => {
e.preventDefault();
// Validate form
if (!validateForm()) {
showError('Please correct the errors above');
return;
}
// Get form values
const email = document.getElementById('email').value.trim().toLowerCase();
const password = document.getElementById('password').value;
const remember = document.getElementById('remember').checked;
// Disable submit button
submitBtn.disabled = true;
submitBtn.textContent = 'Signing in...';
try {
// Use FormData for proper content-type
const formData = new FormData();
formData.append('username', email); // FastAPI-users expects 'username'
formData.append('password', password);
const response = await fetch('/api/auth/jwt/login', {
method: 'POST',
body: formData
});
if (response.ok) {
const data = await response.json();
// Store token with proper key name
localStorage.setItem('access_token', data.access_token);
// Store remember me preference
if (remember) {
localStorage.setItem('rememberMe', 'true');
} else {
localStorage.removeItem('rememberMe');
}
showSuccess('Login successful! Redirecting...');
// Redirect to dashboard
setTimeout(() => {
window.location.href = "{{ url_for('dashboard') }}";
}, 500);
} else {
const error = await response.json();
showError(error.detail || 'Login failed. Please check your credentials.');
submitBtn.disabled = false;
submitBtn.textContent = 'Sign In';
}
} catch (error) {
console.error('Login error:', error);
showError('An error occurred during login. Please try again.');
submitBtn.disabled = false;
submitBtn.textContent = 'Sign In';
}
});
// Clear inline errors on input
document.getElementById('email').addEventListener('input', () => {
document.getElementById('emailError').classList.add('hidden');
});
document.getElementById('password').addEventListener('input', () => {
document.getElementById('passwordError').classList.add('hidden');
});
</script>
{% endblock %}