80 lines
2.4 KiB
TypeScript
80 lines
2.4 KiB
TypeScript
"use client";
|
|
|
|
import { Suspense, useState } from "react";
|
|
import { useSearchParams } from "next/navigation";
|
|
import { TextField } from "@nexarch/shl";
|
|
import { ApiError, completePasswordReset } from "../../../lib/api";
|
|
|
|
export default function PasswordResetCompletePage() {
|
|
return (
|
|
<Suspense fallback={null}>
|
|
<PasswordResetCompleteForm />
|
|
</Suspense>
|
|
);
|
|
}
|
|
|
|
function PasswordResetCompleteForm() {
|
|
const params = useSearchParams();
|
|
const token = params.get("token") ?? "";
|
|
const [newPassword, setNewPassword] = useState("");
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [done, setDone] = useState(false);
|
|
const [submitting, setSubmitting] = useState(false);
|
|
|
|
async function onSubmit(event: React.FormEvent<HTMLFormElement>) {
|
|
event.preventDefault();
|
|
setError(null);
|
|
setSubmitting(true);
|
|
try {
|
|
await completePasswordReset(token, newPassword);
|
|
setDone(true);
|
|
} catch (err) {
|
|
// Akzeptanzkriterium 3: generische Meldung, keine interne Fehlerursache.
|
|
setError(err instanceof ApiError ? err.message : "Der Link ist ungültig oder abgelaufen.");
|
|
} finally {
|
|
setSubmitting(false);
|
|
}
|
|
}
|
|
|
|
if (done) {
|
|
return (
|
|
<main style={{ maxWidth: 400, margin: "80px auto", padding: "0 16px" }}>
|
|
<h1>Passwort geändert</h1>
|
|
<p>
|
|
Ihr Passwort wurde erfolgreich geändert. <a href="/login">Jetzt anmelden</a>.
|
|
</p>
|
|
</main>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<main style={{ maxWidth: 400, margin: "80px auto", padding: "0 16px" }}>
|
|
<h1>Neues Passwort festlegen</h1>
|
|
{!token && (
|
|
<p role="alert" style={{ color: "var(--shl-color-danger)" }}>
|
|
Kein gültiger Link. Bitte fordern Sie einen neuen Reset-Link an.
|
|
</p>
|
|
)}
|
|
<form onSubmit={onSubmit} noValidate>
|
|
<TextField
|
|
label="Neues Passwort"
|
|
type="password"
|
|
autoComplete="new-password"
|
|
required
|
|
minLength={8}
|
|
value={newPassword}
|
|
onChange={(e) => setNewPassword(e.target.value)}
|
|
/>
|
|
{error && (
|
|
<p role="alert" style={{ color: "var(--shl-color-danger)" }}>
|
|
{error}
|
|
</p>
|
|
)}
|
|
<button type="submit" disabled={submitting || !token}>
|
|
{submitting ? "Wird gespeichert…" : "Passwort speichern"}
|
|
</button>
|
|
</form>
|
|
</main>
|
|
);
|
|
}
|