mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
token info form
This commit is contained in:
@@ -20,12 +20,12 @@ export const Field = React.forwardRef<HTMLDivElement, FieldProps>(
|
||||
const { label, children, helperText, errorText, optionalText, ...rest } = props;
|
||||
|
||||
// A floating field cannot be without a label.
|
||||
if (props.floating && label) {
|
||||
if (rest.floating && label) {
|
||||
const injectedProps = {
|
||||
className: 'peer',
|
||||
placeholder: ' ',
|
||||
size: props.size,
|
||||
floating: props.floating,
|
||||
size: rest.size,
|
||||
floating: rest.floating,
|
||||
bgColor: rest.bgColor,
|
||||
disabled: rest.disabled,
|
||||
readOnly: rest.readOnly,
|
||||
@@ -79,6 +79,13 @@ export const Field = React.forwardRef<HTMLDivElement, FieldProps>(
|
||||
);
|
||||
}
|
||||
|
||||
// Pass size value to the input component
|
||||
const injectedProps = {
|
||||
size: rest.size,
|
||||
};
|
||||
const child = React.Children.only<React.ReactElement<InputProps | InputGroupProps>>(children);
|
||||
const clonedChild = React.cloneElement(child, injectedProps);
|
||||
|
||||
return (
|
||||
<ChakraField.Root ref={ ref } { ...rest }>
|
||||
{ label && (
|
||||
@@ -87,7 +94,7 @@ export const Field = React.forwardRef<HTMLDivElement, FieldProps>(
|
||||
<ChakraField.RequiredIndicator fallback={ optionalText }/>
|
||||
</ChakraField.Label>
|
||||
) }
|
||||
{ children }
|
||||
{ clonedChild }
|
||||
{ helperText && (
|
||||
<ChakraField.HelperText>{ helperText }</ChakraField.HelperText>
|
||||
) }
|
||||
|
||||
@@ -117,6 +117,9 @@ export const recipe = defineSlotRecipe({
|
||||
floating: true,
|
||||
css: {
|
||||
label: {
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
padding: '10px 16px 0px 16px',
|
||||
textStyle: 'xs',
|
||||
_peerPlaceholderShown: {
|
||||
@@ -142,6 +145,9 @@ export const recipe = defineSlotRecipe({
|
||||
floating: true,
|
||||
css: {
|
||||
label: {
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
// 16px = scrollbar width
|
||||
width: 'calc(100% - 4px - 20px)',
|
||||
padding: '20px 24px 0px 24px',
|
||||
|
||||
@@ -12,7 +12,6 @@ import getQueryParamString from 'lib/router/getQueryParamString';
|
||||
import { TOKEN_INFO_APPLICATION, VERIFIED_ADDRESS } from 'stubs/account';
|
||||
import { Button } from 'toolkit/chakra/button';
|
||||
import { Link } from 'toolkit/chakra/link';
|
||||
import { Skeleton } from 'toolkit/chakra/skeleton';
|
||||
import { useDisclosure } from 'toolkit/hooks/useDisclosure';
|
||||
import AddressVerificationModal from 'ui/addressVerification/AddressVerificationModal';
|
||||
import AccountPageDescription from 'ui/shared/AccountPageDescription';
|
||||
@@ -144,12 +143,12 @@ const VerifiedAddresses = () => {
|
||||
return (
|
||||
<>
|
||||
<PageTitle title="Token info application form" backLink={ backLink }/>
|
||||
{ /* <TokenInfoForm
|
||||
<TokenInfoForm
|
||||
address={ selectedAddress }
|
||||
tokenName={ tokenName }
|
||||
application={ applicationsQuery.data?.submissions.find(({ tokenAddress }) => tokenAddress.toLowerCase() === selectedAddress.toLowerCase()) }
|
||||
onSubmit={ handleApplicationSubmit }
|
||||
/> */ }
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import type { ColorMode } from '@chakra-ui/react';
|
||||
import { Image, chakra, DarkMode } from '@chakra-ui/react';
|
||||
import { chakra } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import Skeleton from 'ui/shared/chakra/Skeleton';
|
||||
import type { ColorMode } from 'toolkit/chakra/color-mode';
|
||||
import { Image } from 'toolkit/chakra/image';
|
||||
import { Skeleton } from 'toolkit/chakra/skeleton';
|
||||
|
||||
interface Props {
|
||||
src: string | undefined;
|
||||
@@ -23,11 +24,11 @@ const ImageUrlPreview = ({
|
||||
fallback: fallbackProp,
|
||||
colorMode,
|
||||
}: Props) => {
|
||||
const skeleton = <Skeleton className={ className } w="100%" h="100%"/>;
|
||||
const skeleton = <Skeleton loading className={ [ className, colorMode === 'dark' ? 'dark' : undefined ].filter(Boolean).join(' ') } w="100%" h="100%"/>;
|
||||
|
||||
const fallback = (() => {
|
||||
if (src && !isInvalid) {
|
||||
return colorMode === 'dark' ? <DarkMode>{ skeleton }</DarkMode> : skeleton;
|
||||
return skeleton;
|
||||
}
|
||||
return fallbackProp;
|
||||
})();
|
||||
|
||||
@@ -13,6 +13,7 @@ import FancySelect from 'ui/shared/forms/inputs/select/FancySelect';
|
||||
// this type only works for plain objects, not for nested objects or arrays (e.g. ui/publicTags/submit/types.ts:FormFields)
|
||||
// type SelectField<O> = { [K in keyof O]: NonNullable<O[K]> extends Option ? K : never }[keyof O];
|
||||
|
||||
// TODO @tom2drum remove this component
|
||||
type Props<
|
||||
FormFields extends FieldValues,
|
||||
Name extends Path<FormFields>,
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import React from 'react';
|
||||
import type { Path, FieldValues } from 'react-hook-form';
|
||||
import { useController, useFormContext } from 'react-hook-form';
|
||||
|
||||
import type { FormFieldPropsBase } from './types';
|
||||
|
||||
import type { SelectRootProps } from 'toolkit/chakra/select';
|
||||
import { SelectContent, SelectControl, SelectItem, SelectRoot, SelectValueText } from 'toolkit/chakra/select';
|
||||
|
||||
type Props<
|
||||
FormFields extends FieldValues,
|
||||
Name extends Path<FormFields>,
|
||||
> = FormFieldPropsBase<FormFields, Name> & SelectRootProps;
|
||||
|
||||
const FormFieldSelect = <
|
||||
FormFields extends FieldValues,
|
||||
Name extends Path<FormFields>,
|
||||
>(props: Props<FormFields, Name>) => {
|
||||
const { name, rules, collection, placeholder, ...rest } = props;
|
||||
|
||||
const { control } = useFormContext<FormFields>();
|
||||
const { field, fieldState, formState } = useController<FormFields, typeof name>({
|
||||
control,
|
||||
name,
|
||||
rules,
|
||||
});
|
||||
|
||||
const isDisabled = formState.isSubmitting;
|
||||
|
||||
const handleChange = React.useCallback(({ value }: { value: Array<string> }) => {
|
||||
field.onChange(value);
|
||||
}, [ field ]);
|
||||
|
||||
const handleBlur = React.useCallback(() => {
|
||||
field.onBlur();
|
||||
}, [ field ]);
|
||||
|
||||
// TODO @tom2drum: fix initial value is not displayed in the select
|
||||
return (
|
||||
<SelectRoot
|
||||
ref={ field.ref }
|
||||
name={ field.name }
|
||||
value={ field.value }
|
||||
onValueChange={ handleChange }
|
||||
onInteractOutside={ handleBlur }
|
||||
collection={ collection }
|
||||
disabled={ isDisabled }
|
||||
invalid={ Boolean(fieldState.error) }
|
||||
{ ...rest }
|
||||
>
|
||||
<SelectControl>
|
||||
<SelectValueText placeholder={ placeholder }/>
|
||||
</SelectControl>
|
||||
<SelectContent>
|
||||
{ collection.items.map((item) => (
|
||||
<SelectItem item={ item } key={ item.value }>
|
||||
{ item.label }
|
||||
</SelectItem>
|
||||
)) }
|
||||
</SelectContent>
|
||||
</SelectRoot>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(FormFieldSelect) as typeof FormFieldSelect;
|
||||
+12
-12
@@ -15,32 +15,32 @@ const FieldShowcase = () => {
|
||||
{ ([ 'sm', 'md', 'lg' ] as const).map((size) => (
|
||||
<Sample label={ `size: ${ size }` } w="100%" key={ size } alignItems="flex-start">
|
||||
<Field label="Email" required size={ size } helperText="Helper text" maxWidth="200px">
|
||||
<Input size={ size }/>
|
||||
<Input/>
|
||||
</Field>
|
||||
<Field label="Email (disabled)" required size={ size } maxWidth="200px">
|
||||
<Input size={ size } disabled value="[email protected]"/>
|
||||
<Input disabled value="[email protected]"/>
|
||||
</Field>
|
||||
<Field label="Email (readOnly)" required size={ size } maxWidth="200px">
|
||||
<Input size={ size } readOnly value="[email protected]"/>
|
||||
<Input readOnly value="[email protected]"/>
|
||||
</Field>
|
||||
<Field label="Email (invalid)" required size={ size } errorText="Something went wrong" invalid maxWidth="200px">
|
||||
<Input size={ size } value="duck"/>
|
||||
<Input value="duck"/>
|
||||
</Field>
|
||||
</Sample>
|
||||
)) }
|
||||
|
||||
<Sample label="size: xl" w="100%" alignItems="flex-start">
|
||||
<Field label="Email" required floating size="xl" helperText="Helper text" maxWidth="300px">
|
||||
<Input size="xl"/>
|
||||
<Input/>
|
||||
</Field>
|
||||
<Field label="Email (disabled)" required floating disabled size="xl" maxWidth="300px">
|
||||
<Input size="xl" value="[email protected]"/>
|
||||
<Input value="[email protected]"/>
|
||||
</Field>
|
||||
<Field label="Email (readOnly)" required floating readOnly size="xl" maxWidth="300px">
|
||||
<Input size="xl" value="[email protected]"/>
|
||||
<Input value="[email protected]"/>
|
||||
</Field>
|
||||
<Field label="Email (invalid)" required floating size="xl" errorText="Something went wrong" invalid maxWidth="300px">
|
||||
<Input size="xl" value="duck"/>
|
||||
<Input value="duck"/>
|
||||
</Field>
|
||||
</Sample>
|
||||
</SamplesStack>
|
||||
@@ -63,16 +63,16 @@ const FieldShowcase = () => {
|
||||
</Sample>
|
||||
<Sample label="floating label" p={ 4 } bgColor={{ _light: 'blackAlpha.200', _dark: 'whiteAlpha.200' }} alignItems="flex-start">
|
||||
<Field label="Email" required floating size="xl" helperText="Helper text" maxWidth="300px">
|
||||
<Input size="xl"/>
|
||||
<Input/>
|
||||
</Field>
|
||||
<Field label="Email (disabled)" required disabled floating size="xl" maxWidth="300px">
|
||||
<Input size="xl" value="[email protected]"/>
|
||||
<Input value="[email protected]"/>
|
||||
</Field>
|
||||
<Field label="Email (readOnly)" required readOnly floating size="xl" maxWidth="300px">
|
||||
<Input size="xl" value="[email protected]"/>
|
||||
<Input value="[email protected]"/>
|
||||
</Field>
|
||||
<Field label="Email (invalid)" required floating size="xl" errorText="Something went wrong" invalid maxWidth="300px">
|
||||
<Input size="xl" value="duck"/>
|
||||
<Input value="duck"/>
|
||||
</Field>
|
||||
</Sample>
|
||||
</SamplesStack>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Button, Grid, GridItem, Text } from '@chakra-ui/react';
|
||||
import { Grid, GridItem, Text } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import type { SubmitHandler } from 'react-hook-form';
|
||||
import { FormProvider, useForm } from 'react-hook-form';
|
||||
@@ -10,9 +10,10 @@ import config from 'configs/app';
|
||||
import type { ResourceError } from 'lib/api/resources';
|
||||
import useApiFetch from 'lib/api/useApiFetch';
|
||||
import useApiQuery from 'lib/api/useApiQuery';
|
||||
import useToast from 'lib/hooks/useToast';
|
||||
import useUpdateEffect from 'lib/hooks/useUpdateEffect';
|
||||
import * as mixpanel from 'lib/mixpanel/index';
|
||||
import { Button } from 'toolkit/chakra/button';
|
||||
import { toaster } from 'toolkit/chakra/toaster';
|
||||
import ContentLoader from 'ui/shared/ContentLoader';
|
||||
import DataFetchAlert from 'ui/shared/DataFetchAlert';
|
||||
import FormFieldAddress from 'ui/shared/forms/fields/FormFieldAddress';
|
||||
@@ -42,7 +43,6 @@ const TokenInfoForm = ({ address, tokenName, application, onSubmit }: Props) =>
|
||||
const openEventSent = React.useRef<boolean>(false);
|
||||
|
||||
const apiFetch = useApiFetch();
|
||||
const toast = useToast();
|
||||
|
||||
const configQuery = useApiQuery('token_info_applications_config', {
|
||||
pathParams: { chainId: config.chain.id },
|
||||
@@ -85,16 +85,12 @@ const TokenInfoForm = ({ address, tokenName, application, onSubmit }: Props) =>
|
||||
throw result;
|
||||
}
|
||||
} catch (error) {
|
||||
toast({
|
||||
position: 'top-right',
|
||||
toaster.error({
|
||||
title: 'Error',
|
||||
description: (error as ResourceError<{ message: string }>)?.payload?.message || 'Something went wrong. Try again later.',
|
||||
status: 'error',
|
||||
variant: 'subtle',
|
||||
isClosable: true,
|
||||
});
|
||||
}
|
||||
}, [ apiFetch, application?.id, application?.status, onSubmit, toast ]);
|
||||
}, [ apiFetch, application?.id, application?.status, onSubmit ]);
|
||||
|
||||
useUpdateEffect(() => {
|
||||
if (formState.submitCount > 0 && !formState.isValid) {
|
||||
@@ -119,8 +115,8 @@ const TokenInfoForm = ({ address, tokenName, application, onSubmit }: Props) =>
|
||||
}
|
||||
|
||||
const fieldProps = {
|
||||
size: { base: 'md', lg: 'lg' },
|
||||
isReadOnly: application?.status === 'IN_PROCESS',
|
||||
size: 'xl' as const,
|
||||
readOnly: application?.status === 'IN_PROCESS',
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -129,16 +125,16 @@ const TokenInfoForm = ({ address, tokenName, application, onSubmit }: Props) =>
|
||||
<TokenInfoFormStatusText application={ application }/>
|
||||
<Grid mt={ 8 } gridTemplateColumns={{ base: '1fr', lg: '1fr 1fr' }} columnGap={ 5 } rowGap={ 5 }>
|
||||
|
||||
<FormFieldText<Fields> name="token_name" isRequired placeholder="Token name" { ...fieldProps } isReadOnly/>
|
||||
<FormFieldAddress<Fields> name="address" isRequired placeholder="Token contract address" { ...fieldProps } isReadOnly/>
|
||||
<FormFieldText<Fields> name="requester_name" isRequired placeholder="Requester name" { ...fieldProps }/>
|
||||
<FormFieldEmail<Fields> name="requester_email" isRequired placeholder="Requester email" { ...fieldProps }/>
|
||||
<FormFieldText<Fields> name="token_name" required placeholder="Token name" { ...fieldProps } readOnly/>
|
||||
<FormFieldAddress<Fields> name="address" required placeholder="Token contract address" { ...fieldProps } readOnly/>
|
||||
<FormFieldText<Fields> name="requester_name" required placeholder="Requester name" { ...fieldProps }/>
|
||||
<FormFieldEmail<Fields> name="requester_email" required placeholder="Requester email" { ...fieldProps }/>
|
||||
|
||||
<TokenInfoFormSectionHeader>Project info</TokenInfoFormSectionHeader>
|
||||
<FormFieldText<Fields> name="project_name" placeholder="Project name" { ...fieldProps } rules={ nonWhitespaceFieldRules }/>
|
||||
<TokenInfoFieldProjectSector { ...fieldProps } config={ configQuery.data.projectSectors }/>
|
||||
<FormFieldEmail<Fields> name="project_email" isRequired placeholder="Official project email address" { ...fieldProps }/>
|
||||
<FormFieldUrl<Fields> name="project_website" isRequired placeholder="Official project website" { ...fieldProps }/>
|
||||
<FormFieldEmail<Fields> name="project_email" required placeholder="Official project email address" { ...fieldProps }/>
|
||||
<FormFieldUrl<Fields> name="project_website" required placeholder="Official project website" { ...fieldProps }/>
|
||||
<FormFieldUrl<Fields> name="docs" placeholder="Docs" { ...fieldProps }/>
|
||||
<TokenInfoFieldSupport { ...fieldProps }/>
|
||||
<GridItem colSpan={{ base: 1, lg: 2 }}>
|
||||
@@ -147,14 +143,14 @@ const TokenInfoForm = ({ address, tokenName, application, onSubmit }: Props) =>
|
||||
<GridItem colSpan={{ base: 1, lg: 2 }}>
|
||||
<FormFieldText<Fields>
|
||||
name="project_description"
|
||||
isRequired
|
||||
required
|
||||
placeholder="Project description"
|
||||
maxH="160px"
|
||||
rules={{ maxLength: 300, ...nonWhitespaceFieldRules }}
|
||||
asComponent="Textarea"
|
||||
{ ...fieldProps }
|
||||
/>
|
||||
<Text variant="secondary" fontSize="sm" mt={ 1 }>
|
||||
<Text color="text.secondary" fontSize="sm" mt={ 1 }>
|
||||
Introduce or summarize the project’s operation/goals in a maximum of 300 characters.
|
||||
The description should be written in a neutral point of view and must exclude unsubstantiated claims unless proven otherwise.
|
||||
</Text>
|
||||
@@ -194,9 +190,9 @@ const TokenInfoForm = ({ address, tokenName, application, onSubmit }: Props) =>
|
||||
type="submit"
|
||||
size="lg"
|
||||
mt={ 8 }
|
||||
isLoading={ formState.isSubmitting }
|
||||
loading={ formState.isSubmitting }
|
||||
loadingText="Send request"
|
||||
isDisabled={ application?.status === 'IN_PROCESS' }
|
||||
disabled={ application?.status === 'IN_PROCESS' }
|
||||
>
|
||||
Send request
|
||||
</Button>
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
import { GridItem } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import { Heading } from 'toolkit/chakra/heading';
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const TokenInfoFormSectionHeader = ({ children }: Props) => {
|
||||
return (
|
||||
<GridItem colSpan={{ base: 1, lg: 2 }} fontFamily="heading" fontSize="lg" fontWeight={ 500 } mt={ 3 }>
|
||||
{ children }
|
||||
<GridItem colSpan={{ base: 1, lg: 2 }} mt={ 3 }>
|
||||
<Heading level="2">
|
||||
{ children }
|
||||
</Heading>
|
||||
</GridItem>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Alert } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import type { TokenInfoApplication } from 'types/api/account';
|
||||
|
||||
import { Alert } from 'toolkit/chakra/alert';
|
||||
|
||||
interface Props {
|
||||
application?: TokenInfoApplication;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Center, useColorModeValue } from '@chakra-ui/react';
|
||||
import { Center } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
interface Props {
|
||||
@@ -8,13 +8,13 @@ interface Props {
|
||||
}
|
||||
|
||||
const TokenInfoIconPreview = ({ url, isInvalid, children }: Props) => {
|
||||
const borderColor = useColorModeValue('gray.100', 'gray.700');
|
||||
const borderColorFilled = useColorModeValue('gray.300', 'gray.600');
|
||||
const borderColor = { _light: 'gray.100', _dark: 'gray.700' };
|
||||
const borderColorFilled = { _light: 'gray.300', _dark: 'gray.600' };
|
||||
const borderColorActive = isInvalid ? 'error' : borderColorFilled;
|
||||
|
||||
return (
|
||||
<Center
|
||||
boxSize={{ base: '60px', lg: '80px' }}
|
||||
boxSize="60px"
|
||||
flexShrink={ 0 }
|
||||
borderWidth="2px"
|
||||
borderColor={ url ? borderColorActive : borderColor }
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { FormControlProps } from '@chakra-ui/react';
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import type { Fields } from '../types';
|
||||
|
||||
import { times } from 'lib/html-entities';
|
||||
import type { FieldProps } from 'toolkit/chakra/field';
|
||||
import ImageUrlPreview from 'ui/shared/forms/components/ImageUrlPreview';
|
||||
import FormFieldUrl from 'ui/shared/forms/fields/FormFieldUrl';
|
||||
import useFieldWithImagePreview from 'ui/shared/forms/utils/useFieldWithImagePreview';
|
||||
@@ -13,11 +13,11 @@ import TokenLogoPlaceholder from 'ui/shared/TokenLogoPlaceholder';
|
||||
import TokenInfoIconPreview from '../TokenInfoIconPreview';
|
||||
|
||||
interface Props {
|
||||
isReadOnly?: boolean;
|
||||
size?: FormControlProps['size'];
|
||||
readOnly?: boolean;
|
||||
size?: FieldProps['size'];
|
||||
}
|
||||
|
||||
const TokenInfoFieldIconUrl = ({ isReadOnly, size }: Props) => {
|
||||
const TokenInfoFieldIconUrl = ({ readOnly, size }: Props) => {
|
||||
|
||||
const previewUtils = useFieldWithImagePreview({ name: 'icon_url', isRequired: true });
|
||||
|
||||
@@ -26,15 +26,15 @@ const TokenInfoFieldIconUrl = ({ isReadOnly, size }: Props) => {
|
||||
<FormFieldUrl<Fields>
|
||||
name="icon_url"
|
||||
placeholder={ `Link to icon URL, link to download a SVG or 48${ times }48 PNG icon logo` }
|
||||
isReadOnly={ isReadOnly }
|
||||
readOnly={ readOnly }
|
||||
size={ size }
|
||||
{ ...previewUtils.input }
|
||||
/>
|
||||
<TokenInfoIconPreview url={ previewUtils.preview.src } isInvalid={ previewUtils.preview.isInvalid }>
|
||||
<ImageUrlPreview
|
||||
{ ...previewUtils.preview }
|
||||
fallback={ <TokenLogoPlaceholder boxSize={{ base: 10, lg: 12 }}/> }
|
||||
boxSize={{ base: 10, lg: 12 }}
|
||||
fallback={ <TokenLogoPlaceholder boxSize={ 10 }/> }
|
||||
boxSize={ 10 }
|
||||
borderRadius="base"
|
||||
/>
|
||||
</TokenInfoIconPreview>
|
||||
|
||||
@@ -1,27 +1,29 @@
|
||||
import { createListCollection } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import type { Fields } from '../types';
|
||||
import type { TokenInfoApplicationConfig } from 'types/api/account';
|
||||
|
||||
import FormFieldFancySelect from 'ui/shared/forms/fields/FormFieldFancySelect';
|
||||
import FormFieldSelect from 'ui/shared/forms/fields/FormFieldSelect';
|
||||
|
||||
interface Props {
|
||||
isReadOnly?: boolean;
|
||||
readOnly?: boolean;
|
||||
config: TokenInfoApplicationConfig['projectSectors'];
|
||||
}
|
||||
|
||||
const TokenInfoFieldProjectSector = ({ isReadOnly, config }: Props) => {
|
||||
const TokenInfoFieldProjectSector = ({ readOnly, config }: Props) => {
|
||||
|
||||
const options = React.useMemo(() => {
|
||||
return config.map((option) => ({ label: option, value: option }));
|
||||
const collection = React.useMemo(() => {
|
||||
const items = config.map((option) => ({ label: option, value: option }));
|
||||
return createListCollection({ items });
|
||||
}, [ config ]);
|
||||
|
||||
return (
|
||||
<FormFieldFancySelect<Fields, 'project_sector'>
|
||||
<FormFieldSelect<Fields, 'project_sector'>
|
||||
name="project_sector"
|
||||
placeholder="Project industry"
|
||||
options={ options }
|
||||
isReadOnly={ isReadOnly }
|
||||
collection={ collection }
|
||||
readOnly={ readOnly }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { FormControlProps } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import type { ControllerRenderProps } from 'react-hook-form';
|
||||
|
||||
import type { Fields, SocialLinkFields } from '../types';
|
||||
|
||||
import type { FieldProps } from 'toolkit/chakra/field';
|
||||
import FormFieldUrl from 'ui/shared/forms/fields/FormFieldUrl';
|
||||
import type { IconName } from 'ui/shared/IconSvg';
|
||||
import IconSvg from 'ui/shared/IconSvg';
|
||||
@@ -14,36 +14,38 @@ interface Item {
|
||||
color: string;
|
||||
}
|
||||
const SETTINGS: Record<keyof SocialLinkFields, Item> = {
|
||||
github: { label: 'GitHub', icon: 'social/github_filled', color: 'inherit' },
|
||||
github: { label: 'GitHub', icon: 'social/github_filled', color: 'text.primary' },
|
||||
telegram: { label: 'Telegram', icon: 'social/telegram_filled', color: 'telegram' },
|
||||
linkedin: { label: 'LinkedIn', icon: 'social/linkedin_filled', color: 'linkedin' },
|
||||
discord: { label: 'Discord', icon: 'social/discord_filled', color: 'discord' },
|
||||
slack: { label: 'Slack', icon: 'social/slack_filled', color: 'slack' },
|
||||
twitter: { label: 'X (ex-Twitter)', icon: 'social/twitter_filled', color: 'inherit' },
|
||||
twitter: { label: 'X (ex-Twitter)', icon: 'social/twitter_filled', color: 'text.primary' },
|
||||
opensea: { label: 'OpenSea', icon: 'social/opensea_filled', color: 'opensea' },
|
||||
facebook: { label: 'Facebook', icon: 'social/facebook_filled', color: 'facebook' },
|
||||
medium: { label: 'Medium', icon: 'social/medium_filled', color: 'inherit' },
|
||||
medium: { label: 'Medium', icon: 'social/medium_filled', color: 'text.primary' },
|
||||
reddit: { label: 'Reddit', icon: 'social/reddit_filled', color: 'reddit' },
|
||||
};
|
||||
|
||||
interface Props {
|
||||
isReadOnly?: boolean;
|
||||
size?: FormControlProps['size'];
|
||||
readOnly?: boolean;
|
||||
size?: FieldProps['size'];
|
||||
name: keyof SocialLinkFields;
|
||||
}
|
||||
|
||||
const TokenInfoFieldSocialLink = ({ isReadOnly, size, name }: Props) => {
|
||||
const TokenInfoFieldSocialLink = ({ readOnly, size, name }: Props) => {
|
||||
|
||||
const rightElement = React.useCallback(({ field }: { field: ControllerRenderProps<Fields, keyof SocialLinkFields> }) => {
|
||||
return <IconSvg name={ SETTINGS[name].icon } boxSize={ 6 } color={ field.value ? SETTINGS[name].color : '#718096' }/>;
|
||||
const endElement = React.useCallback(({ field }: { field: ControllerRenderProps<Fields> }) => {
|
||||
return <IconSvg name={ SETTINGS[name].icon } boxSize="60px" px={ 4 } color={ field.value ? SETTINGS[name].color : '#718096' }/>;
|
||||
}, [ name ]);
|
||||
|
||||
return (
|
||||
<FormFieldUrl<Fields, keyof SocialLinkFields>
|
||||
<FormFieldUrl<Fields>
|
||||
name={ name }
|
||||
placeholder={ SETTINGS[name].label }
|
||||
rightElement={ rightElement }
|
||||
isReadOnly={ isReadOnly }
|
||||
group={{
|
||||
endElement,
|
||||
}}
|
||||
readOnly={ readOnly }
|
||||
size={ size }
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import type { InputProps } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import type { Fields } from '../types';
|
||||
|
||||
import type { FieldProps } from 'toolkit/chakra/field';
|
||||
import FormFieldText from 'ui/shared/forms/fields/FormFieldText';
|
||||
import { validator as emailValidator } from 'ui/shared/forms/validators/email';
|
||||
import { urlValidator } from 'ui/shared/forms/validators/url';
|
||||
|
||||
interface Props {
|
||||
isReadOnly?: boolean;
|
||||
size?: InputProps['size'];
|
||||
readOnly?: boolean;
|
||||
size?: FieldProps['size'];
|
||||
}
|
||||
|
||||
const TokenInfoFieldSupport = (props: Props) => {
|
||||
|
||||
@@ -6,7 +6,7 @@ export interface Fields extends SocialLinkFields, TickerUrlFields {
|
||||
requester_name: string;
|
||||
requester_email: string;
|
||||
project_name?: string;
|
||||
project_sector: Option | null;
|
||||
project_sector: Array<Option> | null;
|
||||
project_email: string;
|
||||
project_website: string;
|
||||
project_description: string;
|
||||
|
||||
@@ -12,7 +12,7 @@ export function getFormDefaultValues(address: string, tokenName: string, applica
|
||||
requester_name: application.requesterName,
|
||||
requester_email: application.requesterEmail,
|
||||
project_name: application.projectName,
|
||||
project_sector: application.projectSector ? { value: application.projectSector, label: application.projectSector } : null,
|
||||
project_sector: application.projectSector ? [ { value: application.projectSector, label: application.projectSector } ] : null,
|
||||
project_email: application.projectEmail,
|
||||
project_website: application.projectWebsite,
|
||||
project_description: application.projectDescription || '',
|
||||
@@ -52,7 +52,7 @@ export function prepareRequestBody(data: Fields): Omit<TokenInfoApplication, 'id
|
||||
projectDescription: data.project_description,
|
||||
projectEmail: data.project_email,
|
||||
projectName: data.project_name,
|
||||
projectSector: data.project_sector?.value,
|
||||
projectSector: data.project_sector?.[0]?.value,
|
||||
projectWebsite: data.project_website,
|
||||
reddit: data.reddit,
|
||||
requesterEmail: data.requester_email,
|
||||
|
||||
Reference in New Issue
Block a user