240 lines
8.5 KiB
HTML
240 lines
8.5 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Login – Journey Mapper</title>
|
||
|
||
<!-- Open Props CSS -->
|
||
<link rel="stylesheet" href="https://unpkg.com/open-props"/>
|
||
<link rel="stylesheet" href="https://unpkg.com/open-props/normalize.min.css"/>
|
||
|
||
<!-- Font Awesome -->
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||
|
||
<!-- Google Fonts -->
|
||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||
|
||
<style>
|
||
* { box-sizing: border-box; }
|
||
body {
|
||
font-family: var(--font-sans);
|
||
background: var(--gray-0);
|
||
color: var(--gray-9);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
min-height: 100vh;
|
||
margin: 0;
|
||
padding: var(--size-4);
|
||
}
|
||
|
||
.login-container {
|
||
max-width: 400px;
|
||
width: 100%;
|
||
background: var(--surface-1);
|
||
border-radius: var(--radius-3);
|
||
padding: var(--size-6);
|
||
box-shadow: var(--shadow-4);
|
||
}
|
||
|
||
.login-header {
|
||
text-align: center;
|
||
margin-bottom: var(--size-6);
|
||
}
|
||
|
||
.login-header h1 {
|
||
color: var(--indigo-8);
|
||
margin: 0;
|
||
font-size: var(--font-size-5);
|
||
}
|
||
|
||
.auth-tabs {
|
||
display: flex;
|
||
gap: var(--size-2);
|
||
border-bottom: 1px solid var(--surface-4);
|
||
margin-bottom: var(--size-4);
|
||
}
|
||
.auth-tab {
|
||
padding: var(--size-2) var(--size-4);
|
||
cursor: pointer;
|
||
background: none;
|
||
border: none;
|
||
font-size: var(--font-size-2);
|
||
font-weight: var(--font-weight-5);
|
||
color: var(--gray-6);
|
||
transition: all 0.2s;
|
||
}
|
||
.auth-tab.active {
|
||
color: var(--indigo-7);
|
||
border-bottom: 2px solid var(--indigo-7);
|
||
}
|
||
.auth-form {
|
||
display: none;
|
||
}
|
||
.auth-form.active {
|
||
display: block;
|
||
}
|
||
.form-group {
|
||
margin-bottom: var(--size-4);
|
||
}
|
||
label {
|
||
display: block;
|
||
margin-bottom: var(--size-1);
|
||
font-weight: var(--font-weight-5);
|
||
}
|
||
input {
|
||
width: 100%;
|
||
padding: var(--size-2) var(--size-3);
|
||
border: 1px solid var(--surface-4);
|
||
border-radius: var(--radius-2);
|
||
background: var(--surface-2);
|
||
font-size: var(--font-size-2);
|
||
}
|
||
.btn {
|
||
width: 100%;
|
||
padding: var(--size-2);
|
||
border: none;
|
||
border-radius: var(--radius-2);
|
||
background: var(--indigo-7);
|
||
color: white;
|
||
font-size: var(--font-size-2);
|
||
font-weight: var(--font-weight-5);
|
||
cursor: pointer;
|
||
transition: background 0.2s;
|
||
}
|
||
.btn:hover {
|
||
background: var(--indigo-8);
|
||
}
|
||
.toast {
|
||
position: fixed;
|
||
bottom: var(--size-4);
|
||
right: var(--size-4);
|
||
background: var(--green-7);
|
||
color: white;
|
||
padding: var(--size-2) var(--size-4);
|
||
border-radius: var(--radius-2);
|
||
display: none;
|
||
z-index: 1100;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="login-container">
|
||
<div class="login-header">
|
||
<h1><i class="fas fa-map-marked-alt"></i> Journey Mapper</h1>
|
||
<p>Sign in to continue</p>
|
||
</div>
|
||
<div class="auth-tabs">
|
||
<button class="auth-tab active" data-tab="login">Login</button>
|
||
<button class="auth-tab" data-tab="register">Register</button>
|
||
</div>
|
||
<div id="login-form" class="auth-form active">
|
||
<div class="form-group">
|
||
<label>Username</label>
|
||
<input type="text" id="login-username" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Password</label>
|
||
<input type="password" id="login-password" required>
|
||
</div>
|
||
<button id="login-submit" class="btn">Login</button>
|
||
</div>
|
||
<div id="register-form" class="auth-form">
|
||
<div class="form-group">
|
||
<label>Username</label>
|
||
<input type="text" id="register-username" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Password</label>
|
||
<input type="password" id="register-password" required>
|
||
</div>
|
||
<button id="register-submit" class="btn">Register</button>
|
||
</div>
|
||
</div>
|
||
<div id="toast" class="toast"></div>
|
||
|
||
<script>
|
||
const API_BASE = 'http://127.0.0.1:5000/api';
|
||
|
||
function showToast(msg, isError = false) {
|
||
const toast = document.getElementById('toast');
|
||
toast.textContent = msg;
|
||
toast.style.backgroundColor = isError ? 'var(--red-7)' : 'var(--green-7)';
|
||
toast.style.display = 'block';
|
||
setTimeout(() => { toast.style.display = 'none'; }, 3000);
|
||
}
|
||
|
||
async function login(username, password) {
|
||
try {
|
||
const res = await fetch(`${API_BASE}/login`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ username, password }),
|
||
credentials: 'include'
|
||
});
|
||
const data = await res.json();
|
||
if (!res.ok) throw new Error(data.error || 'Login failed');
|
||
showToast(`Welcome, ${data.username}!`);
|
||
window.location.href = 'map-page.html';
|
||
} catch (err) {
|
||
showToast(err.message, true);
|
||
}
|
||
}
|
||
|
||
async function register(username, password) {
|
||
try {
|
||
const res = await fetch(`${API_BASE}/register`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ username, password }),
|
||
credentials: 'include'
|
||
});
|
||
const data = await res.json();
|
||
if (!res.ok) throw new Error(data.error || 'Registration failed');
|
||
showToast(`Registered as ${data.username}. Logging in...`);
|
||
window.location.href = 'map-page.html';
|
||
} catch (err) {
|
||
showToast(err.message, true);
|
||
}
|
||
}
|
||
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
// Tab switching
|
||
document.querySelectorAll('.auth-tab').forEach(tab => {
|
||
tab.addEventListener('click', () => {
|
||
const target = tab.dataset.tab;
|
||
document.querySelectorAll('.auth-tab').forEach(t => t.classList.remove('active'));
|
||
tab.classList.add('active');
|
||
document.querySelectorAll('.auth-form').forEach(f => f.classList.remove('active'));
|
||
document.getElementById(`${target}-form`).classList.add('active');
|
||
});
|
||
});
|
||
// Login button
|
||
document.getElementById('login-submit').addEventListener('click', () => {
|
||
const username = document.getElementById('login-username').value.trim();
|
||
const password = document.getElementById('login-password').value;
|
||
if (!username || !password) {
|
||
showToast('Please enter username and password', true);
|
||
return;
|
||
}
|
||
login(username, password);
|
||
});
|
||
// Register button
|
||
document.getElementById('register-submit').addEventListener('click', () => {
|
||
const username = document.getElementById('register-username').value.trim();
|
||
const password = document.getElementById('register-password').value;
|
||
if (!username || !password) {
|
||
showToast('Please enter username and password', true);
|
||
return;
|
||
}
|
||
if (password.length < 4) {
|
||
showToast('Password must be at least 4 characters', true);
|
||
return;
|
||
}
|
||
register(username, password);
|
||
});
|
||
});
|
||
</script>
|
||
</body>
|
||
</html> |