annator-atom / frontend /atom.html
techprotrade's picture
Fix post-login redirect to ATOM hub (atom.html)
3cfc478 verified
Raw
History Blame Contribute Delete
8.26 kB
<!DOCTYPE html>
<html lang="et">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>ATOM · Command Center</title>
<style>
:root {
--green: #059669;
--green-dark: #047857;
--bg: #0b1220;
--panel: #111827;
--border: #1f2937;
--text: #f8fafc;
--muted: #94a3b8;
--card: #0f172a;
--ok: #34d399;
--warn: #fbbf24;
--err: #f87171;
}
* { box-sizing: border-box; }
body {
margin: 0; min-height: 100vh; font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
background: radial-gradient(1200px 600px at 10% -10%, #064e3b 0%, transparent 50%),
radial-gradient(900px 500px at 100% 0%, #1e3a5f 0%, transparent 45%),
var(--bg);
color: var(--text);
}
header {
display: flex; align-items: center; justify-content: space-between; gap: 1rem;
padding: 1rem 1.5rem; border-bottom: 1px solid var(--border);
background: rgba(15, 23, 42, 0.75); backdrop-filter: blur(10px); position: sticky; top: 0; z-index: 10;
}
.brand { display: flex; align-items: center; gap: .75rem; font-weight: 800; letter-spacing: .02em; }
.mark {
width: 36px; height: 36px; border-radius: 10px; display: grid; place-items: center;
background: linear-gradient(135deg, var(--green), #0ea5e9); color: white; font-size: .9rem;
}
.brand small { display: block; font-weight: 500; color: var(--muted); font-size: .75rem; }
nav { display: flex; flex-wrap: wrap; gap: .5rem; }
nav a, .btn {
color: var(--text); text-decoration: none; border: 1px solid var(--border); background: var(--panel);
padding: .55rem .85rem; border-radius: 10px; font-size: .9rem; font-weight: 600; cursor: pointer;
}
nav a:hover, .btn:hover { border-color: var(--green); }
.btn-primary { background: var(--green); border-color: var(--green); color: white; }
main { max-width: 1100px; margin: 0 auto; padding: 1.5rem; }
.hero {
display: grid; gap: 1rem; grid-template-columns: 1.4fr 1fr; margin-bottom: 1.25rem;
}
@media (max-width: 800px) { .hero { grid-template-columns: 1fr; } }
.card {
background: rgba(17, 24, 39, 0.9); border: 1px solid var(--border); border-radius: 16px; padding: 1.25rem;
}
h1 { margin: 0 0 .5rem; font-size: clamp(1.5rem, 3vw, 2rem); }
p { color: var(--muted); line-height: 1.55; }
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 1rem; }
.tile h3 { margin: 0 0 .35rem; font-size: 1rem; }
.tile p { margin: 0 0 .85rem; font-size: .9rem; }
.status { display: flex; flex-wrap: wrap; gap: .5rem; margin: 1rem 0; }
.badge {
display: inline-flex; align-items: center; gap: .4rem; padding: .35rem .65rem; border-radius: 999px;
background: #0b1729; border: 1px solid var(--border); font-size: .8rem; color: var(--muted);
}
.badge.ok { color: var(--ok); border-color: rgba(52, 211, 153, .35); }
.badge.err { color: var(--err); border-color: rgba(248, 113, 113, .35); }
.badge.warn { color: var(--warn); border-color: rgba(251, 191, 36, .35); }
code, pre {
background: #020617; border: 1px solid var(--border); border-radius: 10px; color: #e2e8f0;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .8rem;
}
pre { padding: .85rem; overflow: auto; max-height: 220px; white-space: pre-wrap; word-break: break-word; }
.muted { color: var(--muted); font-size: .85rem; }
footer { margin-top: 1.5rem; color: var(--muted); font-size: .8rem; text-align: center; }
</style>
</head>
<body>
<header>
<div class="brand">
<div class="mark">AT</div>
<div>ATOM<span><small>Command Center · operator workspace</small></span></div>
</div>
<nav>
<a href="/docs">API Docs</a>
<a href="/api/hf/status">HF Status</a>
<a href="/api/healthz">Health</a>
<a href="/clients/">Kliendibaas</a>
<a href="/dashboard.html">AIMF dashboard</a>
<button class="btn" id="logoutBtn" type="button">Logi välja</button>
</nav>
</header>
<main>
<section class="hero">
<div class="card">
<h1>Tere tulemast ATOMisse</h1>
<p>
See on <strong>ATOM operaatori hub</strong> (mitte AI Money Flow kliendiportaal).
Pärast sisselogimist jõuad siia. All on live API staatus ja sinu konto.
</p>
<div class="status" id="badges">
<span class="badge warn">API: kontrollin…</span>
<span class="badge warn">Auth: kontrollin…</span>
</div>
<div style="display:flex; gap:.6rem; flex-wrap:wrap;">
<a class="btn btn-primary" href="/docs">Ava ATOM API</a>
<a class="btn" href="/login.html">Login leht</a>
<a class="btn" href="/">AIMF avaleht (klient)</a>
</div>
</div>
<div class="card">
<h3 style="margin-top:0">Sessioon</h3>
<p class="muted" id="sessionInfo">Tokenit loen…</p>
<pre id="meBox">/api/auth/me</pre>
</div>
</section>
<section class="grid">
<div class="card tile">
<h3>API / integratsioonid</h3>
<p>FastAPI backend, OpenAPI, health ja operaatori endpointid.</p>
<a class="btn" href="/docs">/docs</a>
</div>
<div class="card tile">
<h3>Kliendibaas</h3>
<p>Staatilised kliendi dossierid (HTML).</p>
<a class="btn" href="/clients/">/clients/</a>
</div>
<div class="card tile">
<h3>AI Money Flow</h3>
<p>Eraldi kliendiportaal (intake + demo dashboard).</p>
<a class="btn" href="/">Avaleht</a>
<a class="btn" href="/dashboard.html">Dashboard</a>
</div>
<div class="card tile">
<h3>Space health</h3>
<p>Hugging Face konteineri staatus.</p>
<a class="btn" href="/api/hf/status">/api/hf/status</a>
</div>
</section>
<footer>
ATOM Command Center · HF Docker Space · pärast login’it suunatakse siia (/atom.html)
</footer>
</main>
<script>
const badges = document.getElementById('badges');
const meBox = document.getElementById('meBox');
const sessionInfo = document.getElementById('sessionInfo');
const token = localStorage.getItem('token') || localStorage.getItem('auth_token') || '';
function setBadges(apiOk, authOk, apiText, authText) {
badges.innerHTML = `
<span class="badge ${apiOk ? 'ok' : 'err'}">API: ${apiText}</span>
<span class="badge ${authOk ? 'ok' : (token ? 'err' : 'warn')}">Auth: ${authText}</span>
`;
}
document.getElementById('logoutBtn').onclick = () => {
localStorage.removeItem('token');
localStorage.removeItem('auth_token');
location.href = '/login.html';
};
if (!token) {
sessionInfo.textContent = 'Token puudub — suunan loginile…';
setBadges(false, false, '—', 'puudub');
setTimeout(() => { location.href = '/login.html?next=/atom.html'; }, 700);
} else {
sessionInfo.textContent = 'Token olemas (localStorage).';
(async () => {
let apiOk = false, authOk = false, apiText = 'down', authText = 'fail';
try {
const h = await fetch('/api/hf/status');
const j = await h.json();
apiOk = h.ok;
apiText = apiOk ? (j.mode || 'ok') : ('HTTP ' + h.status);
} catch (e) {
apiText = 'error';
}
try {
const me = await fetch('/api/auth/me', {
headers: { Authorization: 'Bearer ' + token }
});
const body = await me.json().catch(() => ({}));
if (me.ok) {
authOk = true;
authText = body.email || 'ok';
meBox.textContent = JSON.stringify(body, null, 2);
} else {
authText = body.detail || ('HTTP ' + me.status);
meBox.textContent = JSON.stringify(body, null, 2);
}
} catch (e) {
authText = String(e);
meBox.textContent = String(e);
}
setBadges(apiOk, authOk, apiText, authText);
})();
}
</script>
</body>
</html>