Commit a0c383ef authored by tom's avatar tom

validators page

parent 6290429c
......@@ -362,6 +362,7 @@
"arbitrum",
"arbitrum_sepolia",
"base",
"blackfort_testnet",
"celo_alfajores",
"garnet",
"gnosis",
......
......@@ -8,26 +8,26 @@ import config from 'configs/app';
const validatorsFeature = config.features.validators;
// const Validators = dynamic(() => {
// if (validatorsFeature.isEnabled && validatorsFeature.chainType === 'stability') {
// return import('ui/pages/ValidatorsStability');
// }
const Validators = dynamic(() => {
if (validatorsFeature.isEnabled && validatorsFeature.chainType === 'stability') {
return import('ui/pages/ValidatorsStability');
}
// if (validatorsFeature.isEnabled && validatorsFeature.chainType === 'blackfort') {
// return import('ui/pages/ValidatorsBlackfort');
// }
if (validatorsFeature.isEnabled && validatorsFeature.chainType === 'blackfort') {
return import('ui/pages/ValidatorsBlackfort');
}
// if (validatorsFeature.isEnabled && validatorsFeature.chainType === 'zilliqa') {
// return import('ui/pages/ValidatorsZilliqa');
// }
if (validatorsFeature.isEnabled && validatorsFeature.chainType === 'zilliqa') {
return import('ui/pages/ValidatorsZilliqa');
}
// throw new Error('Validators feature is not enabled.');
// }, { ssr: false });
throw new Error('Validators feature is not enabled.');
}, { ssr: false });
const Page: NextPage = () => {
return (
<PageNextJs pathname="/validators">
{ /* <Validators/> */ }
<Validators/>
</PageNextJs>
);
};
......
......@@ -36,10 +36,11 @@ export interface TableColumnHeaderSortableProps<F extends string> extends TableC
sortValue: string;
onSortToggle: (sortField: F) => void;
disabled?: boolean;
indicatorPosition?: 'left' | 'right';
}
export const TableColumnHeaderSortable = <F extends string>(props: TableColumnHeaderSortableProps<F>) => {
const { sortField, sortValue, onSortToggle, children, disabled, ...rest } = props;
const { sortField, sortValue, onSortToggle, children, disabled, indicatorPosition = 'left', ...rest } = props;
const handleSortToggle = React.useCallback(() => {
onSortToggle(sortField);
......@@ -55,7 +56,8 @@ export const TableColumnHeaderSortable = <F extends string>(props: TableColumnHe
h="100%"
transform={ sortValue.toLowerCase().includes('asc') ? 'rotate(-90deg)' : 'rotate(90deg)' }
position="absolute"
left={ -5 }
left={ indicatorPosition === 'left' ? -5 : undefined }
right={ indicatorPosition === 'right' ? -5 : undefined }
top={ 0 }
/>
) }
......
......@@ -37,7 +37,7 @@ export interface ValidatorsStabilitySorting {
export type ValidatorsStabilitySortingField = ValidatorsStabilitySorting['sort'];
export type ValidatorsStabilitySortingValue = `${ ValidatorsStabilitySortingField }-${ ValidatorsStabilitySorting['order'] }`;
export type ValidatorsStabilitySortingValue = `${ ValidatorsStabilitySortingField }-${ ValidatorsStabilitySorting['order'] }` | 'default';
// Blackfort
......@@ -68,7 +68,7 @@ export interface ValidatorsBlackfortSorting {
export type ValidatorsBlackfortSortingField = ValidatorsBlackfortSorting['sort'];
export type ValidatorsBlackfortSortingValue = `${ ValidatorsBlackfortSortingField }-${ ValidatorsBlackfortSorting['order'] }`;
export type ValidatorsBlackfortSortingValue = `${ ValidatorsBlackfortSortingField }-${ ValidatorsBlackfortSorting['order'] }` | 'default';
// Zilliqa
export interface ValidatorsZilliqaItem {
......
import { Box, Hide, HStack, Show } from '@chakra-ui/react';
import { Box, createListCollection, HStack } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
......@@ -24,11 +24,15 @@ import ValidatorsCounters from 'ui/validators/blackfort/ValidatorsCounters';
import ValidatorsList from 'ui/validators/blackfort/ValidatorsList';
import ValidatorsTable from 'ui/validators/blackfort/ValidatorsTable';
const sortCollection = createListCollection({
items: VALIDATORS_BLACKFORT_SORT_OPTIONS,
});
const ValidatorsBlackfort = () => {
const router = useRouter();
const [ sort, setSort ] =
React.useState<ValidatorsBlackfortSortingValue | undefined>(
getSortValueFromQuery<ValidatorsBlackfortSortingValue>(router.query, VALIDATORS_BLACKFORT_SORT_OPTIONS),
React.useState<ValidatorsBlackfortSortingValue>(
getSortValueFromQuery<ValidatorsBlackfortSortingValue>(router.query, VALIDATORS_BLACKFORT_SORT_OPTIONS) ?? 'default',
);
const { isError, isPlaceholderData, data, pagination, onSortingChange } = useQueryWithPages({
......@@ -44,23 +48,24 @@ const ValidatorsBlackfort = () => {
},
});
const handleSortChange = React.useCallback((value?: ValidatorsBlackfortSortingValue) => {
setSort(value);
onSortingChange(getSortParamsFromValue(value));
const handleSortChange = React.useCallback(({ value }: { value: Array<string> }) => {
const sortValue = value[0] as ValidatorsBlackfortSortingValue;
setSort(sortValue);
onSortingChange(sortValue === 'default' ? undefined : getSortParamsFromValue(sortValue));
}, [ onSortingChange ]);
const sortButton = (
<Sort
name="validators_sorting"
defaultValue={ sort }
options={ VALIDATORS_BLACKFORT_SORT_OPTIONS }
onChange={ handleSortChange }
defaultValue={ [ sort ] }
collection={ sortCollection }
onValueChange={ handleSortChange }
/>
);
const actionBar = (
<>
<HStack spacing={ 3 } mb={ 6 } display={{ base: 'flex', lg: 'none' }}>
<HStack gap={ 3 } mb={ 6 } display={{ base: 'flex', lg: 'none' }}>
{ sortButton }
</HStack>
{ pagination.isVisible && (
......@@ -73,10 +78,10 @@ const ValidatorsBlackfort = () => {
const content = data?.items ? (
<>
<Show below="lg" ssr={ false }>
<Box hideFrom="lg">
<ValidatorsList data={ data.items } isLoading={ isPlaceholderData }/>
</Show>
<Hide below="lg" ssr={ false }>
</Box>
<Box hideBelow="lg">
<ValidatorsTable
data={ data.items }
sort={ sort }
......@@ -84,7 +89,7 @@ const ValidatorsBlackfort = () => {
isLoading={ isPlaceholderData }
top={ pagination.isVisible ? ACTION_BAR_HEIGHT_DESKTOP : 0 }
/>
</Hide>
</Box>
</>
) : null;
......@@ -94,11 +99,12 @@ const ValidatorsBlackfort = () => {
<ValidatorsCounters/>
<DataListDisplay
isError={ isError }
items={ data?.items }
itemsNum={ data?.items.length }
emptyText="There are no validators."
content={ content }
actionBar={ actionBar }
/>
>
{ content }
</DataListDisplay>
</Box>
);
};
......
import { Box, Hide, HStack, Show } from '@chakra-ui/react';
import { Box, createListCollection, HStack } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
......@@ -31,13 +31,17 @@ import ValidatorsFilter from 'ui/validators/stability/ValidatorsFilter';
import ValidatorsList from 'ui/validators/stability/ValidatorsList';
import ValidatorsTable from 'ui/validators/stability/ValidatorsTable';
const sortCollection = createListCollection({
items: VALIDATORS_STABILITY_SORT_OPTIONS,
});
const ValidatorsStability = () => {
const router = useRouter();
// const [ searchTerm, setSearchTerm ] = React.useState(getQueryParamString(router.query.address_hash) || undefined);
const [ statusFilter, setStatusFilter ] =
React.useState(getQueryParamString(router.query.state_filter) as ValidatorsStabilityFilters['state_filter'] || undefined);
const [ sort, setSort ] = React.useState<ValidatorsStabilitySortingValue | undefined>(
getSortValueFromQuery<ValidatorsStabilitySortingValue>(router.query, VALIDATORS_STABILITY_SORT_OPTIONS),
const [ sort, setSort ] = React.useState<ValidatorsStabilitySortingValue>(
getSortValueFromQuery<ValidatorsStabilitySortingValue>(router.query, VALIDATORS_STABILITY_SORT_OPTIONS) ?? 'default',
);
// const debouncedSearchTerm = useDebounce(searchTerm || '', 300);
......@@ -83,9 +87,10 @@ const ValidatorsStability = () => {
setStatusFilter(state);
}, [ onFilterChange ]);
const handleSortChange = React.useCallback((value?: ValidatorsStabilitySortingValue) => {
setSort(value);
onSortingChange(getSortParamsFromValue(value));
const handleSortChange = React.useCallback(({ value }: { value: Array<string> }) => {
const sortValue = value[0] as ValidatorsStabilitySortingValue;
setSort(sortValue);
onSortingChange(sortValue === 'default' ? undefined : getSortParamsFromValue(sortValue));
}, [ onSortingChange ]);
const filterMenu =
......@@ -104,22 +109,22 @@ const ValidatorsStability = () => {
const sortButton = (
<Sort
name="validators_sorting"
defaultValue={ sort }
options={ VALIDATORS_STABILITY_SORT_OPTIONS }
onChange={ handleSortChange }
defaultValue={ [ sort ] }
collection={ sortCollection }
onValueChange={ handleSortChange }
/>
);
const actionBar = (
<>
<HStack spacing={ 3 } mb={ 6 } display={{ base: 'flex', lg: 'none' }}>
<HStack gap={ 3 } mb={ 6 } display={{ base: 'flex', lg: 'none' }}>
{ filterMenu }
{ sortButton }
{ /* { filterInput } */ }
</HStack>
{ (!isMobile || pagination.isVisible) && (
<ActionBar mt={ -6 }>
<HStack spacing={ 3 } display={{ base: 'none', lg: 'flex' }}>
<HStack gap={ 3 } display={{ base: 'none', lg: 'flex' }}>
{ filterMenu }
{ /* { filterInput } */ }
</HStack>
......@@ -131,12 +136,12 @@ const ValidatorsStability = () => {
const content = data?.items ? (
<>
<Show below="lg" ssr={ false }>
<Box hideFrom="lg">
<ValidatorsList data={ data.items } isLoading={ isPlaceholderData }/>
</Show>
<Hide below="lg" ssr={ false }>
</Box>
<Box hideBelow="lg">
<ValidatorsTable data={ data.items } sort={ sort } setSorting={ handleSortChange } isLoading={ isPlaceholderData }/>
</Hide>
</Box>
</>
) : null;
......@@ -146,7 +151,7 @@ const ValidatorsStability = () => {
<ValidatorsCounters/>
<DataListDisplay
isError={ isError }
items={ data?.items }
itemsNum={ data?.items.length }
emptyText="There are no validators."
filterProps={{
emptyFilteredText: `Couldn${ apos }t find any validator that matches your query.`,
......@@ -155,9 +160,10 @@ const ValidatorsStability = () => {
statusFilter,
),
}}
content={ content }
actionBar={ actionBar }
/>
>
{ content }
</DataListDisplay>
</Box>
);
};
......
import { Box, Hide, Show } from '@chakra-ui/react';
import { Box } from '@chakra-ui/react';
import React from 'react';
import config from 'configs/app';
......@@ -37,12 +37,12 @@ const ValidatorsZilliqa = () => {
const content = data?.items ? (
<>
<Show below="lg" ssr={ false }>
<Box hideFrom="lg">
<ValidatorsList data={ data.items } isLoading={ isPlaceholderData }/>
</Show>
<Hide below="lg" ssr={ false }>
</Box>
<Box hideBelow="lg">
<ValidatorsTable data={ data.items } isLoading={ isPlaceholderData }/>
</Hide>
</Box>
</>
) : null;
......@@ -51,11 +51,12 @@ const ValidatorsZilliqa = () => {
<PageTitle title="Validators" withTextAd/>
<DataListDisplay
isError={ isError }
items={ data?.items }
itemsNum={ data?.items.length }
emptyText="There are no validators."
content={ content }
actionBar={ actionBar }
/>
>
{ content }
</DataListDisplay>
</Box>
);
};
......
import { Alert, AlertIcon, AlertTitle } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query';
import React from 'react';
......@@ -12,7 +11,8 @@ import * as cookies from 'lib/cookies';
import { nbsp, ndash } from 'lib/html-entities';
import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage';
import Skeleton from 'ui/shared/chakra/Skeleton';
import { Alert } from 'toolkit/chakra/alert';
import { Skeleton } from 'toolkit/chakra/skeleton';
const IndexingBlocksAlert = () => {
const appProps = useAppContext();
......@@ -64,7 +64,7 @@ const IndexingBlocksAlert = () => {
}
if (isPending) {
return hasAlertCookie ? <Skeleton h={{ base: '96px', lg: '48px' }} w="100%"/> : null;
return hasAlertCookie ? <Skeleton loading h={{ base: '96px', lg: '48px' }} w="100%"/> : null;
}
if (data.finished_indexing_blocks !== false) {
......@@ -73,11 +73,8 @@ const IndexingBlocksAlert = () => {
return (
<Alert status="info" colorScheme="gray" py={ 3 } borderRadius="md">
<AlertIcon display={{ base: 'none', lg: 'flex' }}/>
<AlertTitle>
{ `${ data.indexed_blocks_ratio && `${ Math.floor(Number(data.indexed_blocks_ratio) * 100) }% Blocks Indexed${ nbsp }${ ndash } ` }
{ `${ data.indexed_blocks_ratio && `${ Math.floor(Number(data.indexed_blocks_ratio) * 100) }% Blocks Indexed${ nbsp }${ ndash } ` }
We're indexing this chain right now. Some of the counts may be inaccurate.` }
</AlertTitle>
</Alert>
);
};
......
......@@ -6,7 +6,7 @@ import type { ValidatorBlackfort } from 'types/api/validators';
import config from 'configs/app';
import { currencyUnits } from 'lib/units';
import Skeleton from 'ui/shared/chakra/Skeleton';
import { Skeleton } from 'toolkit/chakra/skeleton';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid';
import TruncatedValue from 'ui/shared/TruncatedValue';
......@@ -30,28 +30,32 @@ const ValidatorsListItem = ({ data, isLoading }: Props) => {
/>
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>Name</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<Flex><TruncatedValue value={ data.name } isLoading={ isLoading }/></Flex>
</ListItemMobileGrid.Value>
{ data.name && (
<>
<ListItemMobileGrid.Label isLoading={ isLoading }>Name</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<Flex><TruncatedValue value={ data.name } isLoading={ isLoading }/></Flex>
</ListItemMobileGrid.Value>
</>
) }
<ListItemMobileGrid.Label isLoading={ isLoading }>Commission</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<Skeleton isLoaded={ !isLoading } display="inline-block">
<Skeleton loading={ isLoading }>
{ `${ data.commission / 100 }%` }
</Skeleton>
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>Self bonded</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<Skeleton isLoaded={ !isLoading } display="inline-block">
<Skeleton loading={ isLoading }>
{ `${ BigNumber(data.self_bonded_amount).div(BigNumber(10 ** config.chain.currency.decimals)).toFormat() } ${ currencyUnits.ether }` }
</Skeleton>
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>Delegated amount</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<Skeleton isLoaded={ !isLoading } display="inline-block">
<Skeleton loading={ isLoading }>
{ `${ BigNumber(data.delegated_amount).div(BigNumber(10 ** config.chain.currency.decimals)).toFormat() } ${ currencyUnits.ether }` }
</Skeleton>
</ListItemMobileGrid.Value>
......
import { Table, Tbody, Tr, Th, Link } from '@chakra-ui/react';
import React from 'react';
import type {
ValidatorBlackfort,
ValidatorsBlackfortSorting,
ValidatorsBlackfortSortingField,
ValidatorsBlackfortSortingValue,
} from 'types/api/validators';
import { currencyUnits } from 'lib/units';
import IconSvg from 'ui/shared/IconSvg';
import { TableBody, TableColumnHeader, TableColumnHeaderSortable, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';
import getNextSortValue from 'ui/shared/sort/getNextSortValue';
import { default as Thead } from 'ui/shared/TheadSticky';
import { VALIDATORS_BLACKFORT_SORT_SEQUENCE } from './utils';
import ValidatorsTableItem from './ValidatorsTableItem';
interface Props {
data: Array<ValidatorBlackfort>;
sort: ValidatorsBlackfortSortingValue | undefined;
setSorting: (val: ValidatorsBlackfortSortingValue | undefined) => void;
sort: ValidatorsBlackfortSortingValue;
setSorting: ({ value }: { value: Array<string> }) => void;
isLoading?: boolean;
top: number;
}
const ValidatorsTable = ({ data, sort, setSorting, isLoading, top }: Props) => {
const sortIconTransform = sort?.includes('asc' as ValidatorsBlackfortSorting['order']) ? 'rotate(-90deg)' : 'rotate(90deg)';
const onSortToggle = React.useCallback((field: ValidatorsBlackfortSortingField) => () => {
const onSortToggle = React.useCallback((field: ValidatorsBlackfortSortingField) => {
const value = getNextSortValue<ValidatorsBlackfortSortingField, ValidatorsBlackfortSortingValue>(VALIDATORS_BLACKFORT_SORT_SEQUENCE, field)(sort);
setSorting(value);
setSorting({ value: [ value ] });
}, [ sort, setSorting ]);
return (
<Table>
<Thead top={ top }>
<Tr>
<Th>
<Link
display="flex"
alignItems="center"
onClick={ isLoading ? undefined : onSortToggle('address_hash') }
columnGap={ 1 }
>
{ sort?.includes('address') && <IconSvg name="arrows/east" boxSize={ 4 } transform={ sortIconTransform }/> }
Validator’s address
</Link>
</Th>
<Th>Name</Th>
<Th isNumeric>Commission</Th>
<Th isNumeric>{ `Self bonded ${ currencyUnits.ether }` }</Th>
<Th isNumeric>{ `Delegated amount ${ currencyUnits.ether }` }</Th>
</Tr>
</Thead>
<Tbody>
<TableRoot>
<TableHeaderSticky top={ top }>
<TableRow>
<TableColumnHeaderSortable
sortField="address_hash"
sortValue={ sort }
onSortToggle={ onSortToggle }
indicatorPosition="right"
>
Validator’s address
</TableColumnHeaderSortable>
<TableColumnHeader>Name</TableColumnHeader>
<TableColumnHeader isNumeric>Commission</TableColumnHeader>
<TableColumnHeader isNumeric>{ `Self bonded ${ currencyUnits.ether }` }</TableColumnHeader>
<TableColumnHeader isNumeric>{ `Delegated amount ${ currencyUnits.ether }` }</TableColumnHeader>
</TableRow>
</TableHeaderSticky>
<TableBody>
{ data.map((item, index) => (
<ValidatorsTableItem
key={ item.address.hash + (isLoading ? index : '') }
data={ item }
isLoading={ isLoading }/>
)) }
</Tbody>
</Table>
</TableBody>
</TableRoot>
);
};
......
import { Tr, Td, Flex } from '@chakra-ui/react';
import { Flex } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import React from 'react';
import type { ValidatorBlackfort } from 'types/api/validators';
import config from 'configs/app';
import Skeleton from 'ui/shared/chakra/Skeleton';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { TableCell, TableRow } from 'toolkit/chakra/table';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TruncatedValue from 'ui/shared/TruncatedValue';
......@@ -16,35 +17,35 @@ interface Props {
const ValidatorsTableItem = ({ data, isLoading }: Props) => {
return (
<Tr>
<Td verticalAlign="middle">
<TableRow>
<TableCell verticalAlign="middle">
<AddressEntity
address={ data.address }
isLoading={ isLoading }
truncation="constant"
/>
</Td>
<Td verticalAlign="middle">
</TableCell>
<TableCell verticalAlign="middle">
<Flex>
<TruncatedValue value={ data.name } isLoading={ isLoading }/>
</Flex>
</Td>
<Td verticalAlign="middle" isNumeric>
<Skeleton isLoaded={ !isLoading } display="inline-block">
</TableCell>
<TableCell verticalAlign="middle" isNumeric>
<Skeleton loading={ isLoading }>
{ `${ data.commission / 100 }%` }
</Skeleton>
</Td>
<Td verticalAlign="middle" isNumeric>
<Skeleton isLoaded={ !isLoading } display="inline-block">
</TableCell>
<TableCell verticalAlign="middle" isNumeric>
<Skeleton loading={ isLoading }>
{ BigNumber(data.self_bonded_amount).div(BigNumber(10 ** config.chain.currency.decimals)).toFormat() }
</Skeleton>
</Td>
<Td verticalAlign="middle" isNumeric>
<Skeleton isLoaded={ !isLoading } display="inline-block">
</TableCell>
<TableCell verticalAlign="middle" isNumeric>
<Skeleton loading={ isLoading }>
{ BigNumber(data.delegated_amount).div(BigNumber(10 ** config.chain.currency.decimals)).toFormat() }
</Skeleton>
</Td>
</Tr>
</TableCell>
</TableRow>
);
};
......
......@@ -2,14 +2,15 @@ import type {
ValidatorsBlackfortSortingValue,
ValidatorsBlackfortSortingField,
} from 'types/api/validators';
import type { SelectOption } from 'toolkit/chakra/select';
export const VALIDATORS_BLACKFORT_SORT_OPTIONS: Array<SelectOption<ValidatorsBlackfortSortingValue>> = [
{ label: 'Default', value: undefined },
{ label: 'Default', value: 'default' },
{ label: 'Address descending', value: 'address_hash-desc' },
{ label: 'Address ascending', value: 'address_hash-asc' },
];
export const VALIDATORS_BLACKFORT_SORT_SEQUENCE: Record<ValidatorsBlackfortSortingField, Array<ValidatorsBlackfortSortingValue | undefined>> = {
address_hash: [ 'address_hash-desc', 'address_hash-asc', undefined ],
export const VALIDATORS_BLACKFORT_SORT_SEQUENCE: Record<ValidatorsBlackfortSortingField, Array<ValidatorsBlackfortSortingValue>> = {
address_hash: [ 'address_hash-desc', 'address_hash-asc', 'default' ],
};
import { createListCollection } from '@chakra-ui/react';
import React from 'react';
import type { ValidatorsStabilityFilters } from 'types/api/validators';
......@@ -11,6 +12,10 @@ const OPTIONS = [
{ value: 'inactive', label: 'Inactive' },
];
const collection = createListCollection({
items: OPTIONS,
});
interface Props {
hasActiveFilter: boolean;
defaultValue: ValidatorsStabilityFilters['state_filter'] | undefined;
......@@ -21,10 +26,10 @@ const ValidatorsFilter = ({ onChange, defaultValue, hasActiveFilter }: Props) =>
return (
<PopoverFilterRadio
name="validators_filter"
options={ OPTIONS }
collection={ collection }
onChange={ onChange }
hasActiveFilter={ hasActiveFilter }
defaultValue={ defaultValue || OPTIONS[0].value }
initialValue={ defaultValue || OPTIONS[0].value }
/>
);
};
......
......@@ -2,7 +2,7 @@ import React from 'react';
import type { ValidatorStability } from 'types/api/validators';
import Skeleton from 'ui/shared/chakra/Skeleton';
import { Skeleton } from 'toolkit/chakra/skeleton';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid';
import ValidatorStatus from 'ui/shared/statusTag/ValidatorStabilityStatus';
......@@ -33,7 +33,7 @@ const ValidatorsListItem = ({ data, isLoading }: Props) => {
<ListItemMobileGrid.Label isLoading={ isLoading }>Blocks</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<Skeleton isLoaded={ !isLoading } display="inline-block">
<Skeleton loading={ isLoading }>
{ data.blocks_validated_count.toLocaleString() }
</Skeleton>
</ListItemMobileGrid.Value>
......
import { Table, Tbody, Tr, Th, Link } from '@chakra-ui/react';
import React from 'react';
import type {
ValidatorStability,
ValidatorsStabilitySorting,
ValidatorsStabilitySortingField,
ValidatorsStabilitySortingValue,
} from 'types/api/validators';
import { TableBody, TableColumnHeader, TableColumnHeaderSortable, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';
import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar';
import IconSvg from 'ui/shared/IconSvg';
import getNextSortValue from 'ui/shared/sort/getNextSortValue';
import { default as Thead } from 'ui/shared/TheadSticky';
import { VALIDATORS_STABILITY_SORT_SEQUENCE } from './utils';
import ValidatorsTableItem from './ValidatorsTableItem';
interface Props {
data: Array<ValidatorStability>;
sort: ValidatorsStabilitySortingValue | undefined;
setSorting: (val: ValidatorsStabilitySortingValue | undefined) => void;
sort: ValidatorsStabilitySortingValue;
setSorting: ({ value }: { value: Array<ValidatorsStabilitySortingValue> }) => void;
isLoading?: boolean;
}
const ValidatorsTable = ({ data, sort, setSorting, isLoading }: Props) => {
const sortIconTransform = sort?.includes('asc' as ValidatorsStabilitySorting['order']) ? 'rotate(-90deg)' : 'rotate(90deg)';
const onSortToggle = React.useCallback((field: ValidatorsStabilitySortingField) => () => {
const onSortToggle = React.useCallback((field: ValidatorsStabilitySortingField) => {
const value = getNextSortValue<ValidatorsStabilitySortingField, ValidatorsStabilitySortingValue>(VALIDATORS_STABILITY_SORT_SEQUENCE, field)(sort);
setSorting(value);
setSorting({ value: [ value ] });
}, [ sort, setSorting ]);
return (
<Table>
<Thead top={ ACTION_BAR_HEIGHT_DESKTOP }>
<Tr>
<Th width="50%">Validator’s address</Th>
<Th width="25%">
<Link
display="flex"
alignItems="center"
onClick={ isLoading ? undefined : onSortToggle('state') }
columnGap={ 1 }
>
{ sort?.includes('state') && <IconSvg name="arrows/east" boxSize={ 4 } transform={ sortIconTransform }/> }
Status
</Link>
</Th>
<Th width="25%" isNumeric>
<Link
display="flex"
alignItems="center"
justifyContent="flex-end"
onClick={ isLoading ? undefined : onSortToggle('blocks_validated') }
columnGap={ 1 }
>
{ sort?.includes('blocks_validated') && <IconSvg name="arrows/east" boxSize={ 4 } transform={ sortIconTransform }/> }
Blocks
</Link>
</Th>
</Tr>
</Thead>
<Tbody>
<TableRoot>
<TableHeaderSticky top={ ACTION_BAR_HEIGHT_DESKTOP }>
<TableRow>
<TableColumnHeader width="50%">Validator’s address</TableColumnHeader>
<TableColumnHeaderSortable
width="25%"
sortField="state"
sortValue={ sort }
onSortToggle={ onSortToggle }
>
Status
</TableColumnHeaderSortable>
<TableColumnHeaderSortable
width="25%"
sortField="blocks_validated"
sortValue={ sort }
onSortToggle={ onSortToggle }
isNumeric
>
Blocks
</TableColumnHeaderSortable>
</TableRow>
</TableHeaderSticky>
<TableBody>
{ data.map((item, index) => (
<ValidatorsTableItem
key={ item.address.hash + (isLoading ? index : '') }
data={ item }
isLoading={ isLoading }/>
)) }
</Tbody>
</Table>
</TableBody>
</TableRoot>
);
};
......
import { Tr, Td } from '@chakra-ui/react';
import React from 'react';
import type { ValidatorStability } from 'types/api/validators';
import Skeleton from 'ui/shared/chakra/Skeleton';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { TableCell, TableRow } from 'toolkit/chakra/table';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import ValidatorStatus from 'ui/shared/statusTag/ValidatorStabilityStatus';
......@@ -14,23 +14,23 @@ interface Props {
const ValidatorsTableItem = ({ data, isLoading }: Props) => {
return (
<Tr>
<Td verticalAlign="middle">
<TableRow>
<TableCell verticalAlign="middle">
<AddressEntity
address={ data.address }
isLoading={ isLoading }
truncation="constant"
/>
</Td>
<Td verticalAlign="middle">
</TableCell>
<TableCell verticalAlign="middle">
<ValidatorStatus state={ data.state } isLoading={ isLoading }/>
</Td>
<Td verticalAlign="middle" isNumeric>
<Skeleton isLoaded={ !isLoading } display="inline-block">
</TableCell>
<TableCell verticalAlign="middle" isNumeric>
<Skeleton loading={ isLoading }>
{ data.blocks_validated_count.toLocaleString() }
</Skeleton>
</Td>
</Tr>
</TableCell>
</TableRow>
);
};
......
......@@ -2,17 +2,18 @@ import type {
ValidatorsStabilitySortingValue,
ValidatorsStabilitySortingField,
} from 'types/api/validators';
import type { SelectOption } from 'toolkit/chakra/select';
export const VALIDATORS_STABILITY_SORT_OPTIONS: Array<SelectOption<ValidatorsStabilitySortingValue>> = [
{ label: 'Default', value: undefined },
{ label: 'Default', value: 'default' },
{ label: 'Status descending', value: 'state-desc' },
{ label: 'Status ascending', value: 'state-asc' },
{ label: 'Blocks validated descending', value: 'blocks_validated-desc' },
{ label: 'Blocks validated ascending', value: 'blocks_validated-asc' },
];
export const VALIDATORS_STABILITY_SORT_SEQUENCE: Record<ValidatorsStabilitySortingField, Array<ValidatorsStabilitySortingValue | undefined>> = {
state: [ 'state-desc', 'state-asc', undefined ],
blocks_validated: [ 'blocks_validated-desc', 'blocks_validated-asc', undefined ],
export const VALIDATORS_STABILITY_SORT_SEQUENCE: Record<ValidatorsStabilitySortingField, Array<ValidatorsStabilitySortingValue>> = {
state: [ 'state-desc', 'state-asc', 'default' ],
blocks_validated: [ 'blocks_validated-desc', 'blocks_validated-asc', 'default' ],
};
......@@ -5,10 +5,10 @@ import React from 'react';
import type { ValidatorZilliqa } from 'types/api/validators';
import config from 'configs/app';
import Skeleton from 'ui/shared/chakra/Skeleton';
import { Skeleton } from 'toolkit/chakra/skeleton';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import DetailsSponsoredItem from 'ui/shared/DetailsSponsoredItem';
import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo';
import DetailedInfoSponsoredItem from 'ui/shared/DetailedInfo/DetailedInfoSponsoredItem';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
......@@ -22,97 +22,97 @@ interface Props {
const ValidatorDetails = ({ data, isLoading }: Props) => {
return (
<Grid columnGap={ 8 } rowGap={ 3 } templateColumns={{ base: 'minmax(0, 1fr)', lg: 'max-content minmax(728px, auto)' }}>
<DetailsInfoItem.Label
<DetailedInfo.ItemLabel
hint="Index of the staker in the committee"
isLoading={ isLoading }
>
Index
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<Skeleton isLoaded={ !isLoading } display="inline">
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
<Skeleton loading={ isLoading } display="inline">
{ data.index }
</Skeleton>
</DetailsInfoItem.Value>
</DetailedInfo.ItemValue>
<DetailsInfoItem.Label
<DetailedInfo.ItemLabel
hint="Staker's balance"
isLoading={ isLoading }
>
Staked
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
<NativeTokenIcon isLoading={ isLoading } boxSize={ 5 } mr={ 2 }/>
<Skeleton isLoaded={ !isLoading } display="inline">
<Skeleton loading={ isLoading } display="inline">
{ BigNumber(data.balance).div(BigNumber(10 ** config.chain.currency.decimals)).toFormat() } { config.chain.currency.symbol }
</Skeleton>
</DetailsInfoItem.Value>
</DetailedInfo.ItemValue>
<DetailsInfoItem.Label
<DetailedInfo.ItemLabel
hint="libp2p peer ID, corresponding to the staker's BLS public key"
isLoading={ isLoading }
>
Peer ID
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
<Flex alignItems="center" w="100%" minWidth={ 0 }>
<Skeleton isLoaded={ !isLoading } maxW="calc(100% - 28px)" overflow="hidden">
<Skeleton loading={ isLoading } maxW="calc(100% - 28px)" overflow="hidden">
<HashStringShortenDynamic hash={ data.peer_id }/>
</Skeleton>
<CopyToClipboard text={ data.peer_id } isLoading={ isLoading }/>
</Flex>
</DetailsInfoItem.Value>
</DetailedInfo.ItemValue>
<DetailsInfoItem.Label
<DetailedInfo.ItemLabel
hint="The address used for authenticating requests from this staker to the deposit contract"
isLoading={ isLoading }
>
Control address
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
<AddressEntity address={ data.control_address } isLoading={ isLoading }/>
</DetailsInfoItem.Value>
</DetailedInfo.ItemValue>
<DetailsInfoItem.Label
<DetailedInfo.ItemLabel
hint="The address which rewards for this staker will be sent to"
isLoading={ isLoading }
>
Reward address
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
<AddressEntity address={ data.reward_address } isLoading={ isLoading }/>
</DetailsInfoItem.Value>
</DetailedInfo.ItemValue>
<DetailsInfoItem.Label
<DetailedInfo.ItemLabel
hint="The address whose key the validator uses to sign cross-chain events"
isLoading={ isLoading }
>
Signing address
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
<AddressEntity address={ data.signing_address } isLoading={ isLoading }/>
</DetailsInfoItem.Value>
</DetailedInfo.ItemValue>
<DetailsInfoItem.Label
<DetailedInfo.ItemLabel
hint="Block number at which the staker was added"
isLoading={ isLoading }
>
Added at block
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
<BlockEntity number={ data.added_at_block_number } isLoading={ isLoading }/>
</DetailsInfoItem.Value>
</DetailedInfo.ItemValue>
<DetailsInfoItem.Label
<DetailedInfo.ItemLabel
hint="Block number at which the staker's stake was last updated"
isLoading={ isLoading }
>
Stake updated
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
<BlockEntity number={ data.stake_updated_at_block_number } isLoading={ isLoading }/>
</DetailsInfoItem.Value>
</DetailedInfo.ItemValue>
<DetailsSponsoredItem isLoading={ isLoading }/>
<DetailedInfoSponsoredItem isLoading={ isLoading }/>
</Grid>
);
};
......
......@@ -4,7 +4,7 @@ import React from 'react';
import type { ValidatorsZilliqaItem } from 'types/api/validators';
import config from 'configs/app';
import Skeleton from 'ui/shared/chakra/Skeleton';
import { Skeleton } from 'toolkit/chakra/skeleton';
import ValidatorEntity from 'ui/shared/entities/validator/ValidatorEntity';
import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid';
......@@ -28,14 +28,14 @@ const ValidatorsListItem = ({ data, isLoading }: Props) => {
<ListItemMobileGrid.Label isLoading={ isLoading }>Index</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<Skeleton isLoaded={ !isLoading } display="inline-block">
<Skeleton loading={ isLoading } display="inline-block">
{ data.index }
</Skeleton>
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>Balance</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<Skeleton isLoaded={ !isLoading } display="inline-block">
<Skeleton loading={ isLoading } display="inline-block">
{ BigNumber(data.balance).div(BigNumber(10 ** config.chain.currency.decimals)).toFormat() } { config.chain.currency.symbol }
</Skeleton>
</ListItemMobileGrid.Value>
......
import { Table, Tbody, Tr, Th } from '@chakra-ui/react';
import React from 'react';
import type { ValidatorsZilliqaItem } from 'types/api/validators';
import config from 'configs/app';
import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';
import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar';
import { default as Thead } from 'ui/shared/TheadSticky';
import ValidatorsTableItem from './ValidatorsTableItem';
......@@ -16,25 +15,25 @@ interface Props {
const ValidatorsTable = ({ data, isLoading }: Props) => {
return (
<Table>
<Thead top={ ACTION_BAR_HEIGHT_DESKTOP }>
<Tr>
<Th width="50%">BLS public key</Th>
<Th width="25%">Index</Th>
<Th width="25%" isNumeric>
<TableRoot>
<TableHeaderSticky top={ ACTION_BAR_HEIGHT_DESKTOP }>
<TableRow>
<TableColumnHeader width="50%">BLS public key</TableColumnHeader>
<TableColumnHeader width="25%">Index</TableColumnHeader>
<TableColumnHeader width="25%" isNumeric>
Staked { config.chain.currency.symbol }
</Th>
</Tr>
</Thead>
<Tbody>
</TableColumnHeader>
</TableRow>
</TableHeaderSticky>
<TableBody>
{ data.map((item, index) => (
<ValidatorsTableItem
key={ item.bls_public_key + (isLoading ? index : '') }
data={ item }
isLoading={ isLoading }/>
)) }
</Tbody>
</Table>
</TableBody>
</TableRoot>
);
};
......
import { Tr, Td } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import React from 'react';
import type { ValidatorsZilliqaItem } from 'types/api/validators';
import config from 'configs/app';
import Skeleton from 'ui/shared/chakra/Skeleton';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { TableCell, TableRow } from 'toolkit/chakra/table';
import ValidatorEntity from 'ui/shared/entities/validator/ValidatorEntity';
interface Props {
......@@ -15,21 +15,21 @@ interface Props {
const ValidatorsTableItem = ({ data, isLoading }: Props) => {
return (
<Tr>
<Td verticalAlign="middle">
<TableRow>
<TableCell verticalAlign="middle">
<ValidatorEntity id={ data.bls_public_key } isLoading={ isLoading } fontWeight="700"/>
</Td>
<Td verticalAlign="middle">
<Skeleton isLoaded={ !isLoading } display="inline-block">
</TableCell>
<TableCell verticalAlign="middle">
<Skeleton loading={ isLoading } display="inline-block">
{ data.index }
</Skeleton>
</Td>
<Td verticalAlign="middle" isNumeric>
<Skeleton isLoaded={ !isLoading } display="inline-block">
</TableCell>
<TableCell verticalAlign="middle" isNumeric>
<Skeleton loading={ isLoading } display="inline-block">
{ BigNumber(data.balance).div(BigNumber(10 ** config.chain.currency.decimals)).toFormat() }
</Skeleton>
</Td>
</Tr>
</TableCell>
</TableRow>
);
};
......
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