re-vibecode

This commit is contained in:
Debian
2026-03-30 13:56:47 +00:00
parent 42b50b78cc
commit 09d76c044b
17 changed files with 1030 additions and 1135 deletions
+23 -35
View File
@@ -1,69 +1,57 @@
import { useState } from "react";
import type { FormEvent } from "react";
import { useState, type FormEvent } from "react";
type LoginPageProps = {
busy: boolean;
error: string | null;
onLogin: (email: string, password: string, totpCode?: string) => Promise<void>;
onSubmit: (payload: { email: string; password: string }) => Promise<void>;
};
export const LoginPage = ({ busy, error, onLogin }: LoginPageProps) => {
export default function LoginPage({ busy, error, onSubmit }: LoginPageProps) {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [totpCode, setTotpCode] = useState("");
const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
await onLogin(email, password, totpCode || undefined);
await onSubmit({
email: email.trim().toLowerCase(),
password
});
};
return (
<main className="auth-shell">
<section className="auth-card">
<div className="auth-card__eyebrow">Public internet hardened</div>
<h1>Secure Webmail</h1>
<p>
Sessions stay on the server, cookies are <code>httpOnly</code>, and every state-changing request carries an in-memory CSRF token.
<div className="eyebrow">Docker Mailserver</div>
<h1>Mail user management</h1>
<p className="auth-copy">
Sign in with the admin account configured for this panel.
</p>
<form className="auth-form" onSubmit={handleSubmit}>
<label>
Email
<form className="stack" onSubmit={handleSubmit}>
<label className="field">
<span>Admin email</span>
<input
autoComplete="username"
inputMode="email"
type="email"
value={email}
onChange={(event) => setEmail(event.target.value)}
required
autoComplete="username"
autoFocus
disabled={busy}
/>
</label>
<label>
Password
<label className="field">
<span>Password</span>
<input
autoComplete="current-password"
type="password"
value={password}
onChange={(event) => setPassword(event.target.value)}
required
autoComplete="current-password"
disabled={busy}
/>
</label>
<label>
Two-factor code
<input
autoComplete="one-time-code"
inputMode="numeric"
pattern="\d{6}"
type="text"
value={totpCode}
onChange={(event) => setTotpCode(event.target.value)}
placeholder="123456"
/>
</label>
{error ? <div className="form-error">{error}</div> : null}
{error ? <p className="form-error">{error}</p> : null}
<button type="submit" className="primary-button" disabled={busy}>
{busy ? "Signing in..." : "Sign in"}
@@ -72,4 +60,4 @@ export const LoginPage = ({ busy, error, onLogin }: LoginPageProps) => {
</section>
</main>
);
};
}