site footer with live service status (0.29.0)
- footer on all inner pages: wordmark, tagline, link columns (Product/Resources/Platform) - live status pill (checks /api/health + shows N online · N live sessions) with green pulse - sticky-bottom layout; makes the app feel complete + builds trust
This commit is contained in:
parent
7d62877808
commit
7b3ee02de6
@ -7,7 +7,7 @@
|
||||
name="description"
|
||||
content="amerc is an agent mercenary layer for embedding, bringing, and hosting agents across vertical software boundaries."
|
||||
/>
|
||||
<meta name="version" content="0.28.0-keys" />
|
||||
<meta name="version" content="0.29.0-footer" />
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "amerc-site",
|
||||
"version": "0.28.0-keys",
|
||||
"version": "0.29.0-footer",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@ -6,7 +6,7 @@ import Mansion from './Mansion.jsx';
|
||||
import TavernGame from './TavernGame.jsx';
|
||||
import { api } from './api.js';
|
||||
|
||||
const RELEASE = '0.28.0-keys';
|
||||
const RELEASE = '0.29.0-footer';
|
||||
|
||||
const NAV = [
|
||||
{ id: 'home', label: 'Tavern' },
|
||||
@ -89,18 +89,54 @@ function Home({ setRoute }) {
|
||||
);
|
||||
}
|
||||
|
||||
function Page({ children }) { return <div className="td-page">{children}</div>; }
|
||||
function Footer({ setRoute }) {
|
||||
const [ok, setOk] = useState(null);
|
||||
const [stats, setStats] = useState(null);
|
||||
useEffect(() => {
|
||||
api('/health').then(() => setOk(true)).catch(() => setOk(false));
|
||||
api('/agents/stats').then(setStats).catch(() => {});
|
||||
}, []);
|
||||
return (
|
||||
<footer className="ft">
|
||||
<div className="ft-cols">
|
||||
<div className="ft-brand">
|
||||
<div className="ft-logo"><span className="px-logo-gem" aria-hidden="true" /><strong>amerc</strong></div>
|
||||
<p>Hire, run, and deliver AI agents across software boundaries — no infrastructure, just skills.</p>
|
||||
<span className={`ft-status${ok === false ? ' down' : ''}`}>
|
||||
<i /> {ok === null ? 'checking…' : ok ? 'All systems operational' : 'Service degraded'}{stats ? ` · ${stats.online} online · ${stats.sessions} live` : ''}
|
||||
</span>
|
||||
</div>
|
||||
<div className="ft-col"><h5>Product</h5>
|
||||
<button onClick={() => setRoute('agents')}>Browse Agents</button>
|
||||
<button onClick={() => setRoute('mansion')}>Mansion</button>
|
||||
<button onClick={() => setRoute('booth')}>My Booth</button>
|
||||
</div>
|
||||
<div className="ft-col"><h5>Resources</h5>
|
||||
<a href="https://docs.amerc.ai/">Docs</a>
|
||||
<a href="https://docs.amerc.ai/">Roadmap</a>
|
||||
</div>
|
||||
<div className="ft-col"><h5>Platform</h5>
|
||||
<a href="https://git.amerc.ai/">Git</a>
|
||||
<a href="https://webagent.amerc.ai/health">Webagent</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="ft-base">© 2026 amerc · agent mercenary tavern · v{RELEASE}</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
|
||||
function Page({ children, setRoute }) { return <div className="td-page"><div className="td-page-inner">{children}</div><Footer setRoute={setRoute} /></div>; }
|
||||
|
||||
function Scene({ route, setRoute, auth }) {
|
||||
if (route === 'home') return <Home setRoute={setRoute} auth={auth} />;
|
||||
if (route === 'agents') return <Page><AgentBrowse auth={auth} setRoute={setRoute} /></Page>;
|
||||
if (route === 'mansion') return <Page><Mansion auth={auth} setRoute={setRoute} /></Page>;
|
||||
if (route === 'booth') return <Page><BoothDashboard auth={auth} setRoute={setRoute} /></Page>;
|
||||
if (route === 'signin') return <Page><div className="td-signin"><div className="px-board" style={{ position: 'static', transform: 'none', width: 'min(380px,92vw)', margin: '0 auto' }}>
|
||||
if (route === 'agents') return <Page setRoute={setRoute}><AgentBrowse auth={auth} setRoute={setRoute} /></Page>;
|
||||
if (route === 'mansion') return <Page setRoute={setRoute}><Mansion auth={auth} setRoute={setRoute} /></Page>;
|
||||
if (route === 'booth') return <Page setRoute={setRoute}><BoothDashboard auth={auth} setRoute={setRoute} /></Page>;
|
||||
if (route === 'signin') return <Page setRoute={setRoute}><div className="td-signin"><div className="px-board" style={{ position: 'static', transform: 'none', width: 'min(380px,92vw)', margin: '0 auto' }}>
|
||||
<header className="px-board-head"><span className="px-board-tag">GATEHOUSE</span><h2>Open Booth</h2><p>One amerc account across tavern, docs, PM and git.</p></header>
|
||||
<AuthPanel auth={auth} onDone={() => setRoute('booth')} />
|
||||
</div></div></Page>;
|
||||
if (route === 'admin') return <Page><div className="px-board" style={{ position: 'static', transform: 'none', width: 'min(720px,94vw)', margin: '0 auto' }}>
|
||||
if (route === 'admin') return <Page setRoute={setRoute}><div className="px-board" style={{ position: 'static', transform: 'none', width: 'min(720px,94vw)', margin: '0 auto' }}>
|
||||
<header className="px-board-head"><span className="px-board-tag">BACKDOOR</span><h2>Quartermaster</h2><p>Tenant, key, session and risk controls.</p></header>
|
||||
<AdminConsole auth={auth} />
|
||||
</div></Page>;
|
||||
|
||||
@ -1092,3 +1092,21 @@ button {
|
||||
.ap-keys { margin-top: 22px; padding: 18px; border-radius: 14px; background: #130d20; border: 1px solid #271c3a; }
|
||||
.ap-keys-mint { display: flex; gap: 8px; margin: 10px 0; flex-wrap: wrap; }
|
||||
.ap-keys-mint input { flex: 1 1 200px; padding: 9px 11px; border-radius: 8px; background: #0a0f1a; border: 1px solid #2e2440; color: #eaf2ff; font-family: inherit; }
|
||||
|
||||
/* footer + live status (0.29) */
|
||||
.td-page { display: flex; flex-direction: column; }
|
||||
.td-page-inner { flex: 1; }
|
||||
.ft { border-top: 1px solid #1c1430; background: linear-gradient(180deg, #0c0814, #07060c); padding: 30px 24px 18px; font-family: Inter, sans-serif; color: #b9a6cf; }
|
||||
.ft-cols { max-width: 1080px; margin: 0 auto; display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: 28px; }
|
||||
.ft-brand { max-width: 340px; }
|
||||
.ft-logo { display: flex; align-items: center; gap: 8px; }
|
||||
.ft-logo strong { font-family: Georgia, serif; font-size: 20px; color: #f3d27a; }
|
||||
.ft-brand p { font-size: 12px; line-height: 1.7; color: #9a86b8; margin: 8px 0 12px; }
|
||||
.ft-status { display: inline-flex; align-items: center; gap: 8px; font-size: 12px; color: #8fe9c0; background: rgba(54,240,176,0.08); border: 1px solid rgba(54,240,176,0.3); border-radius: 16px; padding: 5px 12px; }
|
||||
.ft-status i { width: 8px; height: 8px; border-radius: 50%; background: #36f0b0; box-shadow: 0 0 0 0 rgba(54,240,176,0.7); animation: ap-pulse 1.8s infinite; }
|
||||
.ft-status.down { color: #ff9f9f; background: rgba(255,122,140,0.08); border-color: rgba(255,122,140,0.3); } .ft-status.down i { background: #ff7a8c; animation: none; }
|
||||
.ft-col h5 { font-size: 11px; text-transform: uppercase; letter-spacing: 1px; color: #6f5b8c; margin: 0 0 10px; }
|
||||
.ft-col a, .ft-col button { display: block; color: #c3d0e6; font-size: 13px; text-decoration: none; background: none; cursor: pointer; padding: 4px 0; text-align: left; font-family: inherit; }
|
||||
.ft-col a:hover, .ft-col button:hover { color: #27d8ff; }
|
||||
.ft-base { max-width: 1080px; margin: 24px auto 0; padding-top: 14px; border-top: 1px solid #140d22; font-size: 11px; color: #5f4f7a; text-align: center; }
|
||||
@media (max-width: 680px) { .ft-cols { grid-template-columns: 1fr 1fr; } .ft-brand { grid-column: 1 / -1; } }
|
||||
|
||||
Loading…
Reference in New Issue
Block a user