Files
F-r-ldraledigplannerare/frontend/src/pages/Auth.tsx
T
jacobh2 0081f60b06 Finish the redesign's edges: auth page, modals, dead code, docs
The sign-in page becomes a plain centred column with the brand mark
and a segmented login/register switch, modals lose their last inner
boxes, the red-days list collapses every year but the current one,
and the retired components' CSS, an unused list component and 23
unreferenced translation keys are removed. ARCHITECTURE.md documents
the Almanacka styling conventions and the per-year balance rule.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XBRLomdLK9jF7wU7a8AA6H
2026-09-20 07:00:22 +02:00

134 lines
4.6 KiB
TypeScript

import { useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { useAuth } from '@/context/AuthContext'
import { useWorkspace } from '@/context/WorkspaceContext'
import { useTranslation } from '@/i18n'
import { LanguageToggle } from '@/components/layout/LanguageToggle'
import './auth.css'
export function Auth() {
const { login, register, isAuthenticated } = useAuth()
const { isLoading: wsLoading } = useWorkspace()
const { t } = useTranslation()
const navigate = useNavigate()
const [isRegister, setIsRegister] = useState(false)
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [displayName, setDisplayName] = useState('')
const [error, setError] = useState<string | null>(null)
useEffect(() => {
if (isAuthenticated && !wsLoading) {
navigate('/', { replace: true })
}
}, [isAuthenticated, wsLoading, navigate])
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setError(null)
try {
if (isRegister) {
await register({ email, password, displayName })
navigate('/', { replace: true })
} else {
await login({ email, password })
navigate('/', { replace: true })
}
} catch (err: unknown) {
setError(err instanceof Error ? err.message : t('auth.genericError'))
}
}
// Switching tabs clears any error from the previous tab's attempt — a
// failed login's error text should not linger, confusingly, once the
// person has moved on to creating an account (or back).
const switchTab = (register: boolean) => {
setIsRegister(register)
setError(null)
}
return (
<main className="auth-page">
<div className="auth-page__lang">
<LanguageToggle />
</div>
<div className="auth-page__column">
<img className="auth-page__mark" src="/img/icon-64.png" alt="" width={32} height={32} />
<h1 className="auth-page__title">{isRegister ? t('auth.createAccountTitle') : t('auth.loginTitle')}</h1>
<div className="segmented auth-page__tabs" role="group">
<button
type="button"
className="segmented__option auth-page__tab"
aria-pressed={!isRegister}
onClick={() => switchTab(false)}
data-testid="auth-tab-login"
>
{t('auth.loginButton')}
</button>
<button
type="button"
className="segmented__option auth-page__tab"
aria-pressed={isRegister}
onClick={() => switchTab(true)}
data-testid="auth-tab-register"
>
{t('auth.createAccountButton')}
</button>
</div>
<form className="auth-page__form" onSubmit={(e) => void handleSubmit(e)}>
{isRegister && (
<div className="field">
<label htmlFor="auth-name" className="field__label">
{t('auth.nameLabel')}
</label>
<input
id="auth-name"
className="field__control"
placeholder={t('auth.namePlaceholder')}
value={displayName}
onChange={(e) => setDisplayName(e.target.value)}
/>
</div>
)}
<div className="field">
<label htmlFor="auth-email" className="field__label">
{t('auth.emailLabel')}
</label>
<input
id="auth-email"
type="email"
data-testid="auth-email"
className="field__control"
placeholder={t('auth.emailPlaceholder')}
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</div>
<div className="field">
<label htmlFor="auth-password" className="field__label">
{t('auth.passwordLabel')}
</label>
<input
id="auth-password"
type="password"
data-testid="auth-password"
className="field__control"
placeholder={t('auth.passwordPlaceholder')}
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</div>
{error && (
<p role="alert" className="auth-page__error">
{error}
</p>
)}
<button type="submit" className="btn btn--primary btn--block" data-testid="auth-submit">
{isRegister ? t('auth.registerButton') : t('auth.loginButton')}
</button>
</form>
</div>
</main>
)
}