import { zodResolver } from "@hookform/resolvers/zod" import { Alert, Button, Heading, Input, Text, toast } from "@medusajs/ui" import { useForm } from "react-hook-form" import { Trans, useTranslation } from "react-i18next" import { Link, useNavigate, useSearchParams } from "react-router-dom" import * as z from "zod" import { useState } from "react" import { decodeToken } from "react-jwt" import { Form } from "../../components/common/form" import { LogoBox } from "../../components/common/logo-box" import { i18n } from "../../components/utilities/i18n" import { useResetPasswordForEmailPass, useUpdateProviderForEmailPass, } from "../../hooks/api/auth" const ResetPasswordInstructionsSchema = z.object({ email: z.string().email(), }) const ResetPasswordSchema = z .object({ password: z.string().min(1), repeat_password: z.string().min(1), }) .superRefine(({ password, repeat_password }, ctx) => { if (password !== repeat_password) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: i18n.t("resetPassword.passwordMismatch"), path: ["repeat_password"], }) } }) const ResetPasswordTokenSchema = z.object({ entity_id: z.string(), provider: z.string(), exp: z.number(), iat: z.number(), }) type DecodedResetPasswordToken = { entity_id: string // -> email in here provider: string exp: string iat: string } const validateDecodedResetPasswordToken = ( decoded: any ): decoded is DecodedResetPasswordToken => { return ResetPasswordTokenSchema.safeParse(decoded).success } const InvalidResetToken = () => { const { t } = useTranslation() const navigate = useNavigate() return (
{t("resetPassword.invalidLinkTitle")} {t("resetPassword.invalidLinkHint")}
, ]} />
) } const ChooseNewPassword = ({ token }: { token: string }) => { const { t } = useTranslation() const [showAlert, setShowAlert] = useState(false) const invite: DecodedResetPasswordToken | null = token ? decodeToken(token) : null const isValidResetPasswordToken = invite && validateDecodedResetPasswordToken(invite) const form = useForm>({ resolver: zodResolver(ResetPasswordSchema), defaultValues: { password: "", repeat_password: "", }, }) const { mutateAsync, isPending } = useUpdateProviderForEmailPass(token) const handleSubmit = form.handleSubmit(async ({ password }) => { if (!invite) { return } await mutateAsync( { password, }, { onSuccess: () => { form.setValue("password", "") form.setValue("repeat_password", "") setShowAlert(true) }, onError: (error) => { toast.error(error.message) }, } ) }) if (!isValidResetPasswordToken) { return } return (
{t("resetPassword.resetPassword")} {t("resetPassword.newPasswordHint")}
{ return ( ) }} /> { return ( ) }} />
{showAlert && (
{t("resetPassword.successfulResetTitle")} {t("resetPassword.successfulReset")}
)} {!showAlert && ( )}
, ]} />
) } export const ResetPassword = () => { const { t } = useTranslation() const [searchParams] = useSearchParams() const [showAlert, setShowAlert] = useState(false) const token = searchParams.get("token") const form = useForm>({ resolver: zodResolver(ResetPasswordInstructionsSchema), defaultValues: { email: "", }, }) const { mutateAsync, isPending } = useResetPasswordForEmailPass() const handleSubmit = form.handleSubmit(async ({ email }) => { await mutateAsync( { email, }, { onSuccess: () => { form.setValue("email", "") setShowAlert(true) }, onError: (error) => { toast.error(error.message) }, } ) }) if (token) { return } return (
{t("resetPassword.resetPassword")} {t("resetPassword.hint")}
{ return ( ) }} />
{showAlert && (
{t("resetPassword.successfulRequestTitle")} {t("resetPassword.successfulRequest")}
)}
, ]} />
) }