Files
frambe/public/admin/login.html
T

66 lines
3.8 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Frambe Admin — Login</title>
<style>
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
body { background: #0f0f1a; color: #e0e0e0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; display: flex; align-items: center; justify-content: center; min-height: 100vh; }
.login-card { background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.08); border-radius: 16px; padding: 2.5rem; width: 100%; max-width: 380px; }
.login-header { text-align: center; margin-bottom: 2rem; }
.login-header img { width: 56px; height: 56px; border-radius: 12px; margin-bottom: 1rem; }
.login-header h1 { font-size: 1.4rem; font-weight: 300; }
.login-header p { font-size: 0.8rem; color: #666; margin-top: 0.25rem; }
.form-group { margin-bottom: 1.25rem; }
.form-group label { display: block; font-size: 0.8rem; color: #888; margin-bottom: 0.4rem; }
.form-group input { width: 100%; padding: 10px 14px; background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.12); border-radius: 8px; color: #e0e0e0; font-size: 0.95rem; outline: none; transition: border-color 0.15s; }
.form-group input:focus { border-color: #6366f1; }
.login-btn { width: 100%; padding: 11px; background: rgba(99,102,241,0.2); border: 1px solid #6366f1; border-radius: 8px; color: #a5b4fc; font-size: 0.95rem; cursor: pointer; transition: all 0.15s; }
.login-btn:hover { background: rgba(99,102,241,0.35); }
.login-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.error-msg { background: rgba(239,68,68,0.12); border: 1px solid rgba(239,68,68,0.3); border-radius: 8px; color: #fca5a5; font-size: 0.85rem; padding: 8px 12px; margin-bottom: 1rem; display: none; }
</style>
</head>
<body>
<div class="login-card">
<div class="login-header">
<img src="/img/icon.png" alt="Frambe" onerror="this.style.display='none'">
<h1>Frambe Admin</h1>
<p>Sign in to manage your frames</p>
</div>
<div class="error-msg" id="error-msg"></div>
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" autocomplete="username" autofocus>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" autocomplete="current-password">
</div>
<button class="login-btn" id="login-btn" onclick="doLogin()">Sign In</button>
</div>
<script>
document.getElementById('password').addEventListener('keydown', function(e) { if (e.key === 'Enter') doLogin(); });
document.getElementById('username').addEventListener('keydown', function(e) { if (e.key === 'Enter') document.getElementById('password').focus(); });
async function doLogin() {
var btn = document.getElementById('login-btn');
var errEl = document.getElementById('error-msg');
btn.disabled = true;
errEl.style.display = 'none';
var username = document.getElementById('username').value.trim();
var password = document.getElementById('password').value;
if (!username || !password) { errEl.textContent = 'Please enter both username and password'; errEl.style.display = 'block'; btn.disabled = false; return; }
try {
var r = await fetch('/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: username, password: password }) });
var d = await r.json();
if (d.ok) { window.location.href = '/admin'; }
else { errEl.textContent = d.error || 'Login failed'; errEl.style.display = 'block'; }
} catch (e) { errEl.textContent = 'Connection error'; errEl.style.display = 'block'; }
btn.disabled = false;
}
</script>
</body>
</html>