← back to Sublease Agentabrams
public/account.html
84 lines
<!doctype html>
<html lang="en"><head>
<script>
// Credential-safe fetch guard (fleet drop-in) — if this page is opened with
// credentials in the URL (a saved bookmark / Chrome-remembered basic-auth:
// https://user:pass@host/…), the browser poisons document.baseURI, so any
// relative or root-relative fetch('/api/…') throws "Request cannot be constructed
// from a URL that includes credentials" and callers silently fall to an empty
// state. Resolve every non-absolute request URL against the credential-free
// location instead of baseURI. Placed first so it wraps window.fetch before any
// app script runs. Ref: creds-in-url-fetch-guard-fleet-pattern.
(function () {
var _fetch = window.fetch.bind(window);
var cleanBase = function () { return location.origin + location.pathname; };
window.fetch = function (input, init) {
try {
if (typeof input === 'string' && !/^[a-z]+:\/\//i.test(input) && input.indexOf('//') !== 0) {
input = new URL(input, cleanBase()).href;
} else if (input instanceof Request && !/^[a-z]+:\/\//i.test(input.url)) {
input = new Request(new URL(input.url, cleanBase()).href, input);
}
} catch (_) { /* fall through to native */ }
return _fetch(input, init);
};
})();
</script>
<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<title>Account · Sublease Marketplace</title>
<style>
:root{--ink:#14181d;--mut:#6b7480;--line:#e7e9ee;--bg:#fbfbfc;--accent:#1a5f3c}
*{box-sizing:border-box}body{margin:0;font:15px/1.55 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,sans-serif;color:var(--ink);background:var(--bg)}
a{color:var(--accent);text-decoration:none}.wrap{max-width:460px;margin:0 auto;padding:0 22px}
header.top{border-bottom:1px solid var(--line);background:#fff}.top .wrap{display:flex;align-items:center;height:60px;max-width:1200px}
.top a{color:var(--mut);font-size:14px}
.card{background:#fff;border:1px solid var(--line);border-radius:14px;padding:26px;margin:34px auto}
h1{font-size:22px;margin:0 0 4px}.sub{color:var(--mut);margin:0 0 20px;font-size:14px}
.tabs{display:flex;gap:6px;margin-bottom:18px}
.tabs button{flex:1;padding:9px;border:1px solid var(--line);background:#fff;border-radius:8px;font:inherit;cursor:pointer;color:var(--mut)}
.tabs button.on{background:var(--accent);color:#fff;border-color:var(--accent)}
label{display:block;font-size:12.5px;color:var(--mut);margin:12px 0 4px}
input{width:100%;padding:10px 12px;border:1px solid var(--line);border-radius:8px;font:inherit}
.btn{width:100%;margin-top:18px;background:var(--accent);color:#fff;border:0;padding:11px;border-radius:9px;font:600 15px system-ui;cursor:pointer}
.msg{margin-top:12px;font-size:13.5px}.err{color:#b3261e}.ok{color:#1a7a44}
.claim{border:1px solid var(--line);border-radius:9px;padding:10px 12px;margin-top:8px;font-size:13.5px}
.muted{color:var(--mut)}
</style></head><body>
<header class="top"><div class="wrap"><a href="/">← marketplace</a></div></header>
<div class="wrap"><div class="card" id="card"></div></div>
<script>
const $=s=>document.querySelector(s);
async function me(){ return (await fetch('/api/auth/me').then(r=>r.json()).catch(()=>({}))).user; }
async function render(){
const u=await me();
if(u){
$('#card').innerHTML=`<h1>My account</h1><p class="sub">${u.name?u.name+' · ':''}${u.email}</p>
<b style="font-size:14px">Profiles you've claimed</b>
${(u.claims&&u.claims.length)?u.claims.map(id=>`<div class="claim">Broker <a href="/brokers/${id}">#${id}</a> <span class="muted">— pending review</span></div>`).join(''):'<p class="muted" style="font-size:13.5px">None yet. Open your broker profile and click <b>Claim this profile</b>.</p>'}
<button class="btn" id="logout" style="background:#eef2f7;color:#41505f;margin-top:20px">Log out</button>`;
$('#logout').onclick=async()=>{await fetch('/api/auth/logout',{method:'POST'});render();};
return;
}
$('#card').innerHTML=`<h1>Sign in</h1><p class="sub">Create an account to claim and manage your broker profile.</p>
<div class="tabs"><button id="tLogin" class="on">Log in</button><button id="tSignup">Sign up</button></div>
<form id="form">
<div id="nameWrap" style="display:none"><label>Name</label><input name="name" placeholder="Your name"></div>
<label>Email</label><input name="email" type="email" required>
<label>Password</label><input name="password" type="password" required minlength="8" placeholder="8+ characters">
<button class="btn" type="submit" id="submit">Log in</button>
<div class="msg" id="msg"></div>
</form>`;
let mode='login';
const setMode=m=>{mode=m;$('#tLogin').classList.toggle('on',m==='login');$('#tSignup').classList.toggle('on',m==='signup');
$('#nameWrap').style.display=m==='signup'?'block':'none';$('#submit').textContent=m==='signup'?'Create account':'Log in';};
$('#tLogin').onclick=()=>setMode('login'); $('#tSignup').onclick=()=>setMode('signup');
$('#form').onsubmit=async(e)=>{
e.preventDefault(); const fd=Object.fromEntries(new FormData(e.target).entries());
$('#msg').textContent='…';$('#msg').className='msg';
const r=await fetch('/api/auth/'+mode,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(fd)}).then(r=>r.json());
if(r.ok){render();} else {$('#msg').className='msg err';$('#msg').textContent=r.error||'Failed';}
};
}
render();
</script></body></html>