Commit bbe33680 authored by tom's avatar tom

display application status in table

parent d787162c
...@@ -8,6 +8,7 @@ import type { ...@@ -8,6 +8,7 @@ import type {
WatchlistAddress, WatchlistAddress,
VerifiedAddressResponse, VerifiedAddressResponse,
TokenInfoApplicationConfig, TokenInfoApplicationConfig,
TokenInfoApplications,
} from 'types/api/account'; } from 'types/api/account';
import type { import type {
Address, Address,
...@@ -525,6 +526,7 @@ Q extends 'api_keys' ? ApiKeys : ...@@ -525,6 +526,7 @@ Q extends 'api_keys' ? ApiKeys :
Q extends 'watchlist' ? Array<WatchlistAddress> : Q extends 'watchlist' ? Array<WatchlistAddress> :
Q extends 'verified_addresses' ? VerifiedAddressResponse : Q extends 'verified_addresses' ? VerifiedAddressResponse :
Q extends 'token_info_application_config' ? TokenInfoApplicationConfig : Q extends 'token_info_application_config' ? TokenInfoApplicationConfig :
Q extends 'token_info_application' ? TokenInfoApplications :
Q extends 'homepage_stats' ? HomeStats : Q extends 'homepage_stats' ? HomeStats :
Q extends 'homepage_chart_txs' ? ChartTransactionResponse : Q extends 'homepage_chart_txs' ? ChartTransactionResponse :
Q extends 'homepage_chart_market' ? ChartMarketResponse : Q extends 'homepage_chart_market' ? ChartMarketResponse :
......
...@@ -204,3 +204,7 @@ export interface TokenInfoApplication { ...@@ -204,3 +204,7 @@ export interface TokenInfoApplication {
tokenAddress: string; tokenAddress: string;
twitter?: string; twitter?: string;
} }
export interface TokenInfoApplications {
submissions: Array<TokenInfoApplication>;
}
...@@ -21,7 +21,10 @@ const VerifiedAddresses = () => { ...@@ -21,7 +21,10 @@ const VerifiedAddresses = () => {
const [ selectedAddress, setSelectedAddress ] = React.useState<string>(); const [ selectedAddress, setSelectedAddress ] = React.useState<string>();
const modalProps = useDisclosure(); const modalProps = useDisclosure();
const { data, isLoading, isError } = useApiQuery('verified_addresses', { const addressesQuery = useApiQuery('verified_addresses', {
pathParams: { chainId: appConfig.network.id },
});
const applicationsQuery = useApiQuery('token_info_application', {
pathParams: { chainId: appConfig.network.id }, pathParams: { chainId: appConfig.network.id },
}); });
...@@ -32,7 +35,9 @@ const VerifiedAddresses = () => { ...@@ -32,7 +35,9 @@ const VerifiedAddresses = () => {
const handleItemAdd = React.useCallback((address: string) => { const handleItemAdd = React.useCallback((address: string) => {
setSelectedAddress(address); setSelectedAddress(address);
}, []); }, []);
const handleItemEdit = React.useCallback(() => {}, []); const handleItemEdit = React.useCallback((address: string) => {
setSelectedAddress(address);
}, []);
const addButton = ( const addButton = (
<Box marginTop={ 8 }> <Box marginTop={ 8 }>
...@@ -70,12 +75,15 @@ const VerifiedAddresses = () => { ...@@ -70,12 +75,15 @@ const VerifiedAddresses = () => {
return ( return (
<Page> <Page>
<PageTitle text="Token info application form" backLink={ backLink }/> <PageTitle text="Token info application form" backLink={ backLink }/>
<TokenInfoForm address={ selectedAddress }/> <TokenInfoForm
address={ selectedAddress }
application={ applicationsQuery.data?.submissions.find(({ tokenAddress }) => tokenAddress === selectedAddress) }
/>
</Page> </Page>
); );
} }
const content = data?.verifiedAddresses ? ( const content = addressesQuery.data?.verifiedAddresses ? (
<> <>
<Show below="lg" key="content-mobile" ssr={ false }> <Show below="lg" key="content-mobile" ssr={ false }>
{ data.verifiedAddresses.map((item) => ( { data.verifiedAddresses.map((item) => (
...@@ -88,7 +96,12 @@ const VerifiedAddresses = () => { ...@@ -88,7 +96,12 @@ const VerifiedAddresses = () => {
)) } )) }
</Show> </Show>
<Hide below="lg" key="content-desktop" ssr={ false }> <Hide below="lg" key="content-desktop" ssr={ false }>
<VerifiedAddressesTable data={ data.verifiedAddresses } onItemEdit={ handleItemEdit } onItemAdd={ handleItemAdd }/> <VerifiedAddressesTable
data={ addressesQuery.data.verifiedAddresses }
applications={ applicationsQuery.data?.submissions }
onItemEdit={ handleItemEdit }
onItemAdd={ handleItemAdd }
/>
</Hide> </Hide>
</> </>
) : null; ) : null;
...@@ -115,9 +128,9 @@ const VerifiedAddresses = () => { ...@@ -115,9 +128,9 @@ const VerifiedAddresses = () => {
</chakra.div> </chakra.div>
</AccountPageDescription> </AccountPageDescription>
<DataListDisplay <DataListDisplay
isLoading={ isLoading } isLoading={ addressesQuery.isLoading || applicationsQuery.isLoading }
isError={ isError } isError={ addressesQuery.isError || applicationsQuery.isError }
items={ data?.verifiedAddresses } items={ addressesQuery.data?.verifiedAddresses }
content={ content } content={ content }
emptyText="" emptyText=""
skeletonProps={{ customSkeleton: skeleton }} skeletonProps={{ customSkeleton: skeleton }}
......
...@@ -4,6 +4,7 @@ import type { SubmitHandler } from 'react-hook-form'; ...@@ -4,6 +4,7 @@ import type { SubmitHandler } from 'react-hook-form';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import type { Fields } from './types'; import type { Fields } from './types';
import type { TokenInfoApplication } from 'types/api/account';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import useApiFetch from 'lib/api/useApiFetch'; import useApiFetch from 'lib/api/useApiFetch';
...@@ -25,13 +26,14 @@ import TokenInfoFieldRequesterName from './fields/TokenInfoFieldRequesterName'; ...@@ -25,13 +26,14 @@ import TokenInfoFieldRequesterName from './fields/TokenInfoFieldRequesterName';
import TokenInfoFieldSocialLink from './fields/TokenInfoFieldSocialLink'; import TokenInfoFieldSocialLink from './fields/TokenInfoFieldSocialLink';
import TokenInfoFieldSupport from './fields/TokenInfoFieldSupport'; import TokenInfoFieldSupport from './fields/TokenInfoFieldSupport';
import TokenInfoFormSectionHeader from './TokenInfoFormSectionHeader'; import TokenInfoFormSectionHeader from './TokenInfoFormSectionHeader';
import { prepareRequestBody } from './utils'; import { getFormDefaultValues, prepareRequestBody } from './utils';
interface Props { interface Props {
address: string; address: string;
application?: TokenInfoApplication;
} }
const TokenInfoForm = ({ address }: Props) => { const TokenInfoForm = ({ address, application }: Props) => {
const apiFetch = useApiFetch(); const apiFetch = useApiFetch();
...@@ -41,9 +43,7 @@ const TokenInfoForm = ({ address }: Props) => { ...@@ -41,9 +43,7 @@ const TokenInfoForm = ({ address }: Props) => {
const formApi = useForm<Fields>({ const formApi = useForm<Fields>({
mode: 'onBlur', mode: 'onBlur',
defaultValues: { defaultValues: getFormDefaultValues(address, application),
address,
},
}); });
const { handleSubmit, formState, control, trigger } = formApi; const { handleSubmit, formState, control, trigger } = formApi;
...@@ -70,7 +70,7 @@ const TokenInfoForm = ({ address }: Props) => { ...@@ -70,7 +70,7 @@ const TokenInfoForm = ({ address }: Props) => {
return <ContentLoader/>; return <ContentLoader/>;
} }
const fieldProps = { control }; const fieldProps = { control, isReadOnly: application?.status === 'IN_PROCESS' };
return ( return (
<form noValidate onSubmit={ onSubmit } autoComplete="off"> <form noValidate onSubmit={ onSubmit } autoComplete="off">
...@@ -122,6 +122,7 @@ const TokenInfoForm = ({ address }: Props) => { ...@@ -122,6 +122,7 @@ const TokenInfoForm = ({ address }: Props) => {
mt={ 8 } mt={ 8 }
isLoading={ formState.isSubmitting } isLoading={ formState.isSubmitting }
loadingText="Send request" loadingText="Send request"
isDisabled={ application?.status === 'IN_PROCESS' }
> >
Send request Send request
</Button> </Button>
......
import type { Fields } from './types'; import type { Fields } from './types';
import type { TokenInfoApplication } from 'types/api/account'; import type { TokenInfoApplication } from 'types/api/account';
export function getFormDefaultValues(address: string, application: TokenInfoApplication | undefined): Partial<Fields> {
if (!application) {
return { address };
}
return {
address,
requester_name: application.requesterName,
requester_email: application.requesterEmail,
project_name: application.projectName,
project_sector: application.projectSector ? { value: application.projectSector, label: application.projectSector } : null,
project_email: application.projectEmail,
project_website: application.projectWebsite,
project_description: application.projectDescription || '',
docs: application.docs || '',
support: application.support || '',
icon_url: application.iconUrl,
ticker_coin_gecko: application.coinGeckoTicker || '',
ticker_coin_market_cap: application.coinMarketCapTicker,
ticker_defi_llama: application.defiLlamaTicker,
github: application.github || '',
telegram: application.telegram || '',
linkedin: application.linkedin || '',
discord: application.discord || '',
slack: application.slack || '',
twitter: application.twitter || '',
opensea: application.openSea || '',
facebook: application.facebook || '',
medium: application.medium || '',
reddit: application.reddit || '',
};
}
export function prepareRequestBody(data: Fields): Omit<TokenInfoApplication, 'id' | 'status'> { export function prepareRequestBody(data: Fields): Omit<TokenInfoApplication, 'id' | 'status'> {
return { return {
coinGeckoTicker: data.ticker_coin_gecko, coinGeckoTicker: data.ticker_coin_gecko,
......
import { Table, Tbody, Th, Thead, Tr } from '@chakra-ui/react'; import { Table, Tbody, Th, Thead, Tr } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { VerifiedAddress } from 'types/api/account'; import type { TokenInfoApplication, VerifiedAddress } from 'types/api/account';
import VerifiedAddressesTableItem from './VerifiedAddressesTableItem'; import VerifiedAddressesTableItem from './VerifiedAddressesTableItem';
interface Props { interface Props {
data: Array<VerifiedAddress>; data: Array<VerifiedAddress>;
applications: Array<TokenInfoApplication> | undefined;
onItemAdd: (address: string) => void; onItemAdd: (address: string) => void;
onItemEdit: (item: VerifiedAddress) => void; onItemEdit: (address: string) => void;
} }
const VerifiedAddressesTable = ({ data, onItemEdit, onItemAdd }: Props) => { const VerifiedAddressesTable = ({ data, applications, onItemEdit, onItemAdd }: Props) => {
return ( return (
<Table variant="simple"> <Table variant="simple">
<Thead> <Thead>
<Tr> <Tr>
<Th>Address</Th> <Th>Address</Th>
<Th w="180px">Token info</Th> <Th w="232px">Token info</Th>
<Th w="260px">Request status</Th> <Th w="160px">Request status</Th>
<Th w="160px">Actions</Th> <Th w="150px">Date</Th>
</Tr> </Tr>
</Thead> </Thead>
<Tbody> <Tbody>
...@@ -27,6 +28,7 @@ const VerifiedAddressesTable = ({ data, onItemEdit, onItemAdd }: Props) => { ...@@ -27,6 +28,7 @@ const VerifiedAddressesTable = ({ data, onItemEdit, onItemAdd }: Props) => {
<VerifiedAddressesTableItem <VerifiedAddressesTableItem
key={ item.contractAddress } key={ item.contractAddress }
item={ item } item={ item }
application={ applications?.find(({ tokenAddress }) => tokenAddress === item.contractAddress) }
onAdd={ onItemAdd } onAdd={ onItemAdd }
onEdit={ onItemEdit } onEdit={ onItemEdit }
/> />
......
import { Td, Tr, Link } from '@chakra-ui/react'; import { Td, Tr, Link, Flex, Image, Tooltip, IconButton, Icon, chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { VerifiedAddress } from 'types/api/account'; import type { TokenInfoApplication, VerifiedAddress } from 'types/api/account';
import editIcon from 'icons/edit.svg';
import AddressLink from 'ui/shared/address/AddressLink';
import AddressSnippet from 'ui/shared/AddressSnippet'; import AddressSnippet from 'ui/shared/AddressSnippet';
import TokenLogoPlaceholder from 'ui/shared/TokenLogoPlaceholder';
interface Props { interface Props {
item: VerifiedAddress; item: VerifiedAddress;
application: TokenInfoApplication | undefined;
onAdd: (address: string) => void; onAdd: (address: string) => void;
onEdit: (item: VerifiedAddress) => void; onEdit: (address: string) => void;
} }
const VerifiedAddressesTableItem = ({ item, onAdd }: Props) => { const VerifiedAddressesTableItem = ({ item, application, onAdd, onEdit }: Props) => {
const handleAddClick = React.useCallback(() => { const handleAddClick = React.useCallback(() => {
onAdd(item.contractAddress); onAdd(item.contractAddress);
}, [ item, onAdd ]); }, [ item, onAdd ]);
const handleEditClick = React.useCallback(() => {
onEdit(item.contractAddress);
}, [ item, onEdit ]);
const status = (() => {
switch (application?.status) {
case 'IN_PROCESS': {
return <chakra.span fontWeight={ 500 }>In progress</chakra.span>;
}
case 'APPROVED': {
return <chakra.span fontWeight={ 500 } color="green.500">Approved</chakra.span>;
}
case 'UPDATE_REQUIRED': {
return <chakra.span fontWeight={ 500 } color="orange.500">Waiting for update</chakra.span>;
}
case 'REJECTED': {
return <chakra.span fontWeight={ 500 } color="red.500">Rejected</chakra.span>;
}
default:
return null;
}
})();
return ( return (
<Tr> <Tr>
<Td> <Td>
<AddressSnippet address={{ hash: item.contractAddress, is_contract: true, implementation_name: null }}/> <AddressSnippet address={{ hash: item.contractAddress, is_contract: true, implementation_name: null }}/>
</Td> </Td>
<Td> <Td>
<Link onClick={ handleAddClick }>Add details</Link> { application ? (
<Flex alignItems="center" columnGap={ 2 } w="100%">
<Image
borderRadius="base"
boxSize={ 6 }
objectFit="cover"
src={ application.iconUrl }
alt="Token logo"
fallback={ <TokenLogoPlaceholder boxSize={ 6 }/> }
/>
<AddressLink
hash={ application.tokenAddress }
alias={ application.projectName }
type="token"
isDisabled={ application.status === 'IN_PROCESS' }
fontWeight={ 500 }
/>
<Tooltip label="Edit">
<IconButton
aria-label="edit"
variant="simple"
boxSize={ 5 }
borderRadius="none"
flexShrink={ 0 }
onClick={ handleEditClick }
icon={ <Icon as={ editIcon }/> }
/>
</Tooltip>
</Flex>
) : (
<Link onClick={ handleAddClick }>Add details</Link>
) }
</Td> </Td>
<Td></Td> <Td>{ status }</Td>
<Td></Td> <Td></Td>
</Tr> </Tr>
); );
......
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