44 lines
1.8 KiB
TypeScript
44 lines
1.8 KiB
TypeScript
'use client';
|
|
|
|
import { FormEvent, useState } from 'react';
|
|
|
|
type AuthMode = 'login' | 'register';
|
|
|
|
export function AuthForm({ mode }: { mode: AuthMode }) {
|
|
const [email, setEmail] = useState('');
|
|
const [password, setPassword] = useState('');
|
|
const [deviceName, setDeviceName] = useState('Web browser');
|
|
const [message, setMessage] = useState('');
|
|
const [busy, setBusy] = useState(false);
|
|
|
|
async function submit(event: FormEvent<HTMLFormElement>) {
|
|
event.preventDefault();
|
|
setBusy(true);
|
|
setMessage('');
|
|
try {
|
|
const response = await fetch(`/api/auth/${mode}`, {
|
|
method: 'POST',
|
|
headers: { 'content-type': 'application/json' },
|
|
body: JSON.stringify({ email, password, device_name: deviceName }),
|
|
});
|
|
const body = await response.json().catch(() => ({}));
|
|
if (!response.ok) throw new Error(body.detail ?? 'Authentication failed.');
|
|
window.location.assign('/account');
|
|
} catch (error) {
|
|
setMessage(error instanceof Error ? error.message : 'Authentication failed.');
|
|
} finally {
|
|
setBusy(false);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<form className="auth-form" onSubmit={submit}>
|
|
<label>Email<input required type="email" autoComplete="email" value={email} onChange={(e) => setEmail(e.target.value)} /></label>
|
|
<label>Password<input required minLength={12} type="password" autoComplete={mode === 'login' ? 'current-password' : 'new-password'} value={password} onChange={(e) => setPassword(e.target.value)} /></label>
|
|
<label>Device name<input maxLength={255} value={deviceName} onChange={(e) => setDeviceName(e.target.value)} /></label>
|
|
{message && <p className="form-error" role="alert">{message}</p>}
|
|
<button className="button" disabled={busy} type="submit">{busy ? 'Working...' : mode === 'login' ? 'Sign in' : 'Create account'}</button>
|
|
</form>
|
|
);
|
|
}
|