// Login — wires to POST /api/v2/auth/login. // Poster pane shows live integration status so the screen isn't static. function Login({ onLogin }) { const [email, setEmail] = React.useState(''); const [password, setPassword] = React.useState(''); const [loading, setLoading] = React.useState(false); const [err, setErr] = React.useState(''); const [info, setInfo] = React.useState(''); const [forgotLoading, setForgotLoading] = React.useState(false); const [integrations, setIntegrations] = React.useState(null); // Public-ish health info to tell the staff the floor is up. React.useEffect(() => { fetch('/api/v2/health').catch(() => {}); }, []); async function submit(e) { e.preventDefault(); setErr(''); setInfo(''); setLoading(true); try { const data = await api.login(email.trim(), password); window.CURRENT_USER = { ...data.user, initials: initialsOf(data.user.name || data.user.email), color: colorFor(data.user.email || data.user.name || ''), }; // Fire-and-forget: pull integration status once we have a token api.get('/api/v2/system/integrations-status') .then(s => setIntegrations(s?.integrations || [])) .catch(() => {}); onLogin(data.user); } catch (e) { setErr(e.message || 'Sign-in failed'); } finally { setLoading(false); } } async function requestReset() { setErr(''); setInfo(''); const em = email.trim(); if (!em) { setErr('Enter your email above, then click Forgot password.'); return; } setForgotLoading(true); try { await api.post('/api/v2/auth/forgot-password', { email: em }, { auth: false }); setInfo('Reset request sent. The admin will forward the link to you via Telegram.'); } catch (e) { setErr(e.message || 'Could not send reset request.'); } finally { setForgotLoading(false); } } const liveIntegrations = integrations || []; const allGreen = liveIntegrations.length === 0 || liveIntegrations .filter(i => !i.coming_soon) .every(i => !!i.configured); const liveLabel = liveIntegrations.length === 0 ? 'GHL · Telegram · Tommy' : liveIntegrations.filter(i => !i.coming_soon && i.configured).map(i => i.name.split(' ')[0]).join(' · ') || 'config missing'; const nowStamp = new Date().toISOString().slice(0,10); return (
RAW Athletic Club · Staff
EST. 2021 · SF

RAW

No filler · Train raw
Build v2.1.0
{nowStamp}
Region us-east-1
RDS · healthy
Integrations {liveLabel}
{allGreen ? 'all green' : 'check system'}
— Staff access

Sign in.

Dashboard, Tommy approvals, and members. Use your club email.

{err && (
{err}
)} {info && (
{info}
)}
{ e.preventDefault(); requestReset(); }} style={forgotLoading ? {pointerEvents:'none', opacity:0.6} : undefined}> {forgotLoading ? 'Sending…' : 'Forgot password'}
); } function ResetPassword({ token, onDone }) { const [password, setPassword] = React.useState(''); const [confirm, setConfirm] = React.useState(''); const [loading, setLoading] = React.useState(false); const [err, setErr] = React.useState(''); const [done, setDone] = React.useState(false); async function submit(e) { e.preventDefault(); setErr(''); if (!token) { setErr('Missing reset token. Ask the admin for a new link.'); return; } if (password.length < 8) { setErr('Password must be at least 8 characters.'); return; } if (password !== confirm) { setErr('Passwords do not match.'); return; } setLoading(true); try { await api.post('/api/v2/auth/reset-password', { token, password }, { auth: false }); setDone(true); setTimeout(() => { window.history.replaceState({}, '', '/'); onDone && onDone(); }, 1600); } catch (e) { setErr(e.message || 'Reset failed. The link may be invalid or expired.'); } finally { setLoading(false); } } return (
RAW Athletic Club · Staff
EST. 2021 · SF

RAW

Reset access · Secure handshake
Token2 hour window
RequestForgot-password
ChannelTelegram admin
— Reset password

{done ? 'You’re all set.' : 'Set a new password.'}

{done ? 'Password updated. Taking you back to sign-in…' : 'Pick something you’ll remember. Min 8 characters. The link expires 2 hours after it was requested.'}

{!done && (
{err && (
{err}
)}
)}
Already reset? { e.preventDefault(); window.history.replaceState({}, '', '/'); onDone && onDone(); }}>Back to sign-in →
); } Object.assign(window, { Login, ResetPassword });