// 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 (
Dashboard, Tommy approvals, and members. Use your club email.
{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 && ( )}