/* ===== Authentication screens (React) ================================== Split-screen auth UI gating the Sectionly dashboard. Talks to window.Auth. Exposes (the gate's signed-out experience) on window. ===================================================================== */ /* extra icons used only by auth (others come from window.ICONS in data.js) */ window.ICONS['eye-off'] = ''; window.ICONS['key'] = ''; window.ICONS['send'] = ''; window.ICONS['shield-check'] = ''; /* ---- Google "G" logo (multi-color, not a stroke icon) ---- */ function GoogleG({ size }) { size = size || 19; return ( ); } function GoogleWordmark() { const cols = [['G', '#4285F4'], ['o', '#EA4335'], ['o', '#FBBC05'], ['g', '#4285F4'], ['l', '#34A853'], ['e', '#EA4335']]; return ( {cols.map(([c, col], i) => {c})} ); } /* ---- shared field with leading icon + optional show/hide ---- */ function AuthInput({ icon, type, value, onChange, placeholder, autoComplete, state, onBlur, onKeyDown, inputRef, name }) { const [show, setShow] = useState(false); const isPw = type === 'password'; const realType = isPw ? (show ? 'text' : 'password') : type; return (
onChange(e.target.value)} onBlur={onBlur} onKeyDown={onKeyDown} placeholder={placeholder} autoComplete={autoComplete} /> {isPw && }
); } function Alert({ children }) { if (!children) return null; return
{children}
; } function SubmitBtn({ busy, children, disabled }) { return ( ); } /* ---- left brand panel ---- */ function BrandPanel() { return (
Sectionly
Section Templates Dashboard

Ship beautiful pages, section by section.

Your library of production-ready frontend sections — browse, customize and copy clean HTML & React in seconds.

  • 120+ curated, responsive sections
  • Copy-paste HTML & React, no lock-in
  • Favorites & collections that sync
© {new Date().getFullYear()} Sectionly · Crafted for frontend developers
); } /* ==================================================================== Sign in ==================================================================== */ function SignInScreen({ ctx }) { const [email, setEmail] = useState(ctx.email || ''); const [password, setPassword] = useState(''); const [remember, setRemember] = useState(true); const [touched, setTouched] = useState({}); const [err, setErr] = useState(''); const [busy, setBusy] = useState(false); const emailBad = touched.email && !window.Auth.validEmail(email.trim().toLowerCase()); const submit = (e) => { e.preventDefault(); setErr(''); setBusy(true); window.Auth.signIn({ email, password, remember }) .then(u => ctx.onAuthed(u)) .catch(ex => { setBusy(false); if (ex && ex.needVerify) { ctx.go('verify', { email: ex.email, devCode: ex.verifyCode, name: '' }); return; } setErr(ex.message || 'Could not sign in.'); }); }; return (
Sectionly

Welcome back

Sign in to your Sectionly account to continue.

{err}
or sign in with email
setTouched(t => ({ ...t, email: true }))} /> {emailBad && Enter a valid email address.}
Sign in
Demo account — email ashraf@sectionly.app, password password123. Or create your own below.
Don’t have an account?
); } /* ==================================================================== Sign up ==================================================================== */ function passwordChecks(pw) { return { len: pw.length >= 8, alpha: /[a-zA-Z]/.test(pw), num: /[0-9]/.test(pw) }; } function SignUpScreen({ ctx }) { const [name, setName] = useState(''); const [email, setEmail] = useState(ctx.email || ''); const [password, setPassword] = useState(''); const [touched, setTouched] = useState({}); const [err, setErr] = useState(''); const [busy, setBusy] = useState(false); const checks = passwordChecks(password); const pwOk = checks.len && checks.alpha && checks.num; const emailBad = touched.email && !window.Auth.validEmail(email.trim().toLowerCase()); const canSubmit = name.trim() && window.Auth.validEmail(email.trim().toLowerCase()) && pwOk; const submit = (e) => { e.preventDefault(); setErr(''); setBusy(true); window.Auth.signUp({ name, email, password }) .then(res => { setBusy(false); // local mode returns { user, verifyCode }; server mode signs in directly if (res && res.verifyCode != null) ctx.go('verify', { email: email.trim().toLowerCase(), name: name.trim(), devCode: res.verifyCode }); else if (res && (res.verified === false)) ctx.go('verify', { email: email.trim().toLowerCase(), name: name.trim(), devCode: null }); else ctx.onAuthed(res); }) .catch(ex => { setBusy(false); setErr(ex.message || 'Could not create your account.'); }); }; const rules = [['len', 'At least 8 characters'], ['alpha', 'Contains a letter'], ['num', 'Contains a number']]; return (
Sectionly

Create your account

Start building with the Sectionly library — free, no card required.

{err}
or sign up with email
setTouched(t => ({ ...t, email: true }))} /> {emailBad && Enter a valid email address.}
{password.length > 0 &&
{rules.map(([k, lbl]) =>
{lbl}
)}
}
Create account

By creating an account you agree to our Terms & Privacy Policy.

Already have an account?
); } /* ---- Google chooser trigger button + modal ---- */ function GoogleButton({ ctx, setErr, label, remember }) { const [open, setOpen] = useState(false); return ( <> {open && setOpen(false)} onPick={(acct) => { setOpen(false); window.Auth.signInGoogle(acct, remember) .then(u => ctx.onAuthed(u)) .catch(ex => setErr && setErr(ex.message || 'Google sign-in failed.')); }} />} ); } const DEMO_GOOGLE = [ { name: 'Ashraf Hossain', email: 'ashraf.hossain@gmail.com', color: '#7c4dff' }, { name: 'Maya Chen', email: 'maya.chen@gmail.com', color: '#1a9d5a' } ]; function GoogleChooser({ onClose, onPick }) { const [other, setOther] = useState(false); const [oName, setOName] = useState(''); const [oEmail, setOEmail] = useState(''); useEffect(() => { const onKey = (e) => { if (e.key === 'Escape') onClose(); }; document.addEventListener('keydown', onKey); return () => document.removeEventListener('keydown', onKey); }, [onClose]); const otherOk = oName.trim() && window.Auth.validEmail(oEmail.trim().toLowerCase()); return (
{ if (e.target === e.currentTarget) onClose(); }}>

Choose an account

to continue to Sectionly

{!other ? ( <>
{DEMO_GOOGLE.map(a => )}
) : (
)}
To continue, Google will share your name and email address with Sectionly. This is a simulated chooser for the demo.
); } /* ==================================================================== Verify email ==================================================================== */ function VerifyScreen({ ctx }) { const len = 6; const [digits, setDigits] = useState(Array(len).fill('')); const [err, setErr] = useState(''); const [busy, setBusy] = useState(false); const [devCode, setDevCode] = useState(ctx.devCode || null); const refs = useRef([]); useEffect(() => { if (refs.current[0]) refs.current[0].focus(); }, []); const setAt = (i, v) => { v = v.replace(/\D/g, ''); setDigits(prev => { const next = prev.slice(); if (v.length > 1) { // paste for (let k = 0; k < len; k++) next[k] = v[k] || ''; return next; } next[i] = v; return next; }); if (v && i < len - 1 && refs.current[i + 1]) refs.current[i + 1].focus(); }; const onKey = (i, e) => { if (e.key === 'Backspace' && !digits[i] && i > 0) refs.current[i - 1].focus(); }; const code = digits.join(''); const submit = (e) => { e && e.preventDefault(); setErr(''); setBusy(true); window.Auth.verifyEmail(ctx.email, code) .then(u => ctx.onAuthed(u)) .catch(ex => { setBusy(false); setErr(ex.message || 'That code is not correct.'); }); }; useEffect(() => { if (code.length === len) submit(); /* auto-submit */ }, [code]); const resend = () => { setErr(''); window.Auth.resendVerification(ctx.email).then(res => { if (res && res.verifyCode) setDevCode(res.verifyCode); }); }; return (

Verify your email

We sent a 6-digit code to {ctx.email}. Enter it below to activate your account.

{err}
{digits.map((d, i) => refs.current[i] = el} inputMode="numeric" maxLength={i === 0 ? len : 1} className={d ? 'filled' : ''} value={d} onChange={e => setAt(i, e.target.value)} onKeyDown={e => onKey(i, e)} />)}
{devCode &&
This is a demo, so no real email is sent. Your verification code is {devCode}.
} Verify & continue

Didn’t get a code?

); } /* ==================================================================== Forgot password ==================================================================== */ function ForgotScreen({ ctx }) { const [email, setEmail] = useState(ctx.email || ''); const [busy, setBusy] = useState(false); const [sent, setSent] = useState(false); const [resetToken, setResetToken] = useState(null); const [err, setErr] = useState(''); const submit = (e) => { e.preventDefault(); setErr(''); setBusy(true); window.Auth.requestReset(email) .then(res => { setBusy(false); setSent(true); setResetToken(res && res.resetToken); }) .catch(ex => { setBusy(false); setErr(ex.message || 'Something went wrong.'); }); }; if (sent) { return (

Check your inbox

If an account exists for {email}, we’ve sent a link to reset your password.

{resetToken ? <>
Demo mode — no real email is sent. Continue to set a new password using your reset link.
:
Follow the link in the email to choose a new password. The link expires in 30 minutes.
}

Wrong email?

); } return (

Reset your password

Enter the email linked to your account and we’ll send you a reset link.

{err}
Send reset link
); } /* ==================================================================== Reset password (consume token) ==================================================================== */ function ResetScreen({ ctx }) { const [password, setPassword] = useState(''); const [confirm, setConfirm] = useState(''); const [busy, setBusy] = useState(false); const [err, setErr] = useState(''); const [done, setDone] = useState(false); const checks = passwordChecks(password); const pwOk = checks.len && checks.alpha && checks.num; const match = confirm.length > 0 && confirm === password; const canSubmit = pwOk && match; const submit = (e) => { e.preventDefault(); setErr(''); setBusy(true); window.Auth.resetPassword(ctx.resetToken, password) .then(res => { setBusy(false); setDone(true); }) .catch(ex => { setBusy(false); setErr(ex.message || 'This reset link is invalid or has expired.'); }); }; if (done) { return (

Password updated

Your password has been changed. You can now sign in with your new password.

); } const rules = [['len', 'At least 8 characters'], ['alpha', 'Contains a letter'], ['num', 'Contains a number']]; return (

Choose a new password

Pick a strong password you don’t use anywhere else.

{err}
{password.length > 0 &&
{rules.map(([k, lbl]) =>
{lbl}
)}
}
0 ? (match ? 'ok' : 'err') : ''} /> {confirm.length > 0 && !match && Passwords don’t match.}
Update password
); } /* ==================================================================== AuthApp — the signed-out experience (split layout + screen router) ==================================================================== */ function AuthApp({ onAuthed, theme, toggleTheme }) { const [screen, setScreen] = useState('signin'); const [ctxData, setCtxData] = useState({ email: '' }); const go = useCallback((next, patch) => { setCtxData(prev => Object.assign({}, prev, patch || {})); setScreen(next); const p = document.querySelector('.auth-panel'); if (p) p.scrollTop = 0; }, []); const ctx = Object.assign({}, ctxData, { go, onAuthed }); let body; if (screen === 'signup') body = ; else if (screen === 'verify') body = ; else if (screen === 'forgot') body = ; else if (screen === 'reset') body = ; else body = ; return (
{body}
); } Object.assign(window, { AuthApp, GoogleG, GoogleWordmark });