"use client" import { zodResolver } from "@hookform/resolvers/zod" import { useContext } from "react" import { useForm } from "react-hook-form" import * as z from "zod" import { useCaptcha } from "../../../hooks/use-captcha" import { AuthUIContext } from "../../../lib/auth-ui-provider" import { cn, getLocalizedError, getPasswordSchema } from "../../../lib/utils" import type { AuthLocalization } from "../../../localization/auth-localization" import type { PasswordValidation } from "../../../types/password-validation" import { Captcha } from "../../captcha/captcha" import { PasswordInput } from "../../password-input" import { CardContent } from "../../ui/card" import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "../../ui/form" import { SettingsCard, type SettingsCardClassNames } from "../shared/settings-card" import { InputFieldSkeleton } from "../skeletons/input-field-skeleton" export interface ChangePasswordCardProps { className?: string classNames?: SettingsCardClassNames accounts?: { providerId: string }[] | null isPending?: boolean localization?: AuthLocalization skipHook?: boolean passwordValidation?: PasswordValidation } export function ChangePasswordCard({ className, classNames, accounts, isPending, localization, skipHook, passwordValidation }: ChangePasswordCardProps) { const { authClient, basePath, baseURL, credentials, hooks: { useSession, useListAccounts }, localization: contextLocalization, navigate, viewPaths, toast, localizeErrors } = useContext(AuthUIContext) const confirmPasswordEnabled = credentials?.confirmPassword const contextPasswordValidation = credentials?.passwordValidation localization = { ...contextLocalization, ...localization } passwordValidation = { ...contextPasswordValidation, ...passwordValidation } const { captchaRef, getCaptchaHeaders, resetCaptcha } = useCaptcha({ localization }) const { data: sessionData } = useSession() if (!skipHook) { const result = useListAccounts() accounts = result.data isPending = result.isPending } const formSchema = z .object({ currentPassword: getPasswordSchema( passwordValidation, localization ), newPassword: getPasswordSchema(passwordValidation, { PASSWORD_REQUIRED: localization.NEW_PASSWORD_REQUIRED, PASSWORD_TOO_SHORT: localization.PASSWORD_TOO_SHORT, PASSWORD_TOO_LONG: localization.PASSWORD_TOO_LONG, INVALID_PASSWORD: localization.INVALID_PASSWORD }), confirmPassword: confirmPasswordEnabled ? getPasswordSchema(passwordValidation, { PASSWORD_REQUIRED: localization.CONFIRM_PASSWORD_REQUIRED, PASSWORD_TOO_SHORT: localization.PASSWORD_TOO_SHORT, PASSWORD_TOO_LONG: localization.PASSWORD_TOO_LONG, INVALID_PASSWORD: localization.INVALID_PASSWORD }) : z.string().optional() }) .refine( (data) => !confirmPasswordEnabled || data.newPassword === data.confirmPassword, { message: localization.PASSWORDS_DO_NOT_MATCH, path: ["confirmPassword"] } ) const form = useForm({ resolver: zodResolver(formSchema), defaultValues: { currentPassword: "", newPassword: "", confirmPassword: "" } }) const setPasswordForm = useForm() const { isSubmitting } = form.formState const setPassword = async () => { if (!sessionData) return const email = sessionData?.user.email try { await authClient.requestPasswordReset({ email, redirectTo: `${baseURL}${basePath}/${viewPaths.RESET_PASSWORD}`, fetchOptions: { throw: true, headers: await getCaptchaHeaders("/forget-password") } }) toast({ variant: "success", message: localization.FORGOT_PASSWORD_EMAIL! }) navigate( `${basePath}/${viewPaths.SIGN_IN}${window.location.search}` ) } catch (error) { toast({ variant: "error", message: getLocalizedError({ error, localization, localizeErrors }) }) resetCaptcha() } } const changePassword = async ({ currentPassword, newPassword }: z.infer) => { try { await authClient.changePassword({ currentPassword, newPassword, revokeOtherSessions: true, fetchOptions: { throw: true } }) toast({ variant: "success", message: localization.CHANGE_PASSWORD_SUCCESS! }) } catch (error) { toast({ variant: "error", message: getLocalizedError({ error, localization, localizeErrors }) }) } form.reset() } const credentialsLinked = accounts?.some( (acc) => acc.providerId === "credential" ) if (!isPending && !credentialsLinked) { return (
) } return (
{isPending || !accounts ? ( <> {confirmPasswordEnabled && ( )} ) : ( <> ( {localization.CURRENT_PASSWORD} )} /> ( {localization.NEW_PASSWORD} )} /> {confirmPasswordEnabled && ( ( { localization.CONFIRM_PASSWORD } )} /> )} )}
) }