amerc/src/ConsoleShell.jsx
artheru c8f6d0f892 public documentation (0.39.0)
- amerc-api: GET /docs + /docs/:id are public (reads); writes still require auth
- DocsApp/ConsoleShell: publicView mode renders docs read-only when logged out,
  with a 'Log in to edit' header CTA; edit/new/delete hidden for anonymous
- Docs nav link now shown to everyone
- seeded 5 quality guides: Quickstart, Publish an agent, Embed a chatbox, Showcase, API reference
2026-06-10 06:08:19 +08:00

77 lines
4.0 KiB
JavaScript

import React, { useState } from 'react';
import { useAuth } from './Auth.jsx';
const APPS = [
{ label: 'Tavern', href: 'https://amerc.ai/' },
{ label: 'Docs', href: 'https://docs.amerc.ai/' },
{ label: 'PM', href: 'https://pm.amerc.ai/' },
{ label: 'Git', href: 'https://git.amerc.ai/' },
];
function ConsoleLogin({ auth }) {
const [mode, setMode] = useState('login');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [handle, setHandle] = useState('');
const [err, setErr] = useState('');
const [busy, setBusy] = useState(false);
const submit = async (e) => {
e.preventDefault(); setErr(''); setBusy(true);
try {
const { api } = await import('./api.js');
const d = await api(mode === 'login' ? '/auth/login' : '/auth/signup', { method: 'POST', body: { email, password, handle } });
auth.setUser(d.user);
} catch (e2) { setErr(e2.message); } finally { setBusy(false); }
};
return (
<div className="cs-login-wrap">
<form className="cs-login" onSubmit={submit}>
<div className="cs-brand"><span className="cs-gem" />amerc</div>
<div className="cs-tabs">
<button type="button" className={mode === 'login' ? 'on' : ''} onClick={() => setMode('login')}>Login</button>
<button type="button" className={mode === 'signup' ? 'on' : ''} onClick={() => setMode('signup')}>Sign Up</button>
</div>
<input type="email" placeholder="email" value={email} onChange={(e) => setEmail(e.target.value)} required />
{mode === 'signup' && <input placeholder="display name" value={handle} onChange={(e) => setHandle(e.target.value)} />}
<input type="password" placeholder="password (min 6)" value={password} onChange={(e) => setPassword(e.target.value)} required minLength={6} />
{err && <p className="cs-err">{err}</p>}
<button type="submit" className="cs-btn cs-primary" disabled={busy}>{busy ? '…' : mode === 'login' ? 'Enter' : 'Create account'}</button>
<p className="cs-hint">One amerc account across tavern, docs, PM and git.</p>
</form>
</div>
);
}
export default function ConsoleShell({ title, accent = '#27d8ff', current, requireRole, publicView, children }) {
const auth = useAuth();
if (!auth.ready) return <div className="cs-app"><div className="cs-loading">Loading</div></div>;
if (!auth.user && !publicView) return <div className="cs-app" style={{ '--accent': accent }}><ConsoleLogin auth={auth} /></div>;
if (requireRole && auth.user && auth.user.role !== requireRole) return (
<div className="cs-app" style={{ '--accent': accent }}>
<div className="cs-login-wrap"><div className="cs-login" style={{ textAlign: 'center' }}>
<div className="cs-brand"><span className="cs-gem" />amerc</div>
<p style={{ color: '#ff9f9f', fontSize: 11, lineHeight: 1.8 }}>This space is for <b>{requireRole}s</b> only.</p>
<p className="cs-hint">Signed in as {auth.user.handle} ({auth.user.role}). Ask an admin for access.</p>
<a className="cs-btn cs-primary" href="https://amerc.ai/"> Back to tavern</a>
<button className="cs-btn" onClick={() => auth.logout()}>Log out</button>
</div></div>
</div>
);
return (
<div className="cs-app" style={{ '--accent': accent }}>
<header className="cs-top">
<div className="cs-brand"><span className="cs-gem" />amerc<small>{title}</small></div>
<nav className="cs-appnav">
{APPS.map((a) => <a key={a.label} href={a.href} className={a.label.toLowerCase() === current ? 'on' : ''}>{a.label}</a>)}
</nav>
<div className="cs-user">
{auth.user
? <><span>{auth.user.handle}{auth.user.role === 'admin' ? ' · admin' : ''}</span><button type="button" onClick={() => auth.logout()}>Log out</button></>
: <a className="cs-btn cs-primary" href="https://amerc.ai/#/signin">Log in to edit</a>}
</div>
</header>
<main className="cs-main">{typeof children === 'function' ? children(auth) : children}</main>
</div>
);
}