102 lines
3.5 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Sanad — Sign in</title>
<style>
:root{
--bg:#0c0f14; --panel:#141922; --border:#222a37; --text:#e8eef7;
--dim:#6b7585; --muted:#8a95a7; --accent:#3aa9ff; --err:#ff5d5d; --ok:#5fdc8a;
}
*{box-sizing:border-box}
html,body{height:100%;margin:0}
body{
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Ubuntu,sans-serif;
background:radial-gradient(ellipse at top,#162033 0%,var(--bg) 60%);
color:var(--text);
display:flex;align-items:center;justify-content:center;
padding:1rem;
}
.card{
width:100%;max-width:360px;background:var(--panel);
border:1px solid var(--border);border-radius:14px;
padding:1.8rem 1.6rem 1.4rem;
box-shadow:0 10px 40px rgba(0,0,0,.4);
}
.brand{font-size:1.5rem;font-weight:700;margin-bottom:.1rem}
.brand .accent{color:var(--accent)}
.sub{color:var(--muted);font-size:.78rem;margin-bottom:1.3rem}
label{display:block;font-size:.72rem;color:var(--dim);margin:.5rem 0 .25rem;letter-spacing:.04em;text-transform:uppercase}
input{
width:100%;padding:.55rem .7rem;
background:#0d121a;border:1px solid var(--border);border-radius:8px;
color:var(--text);font:inherit;font-size:.92rem;outline:none;
}
input:focus{border-color:var(--accent);box-shadow:0 0 0 2px rgba(58,169,255,.18)}
.btn{
width:100%;margin-top:1rem;padding:.6rem .8rem;
background:var(--accent);color:#031426;border:0;border-radius:8px;
font-weight:600;font-size:.92rem;cursor:pointer;
transition:filter .12s,opacity .12s;
}
.btn:hover{filter:brightness(1.08)}
.btn:disabled{opacity:.55;cursor:wait}
.msg{margin-top:.85rem;font-size:.78rem;min-height:1em}
.msg.err{color:var(--err)}
.msg.ok{color:var(--ok)}
.hint{margin-top:1rem;font-size:.65rem;color:var(--dim);text-align:center}
</style>
</head>
<body>
<form class="card" id="loginForm" autocomplete="on">
<div class="brand">Sanad <span class="accent">Dashboard</span></div>
<div class="sub">Sign in to continue</div>
<label for="u">Username</label>
<input id="u" name="username" autocomplete="username" required autofocus>
<label for="p">Password</label>
<input id="p" name="password" type="password" autocomplete="current-password" required>
<button class="btn" id="submit" type="submit">Sign in</button>
<div class="msg" id="msg"></div>
<div class="hint">Robot control — local network only</div>
</form>
<script>
const form=document.getElementById('loginForm');
const msg=document.getElementById('msg');
const btn=document.getElementById('submit');
form.addEventListener('submit',async ev=>{
ev.preventDefault();
msg.className='msg';msg.textContent='';
btn.disabled=true;btn.textContent='Signing in…';
try{
const r=await fetch('/api/auth/login',{
method:'POST',
headers:{'Content-Type':'application/json'},
body:JSON.stringify({
username:document.getElementById('u').value,
password:document.getElementById('p').value,
}),
});
if(r.ok){
msg.className='msg ok';msg.textContent='Signed in — redirecting…';
const next=new URL(location.href).searchParams.get('next')||'/';
location.href=next;
return;
}
let detail='Invalid credentials';
try{const j=await r.json();detail=j.detail||detail;}catch(e){}
msg.className='msg err';msg.textContent=detail;
}catch(e){
msg.className='msg err';msg.textContent='Network error: '+(e&&e.message||e);
}finally{
btn.disabled=false;btn.textContent='Sign in';
}
});
</script>
</body>
</html>