Files
muh/frontend/src/pages/LoginPage.tsx

296 lines
11 KiB
TypeScript

import { FormEvent, useState } from "react";
import { apiPost } from "../lib/api";
import { useSession } from "../lib/session";
import type { UserOption } from "../lib/types";
type FeedbackState =
| { type: "error"; text: string }
| { type: "success"; text: string }
| null;
export default function LoginPage() {
const [showRegistration, setShowRegistration] = useState(false);
const [identifier, setIdentifier] = useState("");
const [password, setPassword] = useState("");
const [showLoginValidation, setShowLoginValidation] = useState(false);
const [showRegisterValidation, setShowRegisterValidation] = useState(false);
const [registration, setRegistration] = useState({
companyName: "",
street: "",
houseNumber: "",
postalCode: "",
city: "",
email: "",
phoneNumber: "",
password: "",
passwordConfirmation: "",
});
const [feedback, setFeedback] = useState<FeedbackState>(null);
const { setUser } = useSession();
async function handlePasswordLogin(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
setShowLoginValidation(true);
if (!identifier.trim() || !password.trim()) {
setFeedback({
type: "error",
text: "Bitte E-Mail oder Benutzername und Passwort eingeben.",
});
return;
}
try {
setFeedback(null);
const response = await apiPost<UserOption>("/session/password-login", {
identifier: identifier.trim(),
password,
});
setUser(response);
} catch (loginError) {
setFeedback({ type: "error", text: (loginError as Error).message });
}
}
async function handleRegister(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
setShowRegisterValidation(true);
if (
!registration.companyName.trim()
|| !registration.street.trim()
|| !registration.houseNumber.trim()
|| !registration.postalCode.trim()
|| !registration.city.trim()
|| !registration.email.trim()
|| !registration.phoneNumber.trim()
|| !registration.password.trim()
) {
setFeedback({
type: "error",
text: "Bitte alle Pflichtfelder inklusive Telefonnummer fuer die Registrierung ausfuellen.",
});
return;
}
if (registration.password !== registration.passwordConfirmation) {
setFeedback({ type: "error", text: "Die beiden Passwoerter stimmen nicht ueberein." });
return;
}
try {
setFeedback(null);
const { passwordConfirmation, ...registrationPayload } = registration;
void passwordConfirmation;
const response = await apiPost<UserOption>("/session/register", registrationPayload);
setFeedback({
type: "success",
text: `Registrierung erfolgreich. Willkommen ${response.companyName ?? response.displayName}.`,
});
setUser(response);
} catch (registrationError) {
setFeedback({ type: "error", text: (registrationError as Error).message });
}
}
return (
<div className="login-page">
<section className="login-hero">
<div className="login-hero__copy">
<p className="eyebrow">MUH-App</p>
<h1>Moderne Steuerung fuer Milchproben und Therapien.</h1>
<p className="hero-text">
Fokus auf klare Arbeitsablaeufe, schnelle Probenbearbeitung und ein Portal
fuer Verwaltung, Berichtsdruck und Versandstatus.
</p>
</div>
<div className="login-hero__panel">
<div className="panel-glow" />
<p className="eyebrow">Zugang</p>
<h2>Anmelden oder registrieren</h2>
<p className="muted-text">
Anmeldung per E-Mail oder Benutzername mit Passwort sowie direkte
Kundenregistrierung.
</p>
{feedback ? (
<div className={`alert ${feedback.type === "success" ? "alert--success" : "alert--error"}`}>
{feedback.text}
</div>
) : null}
<div className="auth-grid">
{!showRegistration ? (
<form className={`login-panel__section ${showLoginValidation ? "show-validation" : ""}`} onSubmit={handlePasswordLogin}>
<label className="field field--required">
<span>E-Mail / Benutzername</span>
<input
value={identifier}
onChange={(event) => setIdentifier(event.target.value)}
placeholder="z. B. admin oder name@hof.de"
required
/>
</label>
<label className="field field--required">
<span>Passwort</span>
<input
type="password"
value={password}
onChange={(event) => setPassword(event.target.value)}
required
/>
</label>
<div className="page-actions">
<button type="submit" className="accent-button">
Mit Passwort anmelden
</button>
<button
type="button"
className="secondary-button"
onClick={() => {
setFeedback(null);
setShowRegisterValidation(false);
setShowRegistration(true);
}}
>
Registrieren
</button>
</div>
</form>
) : (
<form className={`login-panel__section ${showRegisterValidation ? "show-validation" : ""}`} onSubmit={handleRegister}>
<div className="field-grid">
<label className="field field--wide field--required">
<span>Firmenname</span>
<input
value={registration.companyName}
onChange={(event) =>
setRegistration((current) => ({ ...current, companyName: event.target.value }))
}
placeholder="z. B. Muster Agrar GmbH"
required
/>
</label>
<label className="field field--required">
<span>Strasse</span>
<input
value={registration.street}
onChange={(event) =>
setRegistration((current) => ({ ...current, street: event.target.value }))
}
placeholder="z. B. Dorfstrasse"
required
/>
</label>
<label className="field field--required">
<span>Hausnummer</span>
<input
value={registration.houseNumber}
onChange={(event) =>
setRegistration((current) => ({ ...current, houseNumber: event.target.value }))
}
placeholder="z. B. 12a"
required
/>
</label>
<label className="field field--required">
<span>PLZ</span>
<input
value={registration.postalCode}
onChange={(event) =>
setRegistration((current) => ({ ...current, postalCode: event.target.value }))
}
placeholder="z. B. 12345"
required
/>
</label>
<label className="field field--required">
<span>Ort</span>
<input
value={registration.city}
onChange={(event) =>
setRegistration((current) => ({ ...current, city: event.target.value }))
}
placeholder="z. B. Musterstadt"
required
/>
</label>
<label className="field field--required">
<span>E-Mail</span>
<input
type="email"
value={registration.email}
onChange={(event) =>
setRegistration((current) => ({ ...current, email: event.target.value }))
}
required
/>
</label>
<label className="field field--required">
<span>Telefonnummer</span>
<input
type="tel"
value={registration.phoneNumber}
onChange={(event) =>
setRegistration((current) => ({ ...current, phoneNumber: event.target.value }))
}
placeholder="z. B. 04531 181424"
required
/>
</label>
<label className="field field--wide field--required">
<span>Passwort</span>
<input
type="password"
value={registration.password}
onChange={(event) =>
setRegistration((current) => ({ ...current, password: event.target.value }))
}
required
/>
</label>
<label className="field field--wide field--required">
<span>Passwort wiederholen</span>
<input
type="password"
value={registration.passwordConfirmation}
className={
showRegisterValidation
&& registration.password !== registration.passwordConfirmation
? "is-invalid"
: ""
}
aria-invalid={
showRegisterValidation && registration.password !== registration.passwordConfirmation
}
onChange={(event) =>
setRegistration((current) => ({
...current,
passwordConfirmation: event.target.value,
}))
}
required
/>
</label>
</div>
<div className="page-actions">
<button type="submit" className="accent-button">
Registrieren
</button>
<button
type="button"
className="secondary-button"
onClick={() => {
setFeedback(null);
setShowLoginValidation(false);
setShowRegistration(false);
}}
>
Zurück
</button>
</div>
</form>
)}
</div>
</div>
</section>
</div>
);
}