Files
twitter-lite/src/features/auth/onboarding-page.tsx
T

152 lines
4.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useRouteContext } from '@tanstack/react-router'
import { ArrowRight, BookOpen, House, MessagesSquare } from 'lucide-react'
import { type FormEvent, useState } from 'react'
import { Button } from '#/components/ui/button'
import { Input } from '#/components/ui/input'
import { Label } from '#/components/ui/label'
import { authRequest } from './auth-client'
import { AuthFrame } from './auth-page'
export function OnboardingPage() {
const { auth } = useRouteContext({ from: '__root__' })
const [name, setName] = useState(auth.owner?.name ?? '')
const [step, setStep] = useState(1)
const [pending, setPending] = useState(false)
const [error, setError] = useState('')
async function finish() {
setPending(true)
setError('')
try {
await authRequest({ action: 'onboard', name: name.trim() })
window.location.assign('/')
} catch (cause) {
setError(
cause instanceof Error
? cause.message
: 'Unable to save. Please try again.',
)
setPending(false)
}
}
async function signOut() {
setPending(true)
setError('')
try {
await authRequest({ action: 'logout' })
window.location.assign('/login')
} catch (cause) {
setError(
cause instanceof Error
? cause.message
: 'Unable to sign out. Please try again.',
)
setPending(false)
}
}
function next(event: FormEvent<HTMLFormElement>) {
event.preventDefault()
if (!name.trim()) return
setStep(2)
}
return (
<AuthFrame>
<p className="auth-step">{step} of 2</p>
{step === 1 ? (
<>
<header className="auth-heading">
<h1>A little context, to start.</h1>
<p>Your day, conversations, and reading, together in one place.</p>
</header>
<form className="auth-form" onSubmit={next}>
<div className="auth-field">
<Label htmlFor="displayName">What should we call you?</Label>
<Input
id="displayName"
name="displayName"
value={name}
onChange={(event) => setName(event.target.value)}
autoComplete="given-name"
required
maxLength={80}
disabled={pending}
/>
</div>
<Button
type="submit"
className="auth-submit"
size="lg"
disabled={pending || !name.trim()}
>
Continue
<ArrowRight aria-hidden="true" />
</Button>
</form>
</>
) : (
<>
<header className="auth-heading">
<h1>Start with today.</h1>
<p>You can take it one thing at a time.</p>
</header>
<ul className="auth-overview">
<li>
<House aria-hidden="true" />
<div>
<strong>A place to begin</strong>
<p>A brief and flexible tasks help you find your next step.</p>
</div>
</li>
<li>
<MessagesSquare aria-hidden="true" />
<div>
<strong>Room for the everyday</strong>
<p>Keep conversations and quick notes close at hand.</p>
</div>
</li>
<li>
<BookOpen aria-hidden="true" />
<div>
<strong>Follow your curiosity</strong>
<p>Explore reading and research alongside your day.</p>
</div>
</li>
</ul>
<p className="auth-prototype">
Home, contacts, notes, reading, and vitals currently use sample
data. You can add connections from your profile menu whenever you’re
ready.
</p>
<div className="auth-actions">
<Button
variant="ghost"
onClick={() => setStep(1)}
disabled={pending}
>
Back
</Button>
<Button size="lg" onClick={finish} disabled={pending}>
{pending ? 'Saving…' : 'Open workspace'}
<ArrowRight aria-hidden="true" />
</Button>
</div>
</>
)}
{error && (
<p className="auth-error" role="alert">
{error}
</p>
)}
<footer className="auth-footer">
<span>{auth.owner?.email}</span>
<Button variant="ghost" size="sm" onClick={signOut} disabled={pending}>
Sign out
</Button>
</footer>
</AuthFrame>
)
}