Commit 460883d9 authored by tom's avatar tom

my profile page

parent a0c383ef
......@@ -3,12 +3,12 @@ import React from 'react';
import PageNextJs from 'nextjs/PageNextJs';
// import MyProfile from 'ui/pages/MyProfile';
import MyProfile from 'ui/pages/MyProfile';
const Page: NextPage = () => {
return (
<PageNextJs pathname="/auth/profile">
{ /* <MyProfile/> */ }
<MyProfile/>
</PageNextJs>
);
};
......
import { Button, chakra, Heading, useDisclosure } from '@chakra-ui/react';
import { chakra } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query';
import React from 'react';
import type { SubmitHandler } from 'react-hook-form';
......@@ -11,8 +11,11 @@ import config from 'configs/app';
import useApiFetch from 'lib/api/useApiFetch';
import getErrorMessage from 'lib/errors/getErrorMessage';
import getErrorObjPayload from 'lib/errors/getErrorObjPayload';
import useToast from 'lib/hooks/useToast';
import * as mixpanel from 'lib/mixpanel';
import { Button } from 'toolkit/chakra/button';
import { Heading } from 'toolkit/chakra/heading';
import { toaster } from 'toolkit/chakra/toaster';
import { useDisclosure } from 'toolkit/hooks/useDisclosure';
import FormFieldText from 'ui/shared/forms/fields/FormFieldText';
import ReCaptcha from 'ui/shared/reCaptcha/ReCaptcha';
import useReCaptcha from 'ui/shared/reCaptcha/useReCaptcha';
......@@ -33,7 +36,6 @@ interface Props {
const MyProfileEmail = ({ profileQuery }: Props) => {
const authModal = useDisclosure();
const apiFetch = useApiFetch();
const toast = useToast();
const recaptcha = useReCaptcha();
const formApi = useForm<FormFields>({
......@@ -65,25 +67,24 @@ const MyProfileEmail = ({ profileQuery }: Props) => {
authModal.onOpen();
} catch (error) {
const apiError = getErrorObjPayload<{ message: string }>(error);
toast({
status: 'error',
toaster.error({
title: 'Error',
description: apiError?.message || getErrorMessage(error) || 'Something went wrong',
});
}
}, [ apiFetch, authModal, toast, recaptcha ]);
}, [ apiFetch, authModal, recaptcha ]);
const hasDirtyFields = Object.keys(formApi.formState.dirtyFields).length > 0;
return (
<section>
<Heading as="h2" size="sm" mb={ 3 }>Notifications</Heading>
<Heading level="2" mb={ 3 }>Notifications</Heading>
<FormProvider { ...formApi }>
<chakra.form
noValidate
onSubmit={ formApi.handleSubmit(onFormSubmit) }
>
<FormFieldText<FormFields> name="name" placeholder="Name" isReadOnly mb={ 3 }/>
<FormFieldText<FormFields> name="name" placeholder="Name" readOnly mb={ 3 }/>
<MyProfileFieldsEmail
isReadOnly={ !config.services.reCaptchaV2.siteKey || Boolean(profileQuery.data?.email) }
defaultValue={ profileQuery.data?.email || undefined }
......@@ -95,8 +96,8 @@ const MyProfileEmail = ({ profileQuery }: Props) => {
size="sm"
variant="outline"
type="submit"
isDisabled={ formApi.formState.isSubmitting || !hasDirtyFields }
isLoading={ formApi.formState.isSubmitting }
disabled={ formApi.formState.isSubmitting || !hasDirtyFields }
loading={ formApi.formState.isSubmitting }
loadingText="Save changes"
>
Save changes
......@@ -104,7 +105,7 @@ const MyProfileEmail = ({ profileQuery }: Props) => {
) }
</chakra.form>
</FormProvider>
{ authModal.isOpen && (
{ authModal.open && (
<AuthModal
initialScreen={{ type: 'otp_code', isAuth: true, email: formApi.getValues('email') }}
onClose={ authModal.onClose }
......
import { Box, Button, Heading, Text, useColorModeValue } from '@chakra-ui/react';
import { Box, Text } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query';
import React from 'react';
import type { UserInfo } from 'types/api/account';
import config from 'configs/app';
import { Button } from 'toolkit/chakra/button';
import { Heading } from 'toolkit/chakra/heading';
import { Link } from 'toolkit/chakra/link';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import LinkExternal from 'ui/shared/links/LinkExternal';
interface Props {
profileQuery: UseQueryResult<UserInfo, unknown>;
......@@ -14,24 +16,23 @@ interface Props {
}
const MyProfileWallet = ({ profileQuery, onAddWallet }: Props) => {
const bgColor = useColorModeValue('blackAlpha.50', 'whiteAlpha.50');
return (
<section>
<Heading as="h2" size="sm" mb={ 3 }>My linked wallet</Heading>
<Heading level="2" mb={ 3 }>My linked wallet</Heading>
<Text mb={ 3 } >
This wallet address is used for login{ ' ' }
{ config.features.rewards.isEnabled && (
<>
and participation in the Merits Program.
<LinkExternal href="https://docs.blockscout.com/using-blockscout/merits" ml={ 1 }>
<Link external href="https://docs.blockscout.com/using-blockscout/merits" ml={ 1 }>
Learn more
</LinkExternal>
</Link>
</>
) }
</Text>
{ profileQuery.data?.address_hash ? (
<Box px={ 3 } py="18px" bgColor={ bgColor } borderRadius="base">
<Box px={ 3 } py="18px" bgColor={{ _light: 'blackAlpha.50', _dark: 'whiteAlpha.50' }} borderRadius="base">
<AddressEntity
address={{ hash: profileQuery.data.address_hash }}
fontWeight="500"
......
import { FormControl, Input, InputGroup, InputRightElement, Text } from '@chakra-ui/react';
import React from 'react';
import { useController, useFormContext } from 'react-hook-form';
import type { FormFields } from '../types';
import FormInputPlaceholder from 'ui/shared/forms/inputs/FormInputPlaceholder';
import { EMAIL_REGEXP } from 'ui/shared/forms/validators/email';
import FormFieldEmail from 'ui/shared/forms/fields/FormFieldEmail';
import IconSvg from 'ui/shared/IconSvg';
interface Props {
......@@ -14,36 +11,21 @@ interface Props {
}
const MyProfileFieldsEmail = ({ isReadOnly, defaultValue }: Props) => {
const { control } = useFormContext<FormFields>();
const { field, fieldState, formState } = useController<FormFields, 'email'>({
control,
name: 'email',
rules: { required: true, pattern: EMAIL_REGEXP },
});
const isDisabled = formState.isSubmitting;
const isVerified = defaultValue && field.value === defaultValue;
return (
<FormControl variant="floating" isDisabled={ isDisabled } isRequired size="md">
<InputGroup>
<Input
{ ...field }
<FormFieldEmail<FormFields>
name="email"
placeholder="Email"
required
isInvalid={ Boolean(fieldState.error) }
isDisabled={ isDisabled }
isReadOnly={ isReadOnly }
autoComplete="off"
readOnly={ isReadOnly }
helperText="Email for watch list notifications and private tags"
group={{
endElement: ({ field }) => {
const isVerified = defaultValue && field.value === defaultValue;
return isVerified ? <IconSvg name="certified" boxSize={ 5 } color="green.500"/> : null;
},
}}
/>
<FormInputPlaceholder text="Email" error={ fieldState.error }/>
{ isVerified && (
<InputRightElement h="100%">
<IconSvg name="certified" boxSize={ 5 } color="green.500"/>
</InputRightElement>
) }
</InputGroup>
<Text variant="secondary" mt={ 1 } fontSize="sm">Email for watch list notifications and private tags</Text>
</FormControl>
);
};
......
import { Flex, useDisclosure } from '@chakra-ui/react';
import { Flex } from '@chakra-ui/react';
import React from 'react';
import type { Screen } from 'ui/snippets/auth/types';
import config from 'configs/app';
import { useDisclosure } from 'toolkit/hooks/useDisclosure';
import MyProfileEmail from 'ui/myProfile/MyProfileEmail';
import MyProfileWallet from 'ui/myProfile/MyProfileWallet';
import AccountPageDescription from 'ui/shared/AccountPageDescription';
......@@ -55,7 +56,7 @@ const MyProfile = () => {
{ config.features.blockchainInteraction.isEnabled &&
<MyProfileWallet profileQuery={ profileQuery } onAddWallet={ handleAddWalletClick }/> }
</Flex>
{ authModal.isOpen && authInitialScreen &&
{ authModal.open && authInitialScreen &&
<AuthModal initialScreen={ authInitialScreen } onClose={ authModal.onClose } mixpanelConfig={ MIXPANEL_CONFIG }/> }
</>
);
......
......@@ -7,6 +7,7 @@ import type { FormFieldPropsBase } from './types';
import { Field } from 'toolkit/chakra/field';
import type { InputProps } from 'toolkit/chakra/input';
import { Input } from 'toolkit/chakra/input';
import { InputGroup } from 'toolkit/chakra/input-group';
import type { TextareaProps } from 'toolkit/chakra/textarea';
import { Textarea } from 'toolkit/chakra/textarea';
......@@ -27,7 +28,7 @@ const FormFieldText = <
placeholder,
rules,
onBlur,
rightElement,
group,
inputProps,
asComponent,
size = 'xl',
......@@ -63,6 +64,15 @@ const FormFieldText = <
/>
);
const content = group ? (
<InputGroup
{ ...group }
endElement={ typeof group.endElement === 'function' ? group.endElement({ field }) : group.endElement }
>
{ input }
</InputGroup>
) : input;
return (
<Field
label={ placeholder }
......@@ -73,35 +83,9 @@ const FormFieldText = <
floating
{ ...restProps }
>
{ input }
{ content }
</Field>
);
// TODO @tom2drum add input group
// return (
// <FormControl
// className={ className }
// variant="floating"
// isDisabled={ isDisabled }
// isRequired={ isRequired }
// size={ size }
// bgColor={ bgColor }
// >
// { rightElement ? (
// <InputGroup>
// { input }
// { inputPlaceholder }
// <InputRightElement h="100%"> { rightElement({ field }) } </InputRightElement>
// </InputGroup>
// ) : (
// <>
// { input }
// { inputPlaceholder }
// </>
// ) }
// </FormControl>
// );
};
export default React.memo(FormFieldText) as typeof FormFieldText;
......@@ -3,6 +3,7 @@ import type { ControllerRenderProps, FieldValues, Path, RegisterOptions } from '
import type { FieldProps } from 'toolkit/chakra/field';
import type { InputProps } from 'toolkit/chakra/input';
import type { InputGroupProps } from 'toolkit/chakra/input-group';
import type { TextareaProps } from 'toolkit/chakra/textarea';
export interface FormFieldPropsBase<
......@@ -14,6 +15,9 @@ export interface FormFieldPropsBase<
rules?: Omit<RegisterOptions<FormFields, Name>, 'valueAsNumber' | 'valueAsDate' | 'setValueAs' | 'disabled'>;
onBlur?: () => void;
onChange?: () => void;
rightElement?: ({ field }: { field: ControllerRenderProps<FormFields, Name> }) => React.ReactNode;
inputProps?: InputProps | TextareaProps;
group?: Omit<InputGroupProps, 'children' | 'endElement'> & {
endElement?: React.ReactNode | (({ field }: { field: ControllerRenderProps<FormFields, Name> }) => React.ReactNode);
};
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment