import { Form, useStoreState } from "@ariakit/react"; import { type Dispatch, type SetStateAction, useState } from "react"; import { Link } from "wouter"; import { useAppConfig, useClient } from "../AppConfig/AppConfig.tsx"; import { cx } from "../class-names.ts"; import { interactiveText } from "../common.css.ts"; import { getSubmitFailureMessage } from "../failureMessages.ts"; import { Letterhead, LetterheadHeading, LetterheadParagraph, LetterheadSubmitButton, } from "../Letterhead/index.tsx"; import { button } from "../Letterhead/style.css.ts"; import { LetterheadFormActions, LetterheadHeader, LetterheadSubmitError, LetterheadTextField, } from "../LetterheadForm/index.tsx"; import { useForm } from "../use-form.ts"; import { validEmail, validPassword } from "../validations.ts"; import type { DefaultFormValues } from "./types.ts"; type SetStep = Dispatch>; function RequestPasswordResetStep(props: { defaultValues?: DefaultFormValues; setStep: SetStep; }) { const { defaultValues } = props; const { client, placeholders, hrefs } = useAppConfig(); const { form, submitName } = useForm({ defaultValues: { email: defaultValues?.email ?? "", }, validate: { email: validEmail, }, async onSubmit({ values }) { const op = await client.requestPasswordReset(values); return op.mapFailure((failure) => { return getSubmitFailureMessage(failure, { 404: "🤔 Couldn't find a user with this email address.", }); }); }, onSuccess(response, { values }) { props.setStep({ type: "SUBMIT_CODE", tokenId: response.tokenId, email: values.email, }); }, }); const emailValue = useStoreState(form, (s) => s.values.email); return ( Password reset Enter your Indie Tabletop Club account email to begin password reset. We will send you a one-time code to verify your access.
Continue {"Remembered your password? "} Log in {"."}
); } function SubmitCodeStep(props: { tokenId: string; email: string; setStep: SetStep; }) { const client = useClient(); const { form, submitName } = useForm({ defaultValues: { code: "", }, async onSubmit({ values }) { const op = await client.checkPasswordResetCode({ ...values, tokenId: props.tokenId, }); return op.mapFailure((failure) => { return getSubmitFailureMessage(failure, { 404: "🚫 This code is incorrect or expired. Please try again.", }); }); }, onSuccess(_, { values }) { props.setStep({ type: "SET_NEW_PASSWORD", tokenId: props.tokenId, code: values.code, email: props.email, }); }, }); return ( Submit code We've sent a one-time code to the email address you have provided. Please, enter the code in the field below to continue.
Verify code
); } function SetNewPasswordStep(props: { tokenId: string; code: string; email: string; setStep: SetStep; }) { const client = useClient(); const { form, submitName } = useForm({ defaultValues: { password: "" }, validate: { password: validPassword }, async onSubmit({ values }) { const res = await client.setNewPassword({ tokenId: props.tokenId, code: props.code, password: values.password, }); if (res.isFailure) { return res.mapFailure((failure) => { return getSubmitFailureMessage(failure, { 404: "⏱️ One-time code has expired. Please restart the password reset process.", }); }); } // Login attempt with new credentials. Must be performed in onSubmit so // that errors are correctly propagated. const loginOp = await client.login({ email: props.email, password: values.password, }); return loginOp.mapFailure(getSubmitFailureMessage); }, onSuccess() { props.setStep({ type: "SUCCESS" }); }, }); return ( Choose New Password Please choose a new password. Make it at least 8 characters long.
Save & Login
); } function SuccessStep() { const { hrefs } = useAppConfig(); return ( Success! Your password has been successfully reset and you've been automatically logged in. Yay! Go to dashboard ); } type ResetPasswordStep = | { type: "REQUEST_PASSWORD_RESET" } | { type: "SUBMIT_CODE"; tokenId: string; email: string } | { type: "SET_NEW_PASSWORD"; tokenId: string; code: string; email: string } | { type: "SUCCESS" }; export type PasswordResetCardProps = { /** * Default values for the initial request password reset step. * * You might want to provide a value for history state or query param, so that * if a user jumps between login and password reset, their email address * is maintained between the two locations. */ defaultValues?: DefaultFormValues; }; /** * Allows the user to reset their password. */ export function PasswordResetCard(props: PasswordResetCardProps) { const [step, setStep] = useState({ type: "REQUEST_PASSWORD_RESET", }); switch (step.type) { case "REQUEST_PASSWORD_RESET": { return ; } case "SUBMIT_CODE": { return ; } case "SET_NEW_PASSWORD": { return ; } case "SUCCESS": { return ; } } }