"use client"; import React from "react"; import { useAuth } from "@/components/auth/authContext"; import { changePassword, ApiClientError } from "@/lib/frontend/apiClient"; import { notifyError, notifySuccess, notifyApiError, } from "@/lib/frontend/ui/toast"; import { buildLoginUrl, LOGIN_REASONS } from "@/lib/frontend/authRedirect"; import { getPasswordPolicyHintLinesDe, reasonsToHintLinesDe, buildWeakPasswordMessageDe, } from "@/lib/frontend/profile/passwordPolicyUi"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Alert, AlertTitle, AlertDescription } from "@/components/ui/alert"; import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter, } from "@/components/ui/card"; function isNonEmptyString(value) { return typeof value === "string" && value.trim().length > 0; } export default function ChangePasswordCard() { const { status, user } = useAuth(); const isAuthenticated = status === "authenticated" && user; const [currentPassword, setCurrentPassword] = React.useState(""); const [newPassword, setNewPassword] = React.useState(""); const [confirmNewPassword, setConfirmNewPassword] = React.useState(""); const [isSubmitting, setIsSubmitting] = React.useState(false); const [error, setError] = React.useState(null); // error: { title: string, description?: string|null, field?: string|null, hints?: string[]|null } const policyLines = React.useMemo(() => { return getPasswordPolicyHintLinesDe(); }, []); function clearForm() { setCurrentPassword(""); setNewPassword(""); setConfirmNewPassword(""); } function redirectToLoginExpired() { const next = typeof window !== "undefined" ? `${window.location.pathname}${window.location.search}` : "/profile"; window.location.replace( buildLoginUrl({ reason: LOGIN_REASONS.EXPIRED, next }), ); } async function onSubmit(e) { e.preventDefault(); if (isSubmitting) return; setError(null); if (!isAuthenticated) { notifyError({ title: "Nicht angemeldet", description: "Bitte melden Sie sich an, um Ihr Passwort zu ändern.", }); return; } if (!isNonEmptyString(currentPassword)) { setError({ field: "currentPassword", title: "Bitte aktuelles Passwort eingeben.", description: null, }); return; } if (!isNonEmptyString(newPassword)) { setError({ field: "newPassword", title: "Bitte ein neues Passwort eingeben.", description: null, }); return; } if (!isNonEmptyString(confirmNewPassword)) { setError({ field: "confirmNewPassword", title: "Bitte neues Passwort bestätigen.", description: null, }); return; } if (newPassword !== confirmNewPassword) { setError({ field: "confirmNewPassword", title: "Passwörter stimmen nicht überein.", description: "Bitte prüfen Sie die Bestätigung.", }); return; } if (newPassword === currentPassword) { setError({ field: "newPassword", title: "Neues Passwort ist ungültig.", description: "Neues Passwort darf nicht identisch zum aktuellen Passwort sein.", }); return; } setIsSubmitting(true); try { await changePassword({ currentPassword, newPassword, }); clearForm(); notifySuccess({ title: "Passwort geändert", description: "Ihr Passwort wurde erfolgreich aktualisiert.", }); } catch (err) { if (err instanceof ApiClientError) { if (err.code === "AUTH_UNAUTHENTICATED") { notifyApiError(err); redirectToLoginExpired(); return; } if (err.code === "AUTH_INVALID_CREDENTIALS") { const title = "Aktuelles Passwort ist falsch."; setError({ field: "currentPassword", title, description: null }); notifyError({ title }); return; } if (err.code === "VALIDATION_WEAK_PASSWORD") { const reasons = err.details?.reasons; const minLength = err.details?.minLength; const hints = reasonsToHintLinesDe({ reasons, minLength }); const description = buildWeakPasswordMessageDe({ reasons, minLength, }); setError({ field: "newPassword", title: "Neues Passwort ist zu schwach.", description, hints, }); notifyError({ title: "Neues Passwort ist zu schwach.", description, }); return; } } setError({ field: null, title: "Passwort konnte nicht geändert werden.", description: "Bitte versuchen Sie es erneut.", }); notifyApiError(err, { fallbackTitle: "Passwort konnte nicht geändert werden.", fallbackDescription: "Bitte versuchen Sie es erneut.", }); } finally { setIsSubmitting(false); } } const showError = Boolean(error && error.title); return ( Passwort Ändern Sie Ihr Passwort. {!isAuthenticated ? (

Hinweis: Passwortänderungen sind nur verfügbar, wenn Sie angemeldet sind.

) : null} {showError ? ( {error.title} {error.description ? ( {error.description} ) : null} {Array.isArray(error.hints) && error.hints.length > 0 ? (
    {error.hints.map((line) => (
  • {line}
  • ))}
) : null}
) : null}
setCurrentPassword(e.target.value)} disabled={!isAuthenticated || isSubmitting} aria-invalid={ error?.field === "currentPassword" ? "true" : "false" } />
setNewPassword(e.target.value)} disabled={!isAuthenticated || isSubmitting} aria-invalid={error?.field === "newPassword" ? "true" : "false"} />
    {policyLines.map((line) => (
  • {line}
  • ))}
setConfirmNewPassword(e.target.value)} disabled={!isAuthenticated || isSubmitting} aria-invalid={ error?.field === "confirmNewPassword" ? "true" : "false" } />
); }