Skip to content
19 changes: 7 additions & 12 deletions src/pages/auth/forgot-password/model/useResetPassword.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,18 @@
import { type DefaultError, useMutation } from '@tanstack/react-query';
import { type DefaultError, useMutation, UseMutationOptions } from '@tanstack/react-query';
import { AuthHttp, TAuth } from 'entities/auth';

interface ResetPasswordProps {
onSuccess?: (body: TAuth.ResetPasswordBody, res: TAuth.ResetPasswordResponse) => void;
onError?: (err: Error) => void;
}
export type UseResetePasswordOptions = Omit<
UseMutationOptions<TAuth.ResetPasswordResponse, DefaultError, TAuth.ResetPasswordBody>,
'mutationFn'
>;

export function useResetPassword({ onSuccess, onError }: ResetPasswordProps = {}) {
export function useResetPassword(options: UseResetePasswordOptions = {}) {
return useMutation<Awaited<TAuth.ResetPasswordResponse>, DefaultError, TAuth.ResetPasswordBody>({
mutationKey: [],
mutationFn: AuthHttp.resetPassword,
meta: {
skipGlobalValidationToast: true,
},
onError: (err) => {
onError?.(err);
},
onSuccess: (res, body) => {
onSuccess?.(body, res);
},
...options,
});
}
14 changes: 12 additions & 2 deletions src/pages/auth/forgot-password/model/useSendCode.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import { type DefaultError, useMutation } from '@tanstack/react-query';
import { type DefaultError, useMutation, UseMutationOptions } from '@tanstack/react-query';
import { AuthHttp, TAuth } from 'entities/auth';

export function useSendCode() {
export type UseSendCodeOptions = Omit<
UseMutationOptions<
TAuth.ResetPasswordVerifyResponse,
DefaultError,
TAuth.ResetPasswordVerifyBody
>,
'mutationFn'
>;

export function useSendCode(options: UseSendCodeOptions = {}) {
return useMutation<
Awaited<TAuth.ResetPasswordVerifyResponse>,
DefaultError,
Expand All @@ -12,5 +21,6 @@ export function useSendCode() {
meta: {
skipGlobalValidationToast: true,
},
...options,
});
}
26 changes: 11 additions & 15 deletions src/pages/auth/forgot-password/model/useSendPassword.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { type DefaultError, useMutation } from '@tanstack/react-query';
import { type DefaultError, useMutation, UseMutationOptions } from '@tanstack/react-query';
import { AuthHttp, TAuth } from 'entities/auth';

interface SendPasswordProps {
onSuccess?: (
body: TAuth.ResetPasswordConfirmBody,
res: TAuth.ResetPasswordConfirmResponse
) => void;
onError?: (err: Error) => void;
}
export type UseSendPasswordOptions = Omit<
UseMutationOptions<
TAuth.ResetPasswordConfirmResponse,
DefaultError,
TAuth.ResetPasswordConfirmBody
>,
'mutationFn'
>;

export function useSendPassword({ onSuccess, onError }: SendPasswordProps = {}) {
export function useSendPassword(options: UseSendPasswordOptions = {}) {
return useMutation<
Awaited<TAuth.ResetPasswordConfirmResponse>,
DefaultError,
Expand All @@ -20,11 +21,6 @@ export function useSendPassword({ onSuccess, onError }: SendPasswordProps = {})
meta: {
skipGlobalValidationToast: true,
},
onError: (err) => {
onError?.(err);
},
onSuccess: (res, body) => {
onSuccess?.(body, res);
},
...options,
});
}
19 changes: 11 additions & 8 deletions src/pages/auth/forgot-password/ui/EmailForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ import { Controller, useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import type { EmailFormValues } from '../model/types';
import { EmailForm as EmailFormSchema } from '../model/schemas';
import { ComponentProps } from 'react';
import { useResetPassword } from '../model/useResetPassword';
import { UseResetePasswordOptions, useResetPassword } from '../model/useResetPassword';
import { setFormErrors } from 'shared/lib/utils';
import { extractValidationIssues } from 'shared/api';
import { ComponentProps } from 'react';

interface EmailFormProps extends Omit<ComponentProps<'form'>, 'children' | 'onSubmit'> {
onSuccess?: (body: TAuth.ResetPasswordBody, res: TAuth.ResetPasswordResponse) => void;
}
type EmailFormProps = Omit<ComponentProps<'form'>, 'children' | 'onSubmit'> & {
mutateOptions?: UseResetePasswordOptions;
};

function EmailForm({ onSuccess, ...props }: EmailFormProps) {
function EmailForm({ mutateOptions = {}, ...props }: EmailFormProps) {
const form = useForm<EmailFormValues>({
resolver: zodResolver(EmailFormSchema),
defaultValues: {
Expand All @@ -36,8 +36,11 @@ function EmailForm({ onSuccess, ...props }: EmailFormProps) {
});

const resetPassword = useResetPassword({
onSuccess,
onError: (err) => setFormErrors(extractValidationIssues(err), form),
...mutateOptions,
onError: (err, ...args) => {
mutateOptions.onError?.(err, ...args);
setFormErrors(extractValidationIssues(err), form);
},
});

const onSubmit = (data: EmailFormValues) => {
Expand Down
16 changes: 11 additions & 5 deletions src/pages/auth/forgot-password/ui/ForgotPasswordPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,11 @@ function ForgotPasswordPage() {
<Logo size="sm" />
</Link>
{step === 'email' ? (
<EmailForm onSuccess={({ email }) => setDraft({ email, step: 'otp' }, DRAFT_TTL_MS)} />
<EmailForm
mutateOptions={{
onSuccess: (_res, { email }) => setDraft({ email, step: 'otp' }, DRAFT_TTL_MS),
}}
/>
) : null}
{step === 'otp' && (
<OTPForm
Expand All @@ -64,10 +68,12 @@ function ForgotPasswordPage() {
{step === 'password' && (
<PasswordForm
email={email}
onSuccess={(_, res) => {
clearDraft();
router.replace(routes.auth.signin());
toast.success(res.message);
mutateOptions={{
onSuccess: (res) => {
clearDraft();
router.replace(routes.auth.signin());
toast.success(res.message);
},
}}
/>
)}
Expand Down
16 changes: 8 additions & 8 deletions src/pages/auth/forgot-password/ui/PasswordForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,22 @@ import { setFormErrors } from 'shared/lib/utils';
import { extractValidationIssues } from 'shared/api';
import { PasswordForm as PasswordFormSchema } from '../model/schemas';
import type { PasswordFormValues } from '../model/types';
import { useSendPassword } from '../model/useSendPassword';
import { useSendPassword, UseSendPasswordOptions } from '../model/useSendPassword';

interface PasswordFormProps extends Omit<ComponentProps<'form'>, 'children' | 'onSubmit'> {
onSuccess?: (
body: TAuth.ResetPasswordConfirmBody,
res: TAuth.ResetPasswordConfirmResponse
) => void;
mutateOptions?: UseSendPasswordOptions;
email: string;
}

function PasswordForm({ onSuccess, email, ...props }: PasswordFormProps) {
function PasswordForm({ mutateOptions = {}, email, ...props }: PasswordFormProps) {
const [showPassword, setShowPassword] = useState(false);

const sendPassword = useSendPassword({
onSuccess,
onError: (err) => setFormErrors(extractValidationIssues(err), form),
...mutateOptions,
onError: (err, ...args) => {
mutateOptions.onError?.(err, ...args);
setFormErrors(extractValidationIssues(err), form);
},
});

const form = useForm<PasswordFormValues>({
Expand Down
19 changes: 7 additions & 12 deletions src/pages/auth/signin/model/useSignin.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
import { type DefaultError, useMutation } from '@tanstack/react-query';
import { type DefaultError, useMutation, UseMutationOptions } from '@tanstack/react-query';
import { AuthHttp, TAuth } from 'entities/auth';

interface UseSigninProps {
onSuccess?: (body: TAuth.SigninBody, res: TAuth.SigninResponse) => void;
onError?: (err: Error) => void;
}
export type UseSigninOptions = Omit<
UseMutationOptions<TAuth.SigninResponse, DefaultError, TAuth.SigninBody>,
'mutationFn'
>;

export function useSignin({ onSuccess, onError }: UseSigninProps = {}) {
export function useSignin(options: UseSigninOptions = {}) {
return useMutation<Awaited<TAuth.SigninResponse>, DefaultError, TAuth.SigninBody>({
mutationFn: AuthHttp.signin,
meta: {
skipGlobalValidationToast: true,
},
onError: (err) => {
onError?.(err);
},
onSuccess: (res, body) => {
onSuccess?.(body, res);
},
...options,
});
}
11 changes: 6 additions & 5 deletions src/pages/auth/signin/ui/SigninForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ import { routes } from 'shared/config';
import { extractValidationIssues } from 'shared/api';
import { TAuth } from 'entities/auth';
import { ComponentProps } from 'react';
import { useSignin } from '../model/useSignin';
import { useSignin, UseSigninOptions } from '../model/useSignin';

interface SigninFormProps extends Omit<ComponentProps<'form'>, 'children' | 'onSubmit'> {
onSuccess?: (body: TAuth.SigninBody, res: TAuth.SigninResponse) => void;
mutateOptions?: UseSigninOptions;
}

export function SigninForm({ className, onSuccess, ...props }: SigninFormProps) {
export function SigninForm({ className, mutateOptions = {}, ...props }: SigninFormProps) {
const form = useForm<SigninFormValues>({
resolver: zodResolver(SigninFormSchema),
defaultValues: {
Expand All @@ -41,8 +41,9 @@ export function SigninForm({ className, onSuccess, ...props }: SigninFormProps)
});

const sendUserData = useSignin({
onSuccess,
onError: (err) => {
...mutateOptions,
onError: (err, ...args) => {
mutateOptions.onError?.(err, ...args);
setFormErrors(extractValidationIssues(err), form);
},
});
Expand Down
16 changes: 9 additions & 7 deletions src/pages/auth/signin/ui/SigninPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,16 @@ function SigninPage() {
<Logo size="sm" />
</Link>
<SigninForm
onSuccess={(_, res) => {
if (res.success) {
AccessToken.token = res.token;
router.replace(routes.profile.root());
if (res.message) {
toast.success(res.message);
mutateOptions={{
onSuccess: (res) => {
if (res.success) {
AccessToken.token = res.token;
router.replace(routes.profile.root());
if (res.message) {
toast.success(res.message);
}
}
}
},
}}
/>
</div>
Expand Down
19 changes: 7 additions & 12 deletions src/pages/auth/signup/model/useSignup.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
import { type DefaultError, useMutation } from '@tanstack/react-query';
import { type DefaultError, useMutation, UseMutationOptions } from '@tanstack/react-query';
import { AuthHttp, TAuth } from 'entities/auth';

interface UseSignupProps {
onSuccess?: (body: TAuth.SignupBody, res: TAuth.SignupResponse) => void;
onError?: (err: Error) => void;
}
export type UseSignupOptions = Omit<
UseMutationOptions<TAuth.SignupResponse, DefaultError, TAuth.SignupBody>,
'mutationFn'
>;

export function useSignup({ onSuccess, onError }: UseSignupProps = {}) {
export function useSignup(options: UseSignupOptions = {}) {
return useMutation<Awaited<TAuth.SignupResponse>, DefaultError, TAuth.SignupBody>({
mutationFn: AuthHttp.signup,
meta: {
skipGlobalValidationToast: true,
},
onError: (err) => {
onError?.(err);
},
onSuccess: (res, body) => {
onSuccess?.(body, res);
},
...options,
});
}
10 changes: 8 additions & 2 deletions src/pages/auth/signup/model/useSignupConfirm.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { type DefaultError, useMutation } from '@tanstack/react-query';
import { type DefaultError, useMutation, UseMutationOptions } from '@tanstack/react-query';
import { AuthHttp, TAuth } from 'entities/auth';

export function useSignupConfirm() {
export type UseSignupConfirmOptions = Omit<
UseMutationOptions<TAuth.SignupConfirmResponse, DefaultError, TAuth.SignupConfirmBody>,
'mutationFn'
>;

export function useSignupConfirm(options: UseSignupConfirmOptions = {}) {
return useMutation<Awaited<TAuth.SignupConfirmResponse>, DefaultError, TAuth.SignupConfirmBody>({
mutationFn: AuthHttp.signupConfirm,
meta: {
skipGlobalValidationToast: true,
},
...options,
});
}
11 changes: 6 additions & 5 deletions src/pages/auth/signup/ui/SignupForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ import { fieldNameMapper } from '../model/utils/field-name-mapper';
import { prepareFullName } from '../model/utils/prepare-fullname';
import { extractValidationIssues } from 'shared/api';
import { TAuth } from 'entities/auth';
import { useSignup } from '../model/useSignup';
import { useSignup, UseSignupOptions } from '../model/useSignup';

interface SignupFormProps extends Omit<ComponentProps<'form'>, 'children' | 'onSubmit'> {
onSuccess?: (body: TAuth.SignupBody, res: TAuth.SignupResponse) => void;
mutateOptions?: UseSignupOptions;
}

export function SignupForm({ className, onSuccess, ...props }: SignupFormProps) {
export function SignupForm({ className, mutateOptions = {}, ...props }: SignupFormProps) {
const [showPassword, setShowPassword] = useState(false);

const form = useForm<SignupFormValues>({
Expand All @@ -49,8 +49,9 @@ export function SignupForm({ className, onSuccess, ...props }: SignupFormProps)
});

const sendUserData = useSignup({
onSuccess,
onError: (err) => {
...mutateOptions,
onError: (err, ...args) => {
mutateOptions.onError?.(err, ...args);
setFormErrors<SignupFormValues, TAuth.SignupBody>(
extractValidationIssues(err),
form,
Expand Down
6 changes: 5 additions & 1 deletion src/pages/auth/signup/ui/SignupPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ function SignupPage() {
</Link>

{step === 'signup' ? (
<SignupForm onSuccess={({ email }) => setDraft({ email, step: 'otp' }, DRAFT_TTL_MS)} />
<SignupForm
mutateOptions={{
onSuccess: (_res, { email }) => setDraft({ email, step: 'otp' }, DRAFT_TTL_MS),
}}
/>
) : null}
{step === 'otp' ? (
<OTPForm
Expand Down
Loading