Something went wrong. Try again.
Lotting software suite
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233<!doctype html><html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Sign In</title> <style> :root { --bg: #0f172a; --card: #1e293b; --text: #f1f5f9; --accent: #38bdf8; --error: #f87171; } * { box-sizing: border-box; margin: 0; padding: 0; } body { display: grid; place-items: center; min-height: 100vh; background: var(--bg); color: var(--text); font-family: ui-sans-serif, system-ui, -apple-system, sans-serif; } .card { width: min(360px, 90vw); padding: 2rem; border-radius: 1rem; background: var(--card); box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); } h1 { font-size: 1.5rem; margin-bottom: 1.5rem; text-align: center; } label { display: block; margin-bottom: 0.25rem; font-size: 0.875rem; color: #94a3b8; } input { width: 100%; padding: 0.75rem 1rem; margin-bottom: 1rem; border: 1px solid #334155; border-radius: 0.5rem; background: #0f172a; color: var(--text); font-size: 1rem; outline: none; transition: border-color 0.15s; } input:focus { border-color: var(--accent); } button { width: 100%; padding: 0.75rem; border: none; border-radius: 0.5rem; background: var(--accent); color: #0f172a; font-weight: 600; font-size: 1rem; cursor: pointer; transition: opacity 0.15s; } button:hover { opacity: 0.9; } button:disabled { opacity: 0.5; cursor: not-allowed; } .error { color: var(--error); font-size: 0.875rem; margin-top: 0.75rem; text-align: center; min-height: 1.25rem; } .hidden { display: none; } .user-bar { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-bottom: 1.5rem; } .user-bar span { font-weight: 600; } </style> </head> <body> <div class="card"> <!-- Sign In Form --> <div id="signinView"> <h1>Sign In</h1> <form id="signinForm"> <label for="username">Username</label> <input id="username" name="username" type="text" required autocomplete="username" />
<label for="password">Password</label> <input id="password" name="password" type="password" required autocomplete="current-password" />
<button type="submit" id="signinBtn">Sign In</button> <div class="error" id="errorMsg"></div> </form> </div>
<!-- Signed-In View --> <div id="userView" class="hidden"> <div class="user-bar"> <span id="welcomeMsg">Welcome!</span> <button id="signoutBtn">Sign Out</button> </div> <p style="color: #94a3b8; font-size: 0.875rem"> You are authenticated. </p> </div> </div>
<script> const signinView = document.getElementById("signinView"); const userView = document.getElementById("userView"); const errorMsg = document.getElementById("errorMsg"); const welcomeMsg = document.getElementById("welcomeMsg");
const API_BASE = "http://localhost:5173"; // set to 'http://localhost:8080' if different origin
// Check existing session on load (e.g., cookie is present or token stored) // If your backend uses HttpOnly cookies, you can't read them here. // Instead, call a lightweight /auth/me endpoint or just show the form. // Uncomment below if you have a GET /auth/me endpoint: fetch(`${API_BASE}/auth/self`, { credentials: "include" }) .then((r) => (r.ok ? r.json() : Promise.reject())) .then((u) => showUser(u.username)) .catch(() => showSignin());
document .getElementById("signinForm") .addEventListener("submit", async (e) => { e.preventDefault(); errorMsg.textContent = ""; const btn = document.getElementById("signinBtn"); btn.disabled = true;
const username = document.getElementById("username").value; const password = document.getElementById("password").value;
try { const res = await fetch(`${API_BASE}/auth/login`, { method: "POST", headers: { "Content-Type": "application/json" }, credentials: "include", // sends cookies; omit if purely token-based body: JSON.stringify({ username, password }), });
if (!res.ok) { const body = await res.json().catch(() => ({})); throw new Error( body.message || body.error || "Invalid credentials", ); }
// If your API returns a token: const { token } = await res.json();
// If your API uses session cookies only, no token needed. // Just call showUser() or refresh page: showUser(username); } catch (err) { errorMsg.textContent = err.message || "Sign in failed"; } finally { btn.disabled = false; } });
document .getElementById("signoutBtn") .addEventListener("click", async () => { try { await fetch(`${API_BASE}/auth/logout`, { method: "POST", credentials: "include", }); } catch (_) { /* ignore network errors */ } showSignin(); });
function showUser(name) { welcomeMsg.textContent = `Welcome, ${name}`; signinView.classList.add("hidden"); userView.classList.remove("hidden"); }
function showSignin() { userView.classList.add("hidden"); signinView.classList.remove("hidden"); document.getElementById("signinForm").reset(); } </script> </body></html>