mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
connect email and code screens to API
This commit is contained in:
@@ -217,6 +217,15 @@ export const RESOURCES = {
|
||||
needAuth: true,
|
||||
},
|
||||
|
||||
// AUTH
|
||||
auth_send_otp: {
|
||||
path: '/api/account/v2/send_otp',
|
||||
},
|
||||
|
||||
auth_confirm_otp: {
|
||||
path: '/api/account/v2/confirm_otp',
|
||||
},
|
||||
|
||||
// STATS MICROSERVICE API
|
||||
stats_counters: {
|
||||
path: '/api/v1/counters',
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import getErrorObj from './getErrorObj';
|
||||
|
||||
export default function getErrorMessage(error: Error | undefined): string | undefined {
|
||||
const errorObj = getErrorObj(error);
|
||||
return errorObj && 'message' in errorObj && typeof errorObj.message === 'string' ? errorObj.message : undefined;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { chakra, FormControl, Input } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import { useController, useFormContext } from 'react-hook-form';
|
||||
|
||||
import type { EmailFormFields } from '../types';
|
||||
|
||||
import { EMAIL_REGEXP } from 'lib/validations/email';
|
||||
import InputPlaceholder from 'ui/shared/InputPlaceholder';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const AuthModalFieldEmail = ({ className }: Props) => {
|
||||
const { control } = useFormContext<EmailFormFields>();
|
||||
const { field, fieldState, formState } = useController<EmailFormFields, 'email'>({
|
||||
control,
|
||||
name: 'email',
|
||||
rules: { required: true, pattern: EMAIL_REGEXP },
|
||||
});
|
||||
|
||||
const isDisabled = formState.isSubmitting;
|
||||
|
||||
return (
|
||||
<FormControl className={ className } variant="floating" isDisabled={ isDisabled } isRequired size="md">
|
||||
<Input
|
||||
{ ...field }
|
||||
required
|
||||
isInvalid={ Boolean(fieldState.error) }
|
||||
isDisabled={ isDisabled }
|
||||
autoComplete="off"
|
||||
/>
|
||||
<InputPlaceholder text="Email" error={ fieldState.error }/>
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(chakra(AuthModalFieldEmail));
|
||||
@@ -0,0 +1,36 @@
|
||||
import { FormControl, Input } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import { useController, useFormContext } from 'react-hook-form';
|
||||
|
||||
import type { OtpCodeFormFields } from '../types';
|
||||
|
||||
import InputPlaceholder from 'ui/shared/InputPlaceholder';
|
||||
|
||||
const AuthModalFieldOtpCode = () => {
|
||||
const { control } = useFormContext<OtpCodeFormFields>();
|
||||
const { field, fieldState, formState } = useController<OtpCodeFormFields, 'code'>({
|
||||
control,
|
||||
name: 'code',
|
||||
rules: { required: true, minLength: 6, maxLength: 6 },
|
||||
});
|
||||
|
||||
const isDisabled = formState.isSubmitting;
|
||||
|
||||
return (
|
||||
<FormControl variant="floating" isDisabled={ isDisabled } isRequired size="md">
|
||||
<Input
|
||||
{ ...field }
|
||||
required
|
||||
isInvalid={ Boolean(fieldState.error) }
|
||||
isDisabled={ isDisabled }
|
||||
type="number"
|
||||
minLength={ 6 }
|
||||
maxLength={ 6 }
|
||||
autoComplete="one-time-code"
|
||||
/>
|
||||
<InputPlaceholder text="Confirmation code" error={ fieldState.error }/>
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(AuthModalFieldOtpCode);
|
||||
@@ -1,7 +1,16 @@
|
||||
import { Box, Button, Input, Text } from '@chakra-ui/react';
|
||||
import { chakra, Button, Text } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import type { SubmitHandler } from 'react-hook-form';
|
||||
import { FormProvider, useForm } from 'react-hook-form';
|
||||
|
||||
import type { Screen } from '../types';
|
||||
import type { EmailFormFields, Screen } from '../types';
|
||||
|
||||
import useApiFetch from 'lib/api/useApiFetch';
|
||||
import getErrorMessage from 'lib/errors/getErrorMessage';
|
||||
import useToast from 'lib/hooks/useToast';
|
||||
import FormFieldReCaptcha from 'ui/shared/forms/fields/FormFieldReCaptcha';
|
||||
|
||||
import AuthModalFieldEmail from '../fields/AuthModalFieldEmail';
|
||||
|
||||
interface Props {
|
||||
onSubmit: (screen: Screen) => void;
|
||||
@@ -9,16 +18,58 @@ interface Props {
|
||||
|
||||
const AuthModalScreenEmail = ({ onSubmit }: Props) => {
|
||||
|
||||
const handleSubmitClick = React.useCallback(() => {
|
||||
onSubmit({ type: 'otp_code', email: '[email protected]' });
|
||||
}, [ onSubmit ]);
|
||||
const apiFetch = useApiFetch();
|
||||
const toast = useToast();
|
||||
|
||||
const formApi = useForm<EmailFormFields>({
|
||||
mode: 'onBlur',
|
||||
defaultValues: {
|
||||
email: '',
|
||||
},
|
||||
});
|
||||
|
||||
const onFormSubmit: SubmitHandler<EmailFormFields> = React.useCallback((formData) => {
|
||||
return apiFetch('auth_send_otp', {
|
||||
fetchParams: {
|
||||
method: 'POST',
|
||||
body: {
|
||||
email: formData.email,
|
||||
recaptcha_response: formData.reCaptcha,
|
||||
},
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
onSubmit({ type: 'otp_code', email: formData.email });
|
||||
})
|
||||
.catch((error) => {
|
||||
toast({
|
||||
status: 'error',
|
||||
title: 'Error',
|
||||
description: getErrorMessage(error) || 'Something went wrong',
|
||||
});
|
||||
});
|
||||
}, [ apiFetch, onSubmit, toast ]);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Text>Account email, used for transaction notifications from your watchlist.</Text>
|
||||
<Input placeholder="Email" mt={ 6 }/>
|
||||
<Button mt={ 6 } onClick={ handleSubmitClick }>Send a code</Button>
|
||||
</Box>
|
||||
<FormProvider { ...formApi }>
|
||||
<chakra.form
|
||||
noValidate
|
||||
onSubmit={ formApi.handleSubmit(onFormSubmit) }
|
||||
>
|
||||
<Text>Account email, used for transaction notifications from your watchlist.</Text>
|
||||
<AuthModalFieldEmail mt={ 6 } mb={ 3 }/>
|
||||
<FormFieldReCaptcha/>
|
||||
<Button
|
||||
mt={ 6 }
|
||||
type="submit"
|
||||
isDisabled={ formApi.formState.isSubmitting || !formApi.formState.isValid }
|
||||
isLoading={ formApi.formState.isSubmitting }
|
||||
loadingText="Send a code"
|
||||
>
|
||||
Send a code
|
||||
</Button>
|
||||
</chakra.form>
|
||||
</FormProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
import { chakra, Box, Text, Input, Button, Link } from '@chakra-ui/react';
|
||||
import { chakra, Box, Text, Button, Link } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import type { SubmitHandler } from 'react-hook-form';
|
||||
import { FormProvider, useForm } from 'react-hook-form';
|
||||
|
||||
import type { Screen } from '../types';
|
||||
import type { OtpCodeFormFields, Screen } from '../types';
|
||||
|
||||
import useApiFetch from 'lib/api/useApiFetch';
|
||||
import getErrorMessage from 'lib/errors/getErrorMessage';
|
||||
import useToast from 'lib/hooks/useToast';
|
||||
import IconSvg from 'ui/shared/IconSvg';
|
||||
|
||||
import AuthModalFieldOtpCode from '../fields/AuthModalFieldOtpCode';
|
||||
|
||||
interface Props {
|
||||
email: string;
|
||||
onSubmit: (screen: Screen) => void;
|
||||
@@ -12,24 +19,97 @@ interface Props {
|
||||
|
||||
const AuthModalScreenOtpCode = ({ email, onSubmit }: Props) => {
|
||||
|
||||
const handleSubmitClick = React.useCallback(() => {
|
||||
onSubmit({ type: 'success_created_email' });
|
||||
}, [ onSubmit ]);
|
||||
const apiFetch = useApiFetch();
|
||||
const toast = useToast();
|
||||
|
||||
const formApi = useForm<OtpCodeFormFields>({
|
||||
mode: 'onBlur',
|
||||
defaultValues: {
|
||||
code: '',
|
||||
},
|
||||
});
|
||||
|
||||
const onFormSubmit: SubmitHandler<OtpCodeFormFields> = React.useCallback((formData) => {
|
||||
return apiFetch('auth_confirm_otp', {
|
||||
fetchParams: {
|
||||
method: 'POST',
|
||||
body: {
|
||||
otp: formData.code,
|
||||
email,
|
||||
},
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
onSubmit({ type: 'success_created_email' });
|
||||
})
|
||||
.catch((error) => {
|
||||
// TODO @tom2drum handle incorrect code error
|
||||
toast({
|
||||
status: 'error',
|
||||
title: 'Error',
|
||||
description: getErrorMessage(error) || 'Something went wrong',
|
||||
});
|
||||
});
|
||||
}, [ apiFetch, email, onSubmit, toast ]);
|
||||
|
||||
const handleResendCodeClick = React.useCallback(() => {
|
||||
return apiFetch('auth_send_otp', {
|
||||
fetchParams: {
|
||||
method: 'POST',
|
||||
body: {
|
||||
email,
|
||||
},
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
toast({
|
||||
status: 'success',
|
||||
title: 'Code sent',
|
||||
description: 'Code has been sent to your email',
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
toast({
|
||||
status: 'error',
|
||||
title: 'Error',
|
||||
description: getErrorMessage(error) || 'Something went wrong',
|
||||
});
|
||||
});
|
||||
}, [ apiFetch, email, toast ]);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Text>
|
||||
Please check{ ' ' }
|
||||
<chakra.span fontWeight="700">{ email }</chakra.span>{ ' ' }
|
||||
and enter your code below.
|
||||
</Text>
|
||||
<Input placeholder="Confirmation code" mt={ 6 }/>
|
||||
<Link display="flex" alignItems="center" gap={ 2 } mt={ 3 } w="fit-content">
|
||||
<IconSvg name="repeat" boxSize={ 5 }/>
|
||||
<Box fontSize="sm">Resend code</Box>
|
||||
</Link>
|
||||
<Button mt={ 6 } onClick={ handleSubmitClick }>Submit</Button>
|
||||
</Box>
|
||||
<FormProvider { ...formApi }>
|
||||
<chakra.form
|
||||
noValidate
|
||||
onSubmit={ formApi.handleSubmit(onFormSubmit) }
|
||||
>
|
||||
<Text mb={ 6 }>
|
||||
Please check{ ' ' }
|
||||
<chakra.span fontWeight="700">{ email }</chakra.span>{ ' ' }
|
||||
and enter your code below.
|
||||
</Text>
|
||||
<AuthModalFieldOtpCode/>
|
||||
<Link
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
gap={ 2 }
|
||||
mt={ 3 }
|
||||
w="fit-content"
|
||||
onClick={ handleResendCodeClick }
|
||||
>
|
||||
<IconSvg name="repeat" boxSize={ 5 }/>
|
||||
<Box fontSize="sm">Resend code</Box>
|
||||
</Link>
|
||||
<Button
|
||||
mt={ 6 }
|
||||
type="submit"
|
||||
isLoading={ formApi.formState.isSubmitting }
|
||||
onClick={ formApi.handleSubmit(onFormSubmit) }
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</chakra.form>
|
||||
</FormProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -8,3 +8,12 @@ export type Screen = {
|
||||
} | {
|
||||
type: 'success_created_email';
|
||||
}
|
||||
|
||||
export interface EmailFormFields {
|
||||
email: string;
|
||||
reCaptcha: string;
|
||||
}
|
||||
|
||||
export interface OtpCodeFormFields {
|
||||
code: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user