Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | 28x 28x 28x 28x 28x 28x 28x 28x 28x 28x 2x 2x 2x 1x 1x 1x 1x 1x 1x 1x 1x 28x 28x 67x 28x 27x 54x 1x 1x 10x 8x 3x | import { useState } from 'react'
import type { SubmitEventHandler } from 'react'
import { Navigate } from 'react-router-dom'
import { useTranslation } from 'react-i18next'
import { useAuth } from '../auth'
import { Button } from '../components/Button'
import { PasswordInput } from '../components/PasswordInput'
import { usePressPulse } from '../usePressPulse'
type Tab = 'login' | 'register'
export function LoginPage() {
const { t } = useTranslation()
const [tab, setTab] = useState<Tab>('login')
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [repeatPassword, setRepeatPassword] = useState('')
const [error, setError] = useState('')
const [loading, setLoading] = useState(false)
const { token, signIn, register } = useAuth()
const [submitRef, pulseSubmit] = usePressPulse<HTMLButtonElement>()
const handleSubmit: SubmitEventHandler<HTMLFormElement> = async (e) => {
e.preventDefault()
pulseSubmit()
if (tab === 'register' && password !== repeatPassword) {
setError(t('login.passwordsNoMatch'))
return
}
setError('')
setLoading(true)
try {
if (tab === 'login') await signIn(username, password)
else Eawait register(username, password)
} catch (e) {
setError(e instanceof Error ? e.message : String(e))
} finally {
setLoading(false)
}
}
const passwordMismatch = error === t('login.passwordsNoMatch')
const inputClass = (invalid: boolean) =>
`w-full border rounded p-2 ${invalid ? 'border-red-500' : 'border-gray-300 dark:border-neutral-600'}`
// already signed in -> skip the login page
if (token) return <Navigate to="/generate" replace />
return (
<main className="mx-auto flex max-w-2xl flex-col gap-4 p-6 animate-fade-in">
<h1 className="text-2xl font-bold">{t('layout.generate')}</h1>
<div className="relative inline-flex self-start rounded-lg bg-gray-100 dark:bg-neutral-700 p-1">
<span
className={`pointer-events-none absolute inset-y-1 left-1 w-24 rounded-md bg-white dark:bg-neutral-800 shadow-sm transition-transform duration-200 ease-out ${
tab === 'register' ? 'translate-x-full' : 'translate-x-0'
}`}
/>
{(['login', 'register'] as const).map((value) => (
<button
key={value}
type="button"
className={`relative z-10 w-24 rounded-md py-1 text-sm font-medium transition-colors ${
tab === value ? 'text-orange-600 dark:text-orange-400' : 'text-gray-500 dark:text-neutral-400'
}`}
onClick={() => {
setTab(value)
setError('')
}}
>
{value === 'login' ? t('login.login') : t('login.register')}
</button>
))}
</div>
<form className="flex flex-col gap-4 max-w-sm" onSubmit={handleSubmit}>
<label className="flex flex-col gap-1">
<span className="font-medium">{t('login.username')}</span>
<input
type="text"
className={inputClass(!!error && !passwordMismatch)}
value={username}
onChange={(e) => setUsername(e.target.value)}
autoComplete="username"
/>
</label>
<label className="flex flex-col gap-1">
<span className="font-medium">{t('login.password')}</span>
<PasswordInput
className={inputClass(!!error)}
value={password}
onChange={(e) => setPassword(e.target.value)}
autoComplete={tab === 'login' ? 'current-password' : 'new-password'}
/>
</label>
{tab === 'register' && (
<label className="flex flex-col gap-1">
<span className="font-medium">{t('login.repeatPassword')}</span>
<PasswordInput
className={inputClass(passwordMismatch)}
value={repeatPassword}
onChange={(e) => setRepeatPassword(e.target.value)}
autoComplete="new-password"
/>
</label>
)}
<Button ref={submitRef} type="submit" className="self-start" disabled={loading}>
{loading ? t('login.pleaseWait') : tab === 'login' ? t('login.login') : t('login.signup')}
</Button>
{error && <p className="text-red-600 dark:text-red-400">{error}</p>}
</form>
</main>
)
}
|