138 lines
4.0 KiB
TypeScript
138 lines
4.0 KiB
TypeScript
"use client";
|
|
import Link from "next/link";
|
|
import { useActionState, useEffect, useId } from "react";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Label } from "@/components/ui/label";
|
|
import {
|
|
requestResetAction,
|
|
finishResetAction,
|
|
changePasswordAction,
|
|
type PasswordActionState,
|
|
} from "./actions";
|
|
|
|
export function PasswordForm({
|
|
mode,
|
|
token,
|
|
}: {
|
|
mode: "request" | "reset" | "change";
|
|
token?: string;
|
|
}) {
|
|
const action =
|
|
mode === "request"
|
|
? requestResetAction
|
|
: mode === "reset"
|
|
? finishResetAction
|
|
: changePasswordAction;
|
|
const [state, formAction, pending] = useActionState(
|
|
action,
|
|
{} as PasswordActionState,
|
|
);
|
|
const id = useId();
|
|
useEffect(() => {
|
|
if (mode === "change" && state.success)
|
|
window.location.replace(
|
|
new URL("/login?passwordChanged=1", window.location.origin).href,
|
|
);
|
|
}, [mode, state.success]);
|
|
if (state.success)
|
|
return (
|
|
<div className="space-y-4">
|
|
<p
|
|
role="status"
|
|
className="rounded-xl border border-[var(--hairline)] bg-[var(--good-soft)] p-4 text-sm leading-6 text-[var(--ink)]"
|
|
>
|
|
{state.message}
|
|
</p>
|
|
{mode !== "request" ? (
|
|
<Link
|
|
href="/login"
|
|
className="text-sm font-semibold text-[var(--accent-deep)] underline underline-offset-4"
|
|
>
|
|
Zur Anmeldung
|
|
</Link>
|
|
) : null}
|
|
</div>
|
|
);
|
|
return (
|
|
<form action={formAction} className="space-y-4">
|
|
{token ? <input type="hidden" name="token" value={token} /> : null}
|
|
{mode === "request" ? (
|
|
<div className="space-y-2">
|
|
<Label htmlFor={`${id}-email`}>E-Mail-Adresse</Label>
|
|
<Input
|
|
id={`${id}-email`}
|
|
name="email"
|
|
type="email"
|
|
autoComplete="email"
|
|
maxLength={254}
|
|
required
|
|
disabled={pending}
|
|
/>
|
|
</div>
|
|
) : (
|
|
<>
|
|
{mode === "change" ? (
|
|
<div className="space-y-2">
|
|
<Label htmlFor={`${id}-current`}>Aktuelles Passwort</Label>
|
|
<Input
|
|
id={`${id}-current`}
|
|
name="currentPassword"
|
|
type="password"
|
|
autoComplete="current-password"
|
|
required
|
|
disabled={pending}
|
|
/>
|
|
</div>
|
|
) : null}
|
|
<div className="space-y-2">
|
|
<Label htmlFor={`${id}-password`}>Neues Passwort</Label>
|
|
<Input
|
|
id={`${id}-password`}
|
|
name="password"
|
|
type="password"
|
|
autoComplete="new-password"
|
|
minLength={8}
|
|
required
|
|
disabled={pending}
|
|
/>
|
|
<p className="text-xs leading-5 text-[var(--ink-soft)]">
|
|
Mindestens 8 Zeichen. Verwende ein eigenes Passwort für den
|
|
Kita-Planer.
|
|
</p>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label htmlFor={`${id}-confirmation`}>
|
|
Neues Passwort wiederholen
|
|
</Label>
|
|
<Input
|
|
id={`${id}-confirmation`}
|
|
name="confirmation"
|
|
type="password"
|
|
autoComplete="new-password"
|
|
minLength={8}
|
|
required
|
|
disabled={pending}
|
|
/>
|
|
</div>
|
|
<p className="text-xs leading-5 text-[var(--ink-soft)]">
|
|
Nach dem Speichern werden alle bisherigen Anmeldungen beendet.
|
|
</p>
|
|
</>
|
|
)}
|
|
{state.error ? (
|
|
<p role="alert" className="text-sm leading-6 text-[var(--danger)]">
|
|
{state.error}
|
|
</p>
|
|
) : null}
|
|
<Button type="submit" className="w-full sm:w-auto" disabled={pending}>
|
|
{pending
|
|
? "Wird verarbeitet …"
|
|
: mode === "request"
|
|
? "Link anfordern"
|
|
: "Passwort speichern"}
|
|
</Button>
|
|
</form>
|
|
);
|
|
}
|