Kairo-Jinkendo/frontend/src/pages/AuthPanel.jsx
Lars 48a599e76c
All checks were successful
Deploy Development / deploy (push) Successful in 43s
Test Suite / pytest-backend (push) Successful in 29s
Test Suite / lint-backend (push) Successful in 2s
Test Suite / compose-smoke (push) Has been skipped
Test Suite / k6 /api/health Baseline (push) Successful in 18s
Test Suite / playwright-smoke (push) Successful in 19s
AP0.6b: Jinkendo UX/PWA Remediation — App Shell, Design-Tokens, responsive Nav, PWA-Manifest
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-05 06:58:48 +02:00

154 lines
5.4 KiB
JavaScript

import { useState, useEffect } from 'react'
import { fetchSetupStatus, login, register } from '../api/me.js'
import { useSession } from '../context/SessionContext.jsx'
export function AuthPanel() {
const { applyLogin } = useSession()
const [setup, setSetup] = useState(null)
const [setupLoading, setSetupLoading] = useState(true)
const [setupError, setSetupError] = useState(null)
const [authMode, setAuthMode] = useState('login')
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [displayName, setDisplayName] = useState('')
const [organizationName, setOrganizationName] = useState('')
const [passwordConfirm, setPasswordConfirm] = useState('')
const [authError, setAuthError] = useState(null)
const [authBusy, setAuthBusy] = useState(false)
useEffect(() => {
fetchSetupStatus()
.then((body) => {
setSetup(body)
if (body.registration_open) setAuthMode('register')
})
.catch((err) => setSetupError(err.message))
.finally(() => setSetupLoading(false))
}, [])
async function handleLogin(e) {
e.preventDefault()
setAuthBusy(true)
setAuthError(null)
try {
const body = await login(email, password)
applyLogin(body)
} catch (err) {
setAuthError(err.message)
} finally {
setAuthBusy(false)
}
}
async function handleRegister(e) {
e.preventDefault()
if (password !== passwordConfirm) {
setAuthError('Passwörter stimmen nicht überein')
return
}
setAuthBusy(true)
setAuthError(null)
try {
const body = await register({
email,
password,
display_name: displayName,
organization_name: organizationName || undefined,
})
applyLogin(body)
} catch (err) {
setAuthError(err.message)
} finally {
setAuthBusy(false)
}
}
const registrationOpen = setup?.registration_open === true
return (
<main className="auth-shell">
<header className="auth-hero">
<h1>Kairo</h1>
<p>Jinkendo Program Director Vorhaben &amp; Maßnahmen</p>
</header>
<section className="card auth-card">
<h2 className="card-title card-title--lg">
{registrationOpen ? 'Ersteinrichtung' : 'Anmelden'}
</h2>
{setupLoading && <p className="muted">Prüfe Ersteinrichtung </p>}
{setupError && <p className="error">{setupError}</p>}
{!setupLoading && !setupError && (
<>
<div className="tabs" role="tablist">
<button
type="button"
className={authMode === 'login' ? 'tab active' : 'tab'}
onClick={() => { setAuthMode('login'); setAuthError(null) }}
>
Anmelden
</button>
<button
type="button"
className={authMode === 'register' ? 'tab active' : 'tab'}
disabled={!registrationOpen}
onClick={() => { setAuthMode('register'); setAuthError(null) }}
>
Registrieren
</button>
</div>
{authMode === 'login' && (
<form className="form" onSubmit={handleLogin}>
<label>
E-Mail
<input type="email" autoComplete="username" value={email} onChange={(e) => setEmail(e.target.value)} required />
</label>
<label>
Passwort
<input type="password" autoComplete="current-password" value={password} onChange={(e) => setPassword(e.target.value)} minLength={8} required />
</label>
<button type="submit" className="btn btn-primary btn-block-mobile" disabled={authBusy}>
{authBusy ? 'Anmelden …' : 'Anmelden'}
</button>
</form>
)}
{authMode === 'register' && registrationOpen && (
<form className="form" onSubmit={handleRegister}>
<label>
Anzeigename
<input value={displayName} onChange={(e) => setDisplayName(e.target.value)} required />
</label>
<label>
Organisation (optional)
<input value={organizationName} onChange={(e) => setOrganizationName(e.target.value)} />
</label>
<label>
E-Mail
<input type="email" autoComplete="email" value={email} onChange={(e) => setEmail(e.target.value)} required />
</label>
<label>
Passwort
<input type="password" autoComplete="new-password" value={password} onChange={(e) => setPassword(e.target.value)} minLength={8} required />
</label>
<label>
Passwort bestätigen
<input type="password" autoComplete="new-password" value={passwordConfirm} onChange={(e) => setPasswordConfirm(e.target.value)} minLength={8} required />
</label>
<button type="submit" className="btn btn-primary btn-block-mobile" disabled={authBusy}>
{authBusy ? 'Registrieren …' : 'Systemadmin anlegen'}
</button>
</form>
)}
</>
)}
{authError && <p className="error">{authError}</p>}
</section>
</main>
)
}