Commit bbe33680 authored by tom's avatar tom

display application status in table

parent d787162c
......@@ -8,6 +8,7 @@ import type {
WatchlistAddress,
VerifiedAddressResponse,
TokenInfoApplicationConfig,
TokenInfoApplications,
} from 'types/api/account';
import type {
Address,
......@@ -525,6 +526,7 @@ Q extends 'api_keys' ? ApiKeys :
Q extends 'watchlist' ? Array<WatchlistAddress> :
Q extends 'verified_addresses' ? VerifiedAddressResponse :
Q extends 'token_info_application_config' ? TokenInfoApplicationConfig :
Q extends 'token_info_application' ? TokenInfoApplications :
Q extends 'homepage_stats' ? HomeStats :
Q extends 'homepage_chart_txs' ? ChartTransactionResponse :
Q extends 'homepage_chart_market' ? ChartMarketResponse :
......
......@@ -204,3 +204,7 @@ export interface TokenInfoApplication {
tokenAddress: string;
twitter?: string;
}
export interface TokenInfoApplications {
submissions: Array<TokenInfoApplication>;
}
......@@ -21,7 +21,10 @@ const VerifiedAddresses = () => {
const [ selectedAddress, setSelectedAddress ] = React.useState<string>();
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 },
});
......@@ -32,7 +35,9 @@ const VerifiedAddresses = () => {
const handleItemAdd = React.useCallback((address: string) => {
setSelectedAddress(address);
}, []);
const handleItemEdit = React.useCallback(() => {}, []);
const handleItemEdit = React.useCallback((address: string) => {
setSelectedAddress(address);
}, []);
const addButton = (
<Box marginTop={ 8 }>
......@@ -70,12 +75,15 @@ const VerifiedAddresses = () => {
return (
<Page>
<PageTitle text="Token info application form" backLink={ backLink }/>
<TokenInfoForm address={ selectedAddress }/>
<TokenInfoForm
address={ selectedAddress }
application={ applicationsQuery.data?.submissions.find(({ tokenAddress }) => tokenAddress === selectedAddress) }
/>
</Page>
);
}
const content = data?.verifiedAddresses ? (
const content = addressesQuery.data?.verifiedAddresses ? (
<>
<Show below="lg" key="content-mobile" ssr={ false }>
{ data.verifiedAddresses.map((item) => (
......@@ -88,7 +96,12 @@ const VerifiedAddresses = () => {
)) }
</Show>
<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>
</>
) : null;
......@@ -115,9 +128,9 @@ const VerifiedAddresses = () => {
</chakra.div>
</AccountPageDescription>
<DataListDisplay
isLoading={ isLoading }
isError={ isError }
items={ data?.verifiedAddresses }
isLoading={ addressesQuery.isLoading || applicationsQuery.isLoading }
isError={ addressesQuery.isError || applicationsQuery.isError }
items={ addressesQuery.data?.verifiedAddresses }
content={ content }
emptyText=""
skeletonProps={{ customSkeleton: skeleton }}
......
......@@ -4,6 +4,7 @@ import type { SubmitHandler } from 'react-hook-form';
import { useForm } from 'react-hook-form';
import type { Fields } from './types';
import type { TokenInfoApplication } from 'types/api/account';
import appConfig from 'configs/app/config';
import useApiFetch from 'lib/api/useApiFetch';
......@@ -25,13 +26,14 @@ import TokenInfoFieldRequesterName from './fields/TokenInfoFieldRequesterName';
import TokenInfoFieldSocialLink from './fields/TokenInfoFieldSocialLink';
import TokenInfoFieldSupport from './fields/TokenInfoFieldSupport';
import TokenInfoFormSectionHeader from './TokenInfoFormSectionHeader';
import { prepareRequestBody } from './utils';
import { getFormDefaultValues, prepareRequestBody } from './utils';
interface Props {
address: string;
application?: TokenInfoApplication;
}
const TokenInfoForm = ({ address }: Props) => {
const TokenInfoForm = ({ address, application }: Props) => {
const apiFetch = useApiFetch();
......@@ -41,9 +43,7 @@ const TokenInfoForm = ({ address }: Props) => {
const formApi = useForm<Fields>({
mode: 'onBlur',
defaultValues: {
address,
},
defaultValues: getFormDefaultValues(address, application),
});
const { handleSubmit, formState, control, trigger } = formApi;
......@@ -70,7 +70,7 @@ const TokenInfoForm = ({ address }: Props) => {
return <ContentLoader/>;
}
const fieldProps = { control };
const fieldProps = { control, isReadOnly: application?.status === 'IN_PROCESS' };
return (
<form noValidate onSubmit={ onSubmit } autoComplete="off">
......@@ -122,6 +122,7 @@ const TokenInfoForm = ({ address }: Props) => {
mt={ 8 }
isLoading={ formState.isSubmitting }
loadingText="Send request"
isDisabled={ application?.status === 'IN_PROCESS' }
>
Send request
</Button>
......
import type { Fields } from './types';
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'> {
return {
coinGeckoTicker: data.ticker_coin_gecko,
......
import { Table, Tbody, Th, Thead, Tr } from '@chakra-ui/react';
import React from 'react';
import type { VerifiedAddress } from 'types/api/account';
import type { TokenInfoApplication, VerifiedAddress } from 'types/api/account';
import VerifiedAddressesTableItem from './VerifiedAddressesTableItem';
interface Props {
data: Array<VerifiedAddress>;
applications: Array<TokenInfoApplication> | undefined;
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 (
<Table variant="simple">
<Thead>
<Tr>
<Th>Address</Th>
<Th w="180px">Token info</Th>
<Th w="260px">Request status</Th>
<Th w="160px">Actions</Th>
<Th w="232px">Token info</Th>
<Th w="160px">Request status</Th>
<Th w="150px">Date</Th>
</Tr>
</Thead>
<Tbody>
......@@ -27,6 +28,7 @@ const VerifiedAddressesTable = ({ data, onItemEdit, onItemAdd }: Props) => {
<VerifiedAddressesTableItem
key={ item.contractAddress }
item={ item }
application={ applications?.find(({ tokenAddress }) => tokenAddress === item.contractAddress) }
onAdd={ onItemAdd }
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 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 TokenLogoPlaceholder from 'ui/shared/TokenLogoPlaceholder';
interface Props {
item: VerifiedAddress;
application: TokenInfoApplication | undefined;
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(() => {
onAdd(item.contractAddress);
}, [ 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 (
<Tr>
<Td>
<AddressSnippet address={{ hash: item.contractAddress, is_contract: true, implementation_name: null }}/>
</Td>
<Td>
{ 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>{ status }</Td>
<Td></Td>
</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