All files / packages/app/features/login reset-password-modal.tsx

100% Statements 15/15
100% Branches 6/6
100% Functions 6/6
100% Lines 15/15

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106                                14x           14x 8x 8x 2x             8x     8x         19x                         49x   16x                                       30x       64x 64x 64x         64x                                      
import type React from "react";
 
import { useMutation } from "@tanstack/react-query";
import { useTranslation } from "react-i18next";
import { z } from "zod";
 
import { useTrpcMutationOptions } from "~app/hooks/use-trpc-mutation-options";
import type { TRPCMutationResult } from "~app/trpc";
import { useAppForm } from "~app/utils/forms";
import { useTRPC } from "~app/utils/trpc";
import { emailSchema } from "~app/utils/validation";
import { Button } from "~components/button";
import { Modal } from "~components/modal";
import { Text } from "~components/text";
import { options as resetPasswordIntentionsAddOptions } from "~mutations/reset-password-intentions/add";
 
const formSchema = z.object({ email: emailSchema });
 
type Form = z.infer<typeof formSchema>;
 
const ResetPasswordModalForm: React.FC<{
	mutation: TRPCMutationResult<"resetPasswordIntentions.add">;
}> = ({ mutation }) => {
	const { t } = useTranslation("login");
	const defaultValues: Partial<Form> = {};
	const form = useAppForm({
		defaultValues: defaultValues as Form,
		validators: {
			onMount: formSchema,
			onChange: formSchema,
			onSubmit: formSchema,
		},
		onSubmit: ({ value }) => mutation.mutate(value),
	});
 
	return (
		<form.AppForm>
			<form.Form className="flex flex-col gap-4">
				<form.AppField name="email">
					{(field) => (
						<field.TextField
							value={field.state.value}
							onValueChange={field.setValue}
							name={field.name}
							onBlur={field.handleBlur}
							label={t("forgotPassword.modal.form.email.label")}
							fieldError={
								field.state.meta.isDirty ? field.state.meta.errors : undefined
							}
							mutation={mutation}
						/>
					)}
				</form.AppField>
				<form.Subscribe selector={(state) => state.canSubmit}>
					{(canSubmit) => (
						<Button
							color="primary"
							isDisabled={!canSubmit || mutation.isPending}
							isLoading={mutation.isPending}
							type="submit"
						>
							{t("forgotPassword.modal.form.submit")}
						</Button>
					)}
				</form.Subscribe>
			</form.Form>
		</form.AppForm>
	);
};
 
type Props = {
	isModalOpen: boolean;
	switchModalOpen: () => void;
};
 
export const ResetPasswordModal: React.FC<Props> = ({
	isModalOpen,
	switchModalOpen,
}) => {
	const { t } = useTranslation("login");
	const trpc = useTRPC();
	const resetPasswordMutation = useMutation(
		trpc.resetPasswordIntentions.add.mutationOptions(
			useTrpcMutationOptions(resetPasswordIntentionsAddOptions),
		),
	);
	return (
		<Modal
			isOpen={isModalOpen}
			label={t("forgotPassword.modal.header")}
			onOpenChange={switchModalOpen}
			header={<Text variant="h3">{t("forgotPassword.modal.header")}</Text>}
		>
			{resetPasswordMutation.status === "success" ? (
				<Text>
					{t("forgotPassword.modal.success", {
						email: resetPasswordMutation.variables.email,
					})}
				</Text>
			) : (
				<ResetPasswordModalForm mutation={resetPasswordMutation} />
			)}
		</Modal>
	);
};