Commit e6b57a22 authored by tom goriunov's avatar tom goriunov Committed by GitHub

Merge pull request #174 from blockscout/tx-mobile-view

tx page: mobile view
parents 0c270cc6 0a3d9a36
......@@ -13,7 +13,7 @@ export const data = [
{
id: 2,
type: 'delegate_call' as TxInternalsType,
status: 'error' as const,
status: 'success' as const,
from: '0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45',
to: '0x12E80C27BfFBB76b4A8d26FF2bfd3C9f310FFA01',
value: 0.5633333,
......@@ -22,7 +22,7 @@ export const data = [
{
id: 3,
type: 'static_call' as TxInternalsType,
status: 'success' as const,
status: 'error' as const,
from: '0x97Aa2EfcF35c0f4c9AaDDCa8c2330fa7A9533830',
to: '0x35317007D203b8a86CA727ad44E473E40450E378',
value: 0.421152366,
......
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.535 11.293a1 1 0 0 0 0 1.414l3.536 3.536a1 1 0 1 1-1.414 1.414l-4.95-4.95a1 1 0 0 1 0-1.414l4.95-4.95a1 1 0 1 1 1.414 1.414l-3.536 3.536Z" fill="currentColor"/>
</svg>
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="m15.026 13.848 2.98 2.978a.834.834 0 1 1-1.18 1.18l-2.978-2.98a7.467 7.467 0 0 1-4.681 1.64c-4.14 0-7.5-3.36-7.5-7.5 0-4.14 3.36-7.5 7.5-7.5 4.14 0 7.5 3.36 7.5 7.5a7.466 7.466 0 0 1-1.641 4.681Zm-1.672-.619A5.814 5.814 0 0 0 15 9.167a5.832 5.832 0 0 0-5.833-5.834 5.831 5.831 0 0 0-5.834 5.834A5.832 5.832 0 0 0 9.167 15a5.814 5.814 0 0 0 4.062-1.646l.125-.125Z" fill="currentColor"/>
</svg>
......@@ -47,7 +47,7 @@ const variantOutline = defineStyle((props) => {
const isGrayTheme = c === 'gray' || c === 'gray-dark';
const color = isGrayTheme ? mode('blackAlpha.800', 'whiteAlpha.800')(props) : mode(`${ c }.600`, `${ c }.300`)(props);
const borderColor = isGrayTheme ? mode('gray.200', 'gray.600')(props) : mode(`${ c }.600`, `${ c }.300`)(props);
const borderColor = isGrayTheme ? mode('gray.300', 'gray.600')(props) : mode(`${ c }.600`, `${ c }.300`)(props);
const activeBg = isGrayTheme ? mode('blue.50', 'gray.600')(props) : mode(`${ c }.50`, 'gray.600')(props);
const activeColor = (() => {
if (c === 'gray') {
......@@ -106,9 +106,8 @@ const variantSimple = defineStyle((props) => {
};
});
const variantSubtle = defineStyle((props) => {
const variantGhost = defineStyle((props) => {
const { colorScheme: c } = props;
const activeBg = mode(`${ c }.50`, 'gray.800')(props);
return {
......@@ -128,10 +127,33 @@ const variantSubtle = defineStyle((props) => {
};
});
const variantSubtle = defineStyle((props) => {
const { colorScheme: c } = props;
if (c === 'gray') {
return {
bg: mode('blackAlpha.200', 'whiteAlpha.200')(props),
color: mode('blackAlpha.800', 'whiteAlpha.800')(props),
_hover: {
color: 'blue.400',
},
};
}
return {
bg: `${ c }.100`,
color: `${ c }.600`,
_hover: {
color: 'blue.400',
},
};
});
const variants = {
solid: variantSolid,
outline: variantOutline,
simple: variantSimple,
ghost: variantGhost,
subtle: variantSubtle,
};
......
......@@ -23,11 +23,8 @@ const sizes = {
minH: 6,
minW: 6,
fontSize: 'sm',
lineHeight: 'sm',
px: 2,
py: '2px',
},
label: {
lineHeight: 5,
},
}),
......
......@@ -88,7 +88,7 @@ const NavigationDesktop = () => {
transform={ isCollapsed ? 'rotate(180deg)' : 'rotate(0)' }
{ ...getDefaultTransitionProps({ transitionProperty: 'transform, left' }) }
transformOrigin="center"
position="fixed"
position="absolute"
top="104px"
left={ isCollapsed ? '80px' : '216px' }
cursor="pointer"
......
......@@ -11,7 +11,7 @@ const ProfileMenuDesktop = () => {
return (
<Popover openDelay={ 300 } placement="bottom-end" gutter={ 10 } isLazy>
<PopoverTrigger>
<Button variant="unstyled" display="inline-flex" height="auto">
<Button variant="unstyled" display="inline-flex" height="auto" flexShrink={ 0 }>
<UserAvatar size={ 50 } data={ data }/>
</Button>
</PopoverTrigger>
......
import { Flex, Link, Icon } from '@chakra-ui/react';
import React from 'react';
import type { RoutedTab } from 'ui/shared/RoutedTabs/types';
import eastArrowIcon from 'icons/arrows/east.svg';
import useLink from 'lib/link/useLink';
import ExternalLink from 'ui/shared/ExternalLink';
import Page from 'ui/shared/Page';
import PageHeader from 'ui/shared/PageHeader';
import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs';
......@@ -24,10 +28,25 @@ export interface Props {
}
const TransactionPageContent = ({ tab }: Props) => {
const link = useLink();
return (
<Page>
{ /* TODO should be shown only when navigating from txs list */ }
<Link mb={ 6 } display="inline-flex" href={ link('txs') }>
<Icon as={ eastArrowIcon } boxSize={ 6 } mr={ 2 } transform="rotate(180deg)"/>
Transactions
</Link>
<PageHeader text="Transaction details"/>
<RoutedTabs tabs={ TABS } defaultActiveTab={ tab }/>
<Flex marginLeft="auto" alignItems="center" flexWrap="wrap" columnGap={ 6 } rowGap={ 3 } mb={ 6 }>
<ExternalLink title="Open in Tenderly" href="#"/>
<ExternalLink title="Open in Blockchair" href="#"/>
<ExternalLink title="Open in Etherscan" href="#"/>
</Flex>
<RoutedTabs
tabs={ TABS }
defaultActiveTab={ tab }
/>
</Page>
);
};
......
import { VStack, useColorModeValue } from '@chakra-ui/react';
import { Flex, useColorModeValue, chakra } from '@chakra-ui/react';
import React from 'react';
interface Props {
children: React.ReactNode;
className?: string;
}
const AccountListItemMobile = ({ children }: Props) => {
const AccountListItemMobile = ({ children, className }: Props) => {
return (
<VStack
gap={ 4 }
<Flex
rowGap={ 6 }
alignItems="flex-start"
flexDirection="column"
paddingY={ 6 }
borderColor={ useColorModeValue('blackAlpha.200', 'whiteAlpha.200') }
borderTopWidth="1px"
_last={{
borderBottomWidth: '1px',
}}
className={ className }
>
{ children }
</VStack>
</Flex>
);
};
export default AccountListItemMobile;
export default chakra(AccountListItemMobile);
......@@ -19,7 +19,7 @@ const AddressSnippet = ({ address, subtitle }: Props) => {
<AddressLink hash={ address } fontWeight="600" ml={ 2 }/>
<CopyToClipboard text={ address } ml={ 1 }/>
</Address>
{ subtitle && <Text fontSize="sm" variant="secondary" mt={ 0.5 } ml={{ base: 0, lg: 8 }}>{ subtitle }</Text> }
{ subtitle && <Text fontSize="sm" variant="secondary" mt={ 0.5 } ml={ 8 }>{ subtitle }</Text> }
</Box>
);
};
......
......@@ -13,7 +13,7 @@ interface Props extends HTMLChakraProps<'div'> {
const DetailsInfoItem = ({ title, hint, children, ...styles }: Props) => {
return (
<>
<GridItem py={ 2 } lineHeight={ 5 } { ...styles } whiteSpace="nowrap">
<GridItem py={{ base: 1, lg: 2 }} lineHeight={ 5 } { ...styles } whiteSpace="nowrap" _notFirst={{ mt: { base: 3, lg: 0 } }}>
<Flex columnGap={ 2 } alignItems="center">
<Tooltip
label={ hint }
......@@ -24,10 +24,20 @@ const DetailsInfoItem = ({ title, hint, children, ...styles }: Props) => {
<Icon as={ infoIcon } boxSize={ 5 }/>
</Box>
</Tooltip>
<Text fontWeight={ 500 }>{ title }</Text>
<Text fontWeight={{ base: 700, lg: 500 }}>{ title }</Text>
</Flex>
</GridItem>
<GridItem display="flex" alignItems="center" py={ 2 } lineHeight={ 5 } whiteSpace="nowrap" { ...styles }>
<GridItem
display="flex"
alignItems="center"
flexWrap="wrap"
rowGap={ 3 }
pl={{ base: 7, lg: 0 }}
py={{ base: 1, lg: 2 }}
lineHeight={ 5 }
whiteSpace="nowrap"
{ ...styles }
>
{ children }
</GridItem>
</>
......
import { Link, Icon } from '@chakra-ui/react';
import React from 'react';
import arrowIcon from 'icons/arrows/north-east.svg';
interface Props {
href: string;
title: string;
}
const ExternalLink = ({ href, title }: Props) => {
return (
<Link fontSize="sm" display="inline-flex" alignItems="center" target="_blank" href={ href }>
{ title }
<Icon as={ arrowIcon } boxSize={ 4 }/>
</Link>
);
};
export default React.memo(ExternalLink);
......@@ -22,11 +22,13 @@ const FilterButton = ({ isActive, appliedFiltersNum, onClick, isCollapsed }: Pro
leftIcon={ isCollapsed ? undefined : FilterIcon }
rightIcon={ appliedFiltersNum ? <Circle bg={ badgeBgColor } size={ 5 } color={ badgeColor }>{ appliedFiltersNum }</Circle> : undefined }
size="sm"
fontWeight="500"
variant="outline"
colorScheme="gray-dark"
onClick={ onClick }
isActive={ isActive }
px={ 1.5 }
flexShrink={ 0 }
>
{ isCollapsed ? FilterIcon : 'Filter' }
</Button>
......
......@@ -36,6 +36,8 @@ const FilterInput = ({ onChange, className, size = 'sm', placeholder }: Props) =
value={ filterQuery }
onChange={ handleFilterQueryChange }
placeholder={ placeholder }
borderWidth="2px"
textOverflow="ellipsis"
/>
</InputGroup>
);
......
......@@ -51,6 +51,8 @@ const Filters = () => {
paddingInlineStart="38px"
placeholder="Search by addresses, hash, method..."
ml="1px"
borderWidth="2px"
textOverflow="ellipsis"
onChange={ handleChange }
borderColor={ inputBorderColor }
value={ value }
......
......@@ -39,7 +39,13 @@ const Page = ({ children }: Props) => {
alignItems="stretch"
>
{ !isMobile && <NavigationDesktop/> }
<VStack width="100%" paddingX={ isMobile ? 4 : 8 } paddingTop={ isMobile ? 0 : 9 } paddingBottom={ 10 } spacing={ 0 }>
<VStack
width="100%"
paddingX={ isMobile ? 4 : 8 }
paddingTop={ isMobile ? 0 : 9 }
paddingBottom={ 10 }
spacing={ 0 }
>
<Header/>
<Box
as="main"
......
import { Box, Icon, IconButton, chakra } from '@chakra-ui/react';
import React from 'react';
import eastArrow from 'icons/arrows/east-mini.svg';
interface Props {
className?: string;
}
const PrevNext = ({ className }: Props) => {
return (
<Box className={ className }>
<IconButton
aria-label="prev"
icon={ <Icon as={ eastArrow } boxSize={ 6 }/> }
h={ 6 }
borderRadius="sm"
variant="subtle"
colorScheme="gray"
/>
<IconButton
aria-label="next"
icon={ <Icon as={ eastArrow }boxSize={ 6 } transform="rotate(180deg)"/> }
h={ 6 }
borderRadius="sm"
variant="subtle"
colorScheme="gray"
ml="10px"
/>
</Box>
);
};
export default chakra(PrevNext);
......@@ -52,7 +52,7 @@ const RoutedTabs = ({ tabs, defaultActiveTab }: Props) => {
return (
<Tabs variant="soft-rounded" colorScheme="blue" isLazy onChange={ handleTabChange } index={ activeTab }>
<TabList
marginBottom={{ base: 6, lg: 8 }}
marginBottom={{ base: 6, lg: 12 }}
flexWrap="nowrap"
whiteSpace="nowrap"
ref={ listRef }
......
......@@ -37,7 +37,7 @@ const RoutedTabsMenu = ({ tabs, tabsCut, isActive, styles, onItemClick, buttonRe
<Popover isLazy placement="bottom-end" key="more" isOpen={ isOpen } onClose={ onClose } onOpen={ onOpen } closeDelay={ 0 }>
<PopoverTrigger>
<Button
variant="subtle"
variant="ghost"
isActive={ isOpen || isActive }
ref={ buttonRef }
{ ...styles }
......@@ -50,7 +50,7 @@ const RoutedTabsMenu = ({ tabs, tabsCut, isActive, styles, onItemClick, buttonRe
{ tabs.slice(tabsCut).map((tab, index) => (
<Button
key={ tab.routeName }
variant="subtle"
variant="ghost"
onClick={ handleItemClick }
isActive={ activeTab.routeName === tab.routeName }
justifyContent="left"
......
import { chakra } from '@chakra-ui/react';
import type { StyleProps } from '@chakra-ui/styled-system';
import React from 'react';
const TextSeparator = (props: StyleProps) => {
return <chakra.span mx={ 3 } { ...props }>|</chakra.span>;
};
export default React.memo(TextSeparator);
......@@ -43,7 +43,7 @@ const Token = ({ symbol, className }: Props) => {
<Link href={ url } target="_blank" ml={ 1 }>
{ token.fullName }
</Link>
<Text ml={ 1 }>({ token.symbol })</Text>
<Text ml={ 1 } variant="secondary">({ token.symbol })</Text>
</Center>
);
};
......
import { Box, Flex, Text, chakra } from '@chakra-ui/react';
import { Box, Flex, Text, chakra, useColorModeValue } from '@chakra-ui/react';
import React from 'react';
interface Props {
......@@ -12,7 +12,7 @@ const Utilization = ({ className, value }: Props) => {
const valueString = (value * 100).toFixed(2) + '%';
return (
<Flex className={ className } alignItems="center">
<Box bg="gray.100" w={ `${ WIDTH }px` } h="4px" borderRadius="full" overflow="hidden">
<Box bg={ useColorModeValue('blackAlpha.200', 'whiteAlpha.200') } w={ `${ WIDTH }px` } h="4px" borderRadius="full" overflow="hidden">
<Box bg="green.500" w={ valueString } h="100%"/>
</Box>
<Text color="green.500" ml="10px" fontWeight="bold">{ valueString }</Text>
......
import { Center, Icon, Text } from '@chakra-ui/react';
import { Flex, Icon, Text } from '@chakra-ui/react';
import React from 'react';
import rightArrowIcon from 'icons/arrows/east.svg';
......@@ -16,16 +16,18 @@ interface Props {
const TokenTransfer = ({ from, to, amount, usd, token }: Props) => {
return (
<Center>
<AddressLink fontWeight="500" hash={ from } truncation="constant"/>
<Icon as={ rightArrowIcon } boxSize={ 6 } mx={ 2 } color="gray.500"/>
<AddressLink fontWeight="500" hash={ to } truncation="constant"/>
<Text fontWeight={ 500 } as="span" ml={ 4 }>For:{ space }
<Flex alignItems="center" flexWrap="wrap" columnGap={ 3 } rowGap={ 3 }>
<Flex alignItems="center">
<AddressLink fontWeight="500" hash={ from } truncation="constant"/>
<Icon as={ rightArrowIcon } boxSize={ 6 } mx={ 2 } color="gray.500"/>
<AddressLink fontWeight="500" hash={ to } truncation="constant"/>
</Flex>
<Text fontWeight={ 500 } as="span">For:{ space }
<Text fontWeight={ 600 } as="span">{ amount }</Text>{ space }
<Text fontWeight={ 400 } variant="secondary" as="span">(${ usd.toFixed(2) })</Text>
</Text>
<Token symbol={ token } ml={ 3 }/>
</Center>
<Token symbol={ token }/>
</Flex>
);
};
......
......@@ -10,12 +10,13 @@ interface RowProps {
isLast?: boolean;
name: string;
type: string;
indexed?: boolean;
}
const PADDING = 4;
const GAP = 5;
const TableRow = ({ isLast, name, type, children }: RowProps) => {
const TableRow = ({ isLast, name, type, children, indexed }: RowProps) => {
const bgColor = useColorModeValue('blackAlpha.50', 'whiteAlpha.50');
return (
......@@ -38,6 +39,14 @@ const TableRow = ({ isLast, name, type, children }: RowProps) => {
>
{ type }
</GridItem>
<GridItem
pr={ GAP }
pt={ GAP }
pb={ isLast ? PADDING : 0 }
bgColor={ bgColor }
>
{ indexed ? 'true' : 'false' }
</GridItem>
<GridItem
pr={ PADDING }
pt={ GAP }
......@@ -55,28 +64,31 @@ const TxDecodedInputData = () => {
const bgColor = useColorModeValue('blackAlpha.50', 'whiteAlpha.50');
return (
<Grid gridTemplateColumns="minmax(80px, auto) minmax(80px, auto) 1fr" fontSize="sm" lineHeight={ 5 } w="100%">
<Grid gridTemplateColumns="minmax(80px, auto) minmax(80px, auto) minmax(80px, auto) minmax(0, 1fr)" fontSize="sm" lineHeight={ 5 } w="100%">
{ /* FIRST PART OF BLOCK */ }
<GridItem fontWeight={ 600 } pl={ PADDING } pr={ GAP }>Method Id</GridItem>
<GridItem colSpan={ 2 } pr={ PADDING }>0xddf252ad</GridItem>
<GridItem fontWeight={ 600 } pl={{ base: 0, lg: PADDING }} pr={{ base: 0, lg: GAP }} colSpan={{ base: 4, lg: undefined }}>Method Id</GridItem>
<GridItem colSpan={{ base: 4, lg: 3 }} pr={{ base: 0, lg: PADDING }} mt={{ base: 2, lg: 0 }}>0xddf252ad</GridItem>
<GridItem
py={ 2 }
mt={ 2 }
pl={ PADDING }
pr={ GAP }
pl={{ base: 0, lg: PADDING }}
pr={{ base: 0, lg: GAP }}
fontWeight={ 600 }
borderTopColor={ useColorModeValue('blackAlpha.200', 'whiteAlpha.200') }
borderTopWidth="1px"
colSpan={{ base: 4, lg: undefined }}
>
Call
</GridItem>
<GridItem
py={ 2 }
mt={ 2 }
colSpan={ 2 }
pr={ PADDING }
py={{ base: 0, lg: 2 }}
mt={{ base: 0, lg: 2 }}
mb={{ base: 2, lg: 0 }}
colSpan={{ base: 4, lg: 3 }}
pr={{ base: 0, lg: PADDING }}
borderTopColor={ useColorModeValue('blackAlpha.200', 'whiteAlpha.200') }
borderTopWidth="1px"
borderTopWidth={{ base: '0px', lg: '1px' }}
whiteSpace="normal"
>
Transfer(address indexed from, address indexed to, uint256 indexed tokenId)
</GridItem>
......@@ -100,6 +112,15 @@ const TxDecodedInputData = () => {
>
Type
</GridItem>
<GridItem
pr={ GAP }
pt={ PADDING }
pb={ 1 }
bgColor={ bgColor }
fontWeight={ 600 }
>
Inde<wbr/>xed?
</GridItem>
<GridItem
pr={ PADDING }
pt={ PADDING }
......@@ -115,7 +136,7 @@ const TxDecodedInputData = () => {
<CopyToClipboard text="0x0000000000000000000000000000000000000000"/>
</Address>
</TableRow>
<TableRow name="from" type="address">
<TableRow name="to" type="address" indexed>
<Address justifyContent="space-between">
<AddressLink hash="0xcf0c50b7ea8af37d57380a0ac199d55b0782c718"/>
<CopyToClipboard text="0xcf0c50b7ea8af37d57380a0ac199d55b0782c718"/>
......
import { Grid, GridItem, Text, Box, Icon, Link, Tag, Flex } from '@chakra-ui/react';
import { Grid, GridItem, Text, Box, Icon, Link, Tag, Flex, Tooltip, chakra } from '@chakra-ui/react';
import React from 'react';
import { scroller, Element } from 'react-scroll';
import { tx } from 'data/tx';
import clockIcon from 'icons/clock.svg';
import flameIcon from 'icons/flame.svg';
import errorIcon from 'icons/status/error.svg';
import successIcon from 'icons/status/success.svg';
import dayjs from 'lib/date/dayjs';
import Address from 'ui/shared/address/Address';
......@@ -12,7 +13,10 @@ import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import PrevNext from 'ui/shared/PrevNext';
import RawInputData from 'ui/shared/RawInputData';
import TextSeparator from 'ui/shared/TextSeparator';
import Token from 'ui/shared/Token';
import Utilization from 'ui/shared/Utilization';
import TokenTransfer from 'ui/tx/TokenTransfer';
......@@ -23,13 +27,6 @@ import TxStatus from 'ui/tx/TxStatus';
const TxDetails = () => {
const [ isExpanded, setIsExpanded ] = React.useState(false);
const leftSeparatorStyles = {
ml: 3,
pl: 3,
borderLeftWidth: '1px',
borderLeftColor: 'gray.700',
};
const handleCutClick = React.useCallback(() => {
setIsExpanded((flag) => !flag);
scroller.scrollTo('TxDetails__cutLink', {
......@@ -39,13 +36,17 @@ const TxDetails = () => {
}, []);
return (
<Grid columnGap={ 8 } rowGap={ 3 } templateColumns="auto 1fr">
<Grid columnGap={ 8 } rowGap={{ base: 3, lg: 3 }} templateColumns={{ base: 'minmax(0, 1fr)', lg: 'auto minmax(0, 1fr)' }}>
<DetailsInfoItem
title="Transaction hash"
hint="Unique character string (TxID) assigned to every verified transaction."
flexWrap="nowrap"
>
{ tx.hash }
<Box overflow="hidden">
<HashStringShortenDynamic hash={ tx.hash }/>
</Box>
<CopyToClipboard text={ tx.hash }/>
<PrevNext ml={ 7 }/>
</DetailsInfoItem>
<DetailsInfoItem
title="Status"
......@@ -58,7 +59,8 @@ const TxDetails = () => {
hint="Block number containing the transaction."
>
<Text>{ tx.block_num }</Text>
<Text { ...leftSeparatorStyles } borderLeftColor="gray.500" variant="secondary">
<TextSeparator color="gray.500"/>
<Text variant="secondary">
{ tx.confirmation_num } Block confirmations
</Text>
</DetailsInfoItem>
......@@ -68,15 +70,16 @@ const TxDetails = () => {
>
<Icon as={ clockIcon } boxSize={ 5 } color="gray.500"/>
<Text ml={ 1 }>{ dayjs(tx.timestamp).fromNow() }</Text>
<Text { ...leftSeparatorStyles }>{ dayjs(tx.timestamp).format('LLLL') }</Text>
<Text { ...leftSeparatorStyles } borderLeftColor="gray.500" variant="secondary">
<TextSeparator/>
<Text whiteSpace="normal">{ dayjs(tx.timestamp).format('LLLL') }<TextSeparator color="gray.500"/></Text>
<Text variant="secondary">
Confirmed within { tx.confirmation_duration } secs
</Text>
</DetailsInfoItem>
<GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 3, lg: 8 }}/>
<DetailsInfoItem
title="From"
hint="Address (external or contract) sending the transaction."
mt={ 8 }
>
<Address>
<AddressIcon hash={ tx.address_from }/>
......@@ -87,28 +90,38 @@ const TxDetails = () => {
<DetailsInfoItem
title="Interacted with contract"
hint="Address (external or contract) receiving the transaction."
flexWrap={{ base: 'wrap', lg: 'nowrap' }}
>
<Address>
<Address mr={ 3 }>
<AddressIcon hash={ tx.address_to }/>
<AddressLink ml={ 2 } hash={ tx.address_to }/>
<CopyToClipboard text={ tx.address_to }/>
</Address>
<Tag colorScheme="orange" variant="solid" ml={ 3 }>SANA</Tag>
<Icon as={ successIcon } boxSize={ 4 } ml={ 2 } color="green.500"/>
<Tag colorScheme="orange" variant="solid" flexShrink={ 0 }>SANA</Tag>
<Tooltip label="Contract execution completed">
<chakra.span display="inline-flex">
<Icon as={ successIcon } boxSize={ 4 } ml={ 2 } color="green.500" cursor="pointer"/>
</chakra.span>
</Tooltip>
<Tooltip label="Error occured during contract execution">
<chakra.span display="inline-flex">
<Icon as={ errorIcon } boxSize={ 4 } ml={ 2 } color="red.500" cursor="pointer"/>
</chakra.span>
</Tooltip>
<Token symbol="USDT" ml={ 3 }/>
</DetailsInfoItem>
<DetailsInfoItem
title="Token transferred"
hint="List of token transferred in the transaction."
>
<Flex flexDirection="column" alignItems="flex-start" rowGap={ 5 }>
<Flex flexDirection="column" alignItems="flex-start" rowGap={ 5 } w="100%">
{ tx.transferred_tokens.map((item) => <TokenTransfer key={ item.token } { ...item }/>) }
</Flex>
</DetailsInfoItem>
<GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 3, lg: 8 }}/>
<DetailsInfoItem
title="Value"
hint="Value sent in the native token (and USD) if applicable."
mt={ 8 }
>
<Text>{ tx.amount.value } Ether</Text>
<Text variant="secondary" ml={ 1 }>(${ tx.amount.value_usd.toFixed(2) })</Text>
......@@ -124,15 +137,16 @@ const TxDetails = () => {
title="Gas price"
hint="Price per unit of gas specified by the sender. Higher gas prices can prioritize transaction inclusion during times of high usage."
>
<Text>{ tx.gas_price.toLocaleString('en', { minimumFractionDigits: 18 }) } Ether</Text>
<Text variant="secondary" ml={ 1 }>({ (tx.gas_price * Math.pow(10, 18)).toFixed(0) } Gwei)</Text>
<Text mr={ 1 }>{ tx.gas_price.toLocaleString('en', { minimumFractionDigits: 18 }) } Ether</Text>
<Text variant="secondary">({ (tx.gas_price * Math.pow(10, 18)).toFixed(0) } Gwei)</Text>
</DetailsInfoItem>
<DetailsInfoItem
title="Gas limit & usage by txn"
hint="Actual gas amount used by the transaction."
>
<Text>{ tx.gas_used.toLocaleString('en') }</Text>
<Text { ...leftSeparatorStyles }>{ tx.gas_limit.toLocaleString('en') }</Text>
<TextSeparator/>
<Text >{ tx.gas_limit.toLocaleString('en') }</Text>
<Utilization ml={ 4 } value={ tx.gas_used / tx.gas_limit }/>
</DetailsInfoItem>
<DetailsInfoItem
......@@ -143,12 +157,14 @@ const TxDetails = () => {
<Box>
<Text as="span" fontWeight="500">Base: </Text>
<Text fontWeight="600" as="span">{ tx.gas_fees.base }</Text>
<TextSeparator/>
</Box>
<Box { ...leftSeparatorStyles }>
<Box>
<Text as="span" fontWeight="500">Max: </Text>
<Text fontWeight="600" as="span">{ tx.gas_fees.max }</Text>
<TextSeparator/>
</Box>
<Box { ...leftSeparatorStyles }>
<Box>
<Text as="span" fontWeight="500">Max priority: </Text>
<Text fontWeight="600" as="span">{ tx.gas_fees.max_priority }</Text>
</Box>
......@@ -158,10 +174,10 @@ const TxDetails = () => {
hint="Amount of ETH burned for this transaction. Equals Block Base Fee per Gas * Gas Used."
>
<Icon as={ flameIcon } boxSize={ 5 } color="gray.500"/>
<Text ml={ 1 }>{ tx.burnt_fees.value.toLocaleString('en', { minimumFractionDigits: 18 }) } Ether</Text>
<Text variant="secondary" ml={ 1 }>(${ tx.burnt_fees.value_usd.toFixed(2) })</Text>
<Text ml={ 1 } mr={ 1 }>{ tx.burnt_fees.value.toLocaleString('en', { minimumFractionDigits: 18 }) } Ether</Text>
<Text variant="secondary">(${ tx.burnt_fees.value_usd.toFixed(2) })</Text>
</DetailsInfoItem>
<GridItem colSpan={ 2 }>
<GridItem colSpan={{ base: undefined, lg: 2 }}>
<Element name="TxDetails__cutLink">
<Link
mt={ 6 }
......@@ -177,8 +193,8 @@ const TxDetails = () => {
</GridItem>
{ isExpanded && (
<>
<GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 1, lg: 4 }}/>
<DetailsInfoItem
mt={ 4 }
title="Other"
hint="Other data related to this transaction."
>
......@@ -186,12 +202,14 @@ const TxDetails = () => {
<Text as="span" fontWeight="500">Txn type: </Text>
<Text fontWeight="600" as="span">{ tx.type.value }</Text>
<Text fontWeight="400" as="span" ml={ 1 }>({ tx.type.eip })</Text>
<TextSeparator/>
</Box>
<Box { ...leftSeparatorStyles }>
<Box>
<Text as="span" fontWeight="500">Nonce: </Text>
<Text fontWeight="600" as="span">{ tx.nonce }</Text>
<TextSeparator/>
</Box>
<Box { ...leftSeparatorStyles }>
<Box>
<Text as="span" fontWeight="500">Position: </Text>
<Text fontWeight="600" as="span">{ tx.position }</Text>
</Box>
......@@ -204,7 +222,7 @@ const TxDetails = () => {
</DetailsInfoItem>
<DetailsInfoItem
title="Decoded input data"
hint="hmmmmmmmmmmm"
hint="Decoded input data"
>
<TxDecodedInputData/>
</DetailsInfoItem>
......
import { Box, Flex, Table, Thead, Tbody, Tr, Th, TableContainer } from '@chakra-ui/react';
import { Box, Flex } from '@chakra-ui/react';
import React from 'react';
import type { TxInternalsType } from 'types/api/tx';
import type ArrayElement from 'types/utils/ArrayElement';
import { data } from 'data/txInternal';
import useIsMobile from 'lib/hooks/useIsMobile';
import { apos } from 'lib/html-entities';
import EmptySearchResult from 'ui/apps/EmptySearchResult';
import FilterInput from 'ui/shared/FilterInput';
import TxInternalsFilter from 'ui/tx/internals/TxInternalsFilter';
import TxInternalsTableItem from 'ui/tx/internals/TxInternalsTableItem';
import TxInternalsList from 'ui/tx/internals/TxInternalsList';
import TxInternalsTable from 'ui/tx/internals/TxInternalsTable';
const searchFn = (searchTerm: string) => (item: ArrayElement<typeof data>): boolean => {
const formattedSearchTerm = searchTerm.toLowerCase();
......@@ -21,6 +23,7 @@ const searchFn = (searchTerm: string) => (item: ArrayElement<typeof data>): bool
const TxInternals = () => {
const [ filters, setFilters ] = React.useState<Array<TxInternalsType>>([]);
const [ searchTerm, setSearchTerm ] = React.useState<string>('');
const isMobile = useIsMobile();
const handleFilterChange = React.useCallback((nextValue: Array<TxInternalsType>) => {
setFilters(nextValue);
......@@ -35,24 +38,7 @@ const TxInternals = () => {
return <EmptySearchResult text={ `Couldn${ apos }t find any transaction that matches your query.` }/>;
}
return (
<TableContainer width="100%">
<Table variant="simple" minWidth="950px" size="sm">
<Thead>
<Tr>
<Th width="20%">Type</Th>
<Th width="calc(20% + 40px)" pr="0">From</Th>
<Th width="calc(20% - 40px)" pl="0">To</Th>
<Th width="20%" isNumeric>Value</Th>
<Th width="20%" isNumeric>Gas limit</Th>
</Tr>
</Thead>
<Tbody>
{ filteredData.map((item) => <TxInternalsTableItem key={ item.id } { ...item }/>) }
</Tbody>
</Table>
</TableContainer>
);
return isMobile ? <TxInternalsList data={ filteredData }/> : <TxInternalsTable data={ filteredData }/>;
})();
return (
......
import {
Accordion,
Text,
Table,
Thead,
Tbody,
Tr,
Th,
TableContainer,
} from '@chakra-ui/react';
import { Accordion, Text } from '@chakra-ui/react';
import React from 'react';
import { data } from 'data/txState';
import TxStateTableItem from './state/TxStateTableItem';
const CURRENCY = 'ETH';
import useIsMobile from 'lib/hooks/useIsMobile';
import TxStateList from 'ui/tx/state/TxStateList';
import TxStateTable from 'ui/tx/state/TxStateTable';
const TxState = () => {
const isMobile = useIsMobile();
const list = isMobile ? <TxStateList/> : <TxStateTable/>;
return (
<>
<Text>
A set of information that represents the current state is updated when a transaction takes place on the network. The below is a summary of those changes
</Text>
<Accordion allowToggle allowMultiple>
<TableContainer width="100%" mt={ 6 }>
<Table variant="simple" minWidth="950px" size="sm">
<Thead>
<Tr>
<Th width="92px">Storage</Th>
<Th width="146px">Address</Th>
<Th width="120px">Miner</Th>
<Th width="33%" isNumeric>{ `After ${ CURRENCY }` }</Th>
<Th width="33%" isNumeric>{ `Before ${ CURRENCY }` }</Th>
<Th width="33%" isNumeric>{ `State difference ${ CURRENCY }` }</Th>
</Tr>
</Thead>
<Tbody>
{ data.map((item, index) => <TxStateTableItem txStateItem={ item } key={ index }/>) }
</Tbody>
</Table>
</TableContainer>
<Accordion allowMultiple defaultIndex={ [] }>
{ list }
</Accordion>
</>
);
......
import { Box } from '@chakra-ui/react';
import React from 'react';
import type { data as txData } from 'data/txInternal';
import TxInternalsListItem from 'ui/tx/internals/TxInternalsListItem';
const TxInternalsList = ({ data }: { data: typeof txData}) => {
return (
<Box mt={ 6 }>
{ data.map((item) => <TxInternalsListItem key={ item.id } { ...item }/>) }
</Box>
);
};
export default TxInternalsList;
import { Flex, Tag, Icon, Box, HStack, Text } from '@chakra-ui/react';
import capitalize from 'lodash/capitalize';
import React from 'react';
import type ArrayElement from 'types/utils/ArrayElement';
import type { data } from 'data/txInternal';
import eastArrowIcon from 'icons/arrows/east.svg';
import AccountListItemMobile from 'ui/shared/AccountListItemMobile';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import TxStatus from 'ui/tx/TxStatus';
type Props = ArrayElement<typeof data>;
const TxInternalsListItem = ({ type, status, from, to, value, gasLimit }: Props) => {
return (
<AccountListItemMobile rowGap={ 3 }>
<Flex>
<Tag colorScheme="cyan" mr={ 2 }>{ capitalize(type) }</Tag>
<TxStatus status={ status }/>
</Flex>
<Box w="100%" display="flex" columnGap={ 3 }>
<Address width="calc((100% - 48px) / 2)">
<AddressIcon hash={ from }/>
<AddressLink ml={ 2 } fontWeight="500" hash={ from }/>
</Address>
<Icon as={ eastArrowIcon } boxSize={ 6 } color="gray.500"/>
<Address width="calc((100% - 48px) / 2)">
<AddressIcon hash={ to }/>
<AddressLink ml={ 2 } fontWeight="500" hash={ to }/>
</Address>
</Box>
<HStack spacing={ 3 }>
<Text fontSize="sm" fontWeight={ 500 }>Value xDAI</Text>
<Text fontSize="sm" variant="secondary">{ value }</Text>
</HStack>
<HStack spacing={ 3 }>
<Text fontSize="sm" fontWeight={ 500 }>Gas limit</Text>
<Text fontSize="sm" variant="secondary">{ gasLimit.toLocaleString('en') }</Text>
</HStack>
</AccountListItemMobile>
);
};
export default TxInternalsListItem;
import { Table, Thead, Tbody, Tr, Th, TableContainer } from '@chakra-ui/react';
import React from 'react';
import type { data as txData } from 'data/txInternal';
import TxInternalsTableItem from 'ui/tx/internals/TxInternalsTableItem';
const TxInternalsTable = ({ data }: { data: typeof txData}) => {
return (
<TableContainer width="100%" mt={ 6 }>
<Table variant="simple" size="sm">
<Thead>
<Tr>
<Th width="28%">Type</Th>
<Th width="20%">From</Th>
<Th width="24px" px={ 0 }/>
<Th width="20%">To</Th>
<Th width="16%" isNumeric>Value</Th>
<Th width="16%" isNumeric>Gas limit</Th>
</Tr>
</Thead>
<Tbody>
{ data.map((item) => (
<TxInternalsTableItem key={ item.id } { ...item }/>
)) }
</Tbody>
</Table>
</TableContainer>
);
};
export default TxInternalsTable;
import { Tr, Td, Tag, Icon } from '@chakra-ui/react';
import { Tr, Td, Tag, Icon, Box } from '@chakra-ui/react';
import React from 'react';
import rightArrowIcon from 'icons/arrows/east.svg';
......@@ -23,17 +23,23 @@ const TxInternalTableItem = ({ type, status, from, to, value, gasLimit }: Props)
return (
<Tr alignItems="top">
<Td>
{ typeTitle && <Tag colorScheme="cyan" mr={ 2 }>{ typeTitle }</Tag> }
{ typeTitle && (
<Box w="126px" display="inline-block">
<Tag colorScheme="cyan" mr={ 5 }>{ typeTitle }</Tag>
</Box>
) }
<TxStatus status={ status }/>
</Td>
<Td pr="0">
<Td>
<Address>
<AddressIcon hash={ from }/>
<AddressLink ml={ 2 } fontWeight="500" hash={ from }/>
<Icon as={ rightArrowIcon } boxSize={ 6 } mx={ 2 } flexShrink={ 0 } color="gray.500"/>
<AddressLink ml={ 2 } fontWeight="500" hash={ from } flexGrow={ 1 }/>
</Address>
</Td>
<Td pl="0">
<Td px={ 0 }>
<Icon as={ rightArrowIcon } boxSize={ 6 } color="gray.500"/>
</Td>
<Td>
<Address>
<AddressIcon hash={ to }/>
<AddressLink ml={ 2 } fontWeight="500" hash={ to }/>
......
import { SearchIcon } from '@chakra-ui/icons';
import { Text, Grid, GridItem, Link, Tooltip, Button, useColorModeValue } from '@chakra-ui/react';
import { Text, Grid, GridItem, Link, Tooltip, Button, Icon, useColorModeValue } from '@chakra-ui/react';
import React from 'react';
import searchIcon from 'icons/search.svg';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
......@@ -15,14 +15,29 @@ interface Props {
index: number;
}
const RowHeader = ({ children }: { children: React.ReactNode }) => <GridItem><Text fontWeight={ 500 }>{ children }</Text></GridItem>;
const RowHeader = ({ children }: { children: React.ReactNode }) => (
<GridItem _notFirst={{ my: { base: 4, lg: 0 } }}>
<Text fontWeight={ 500 }>{ children }</Text>
</GridItem>
);
const TxLogItem = ({ address, index, topics, data }: Props) => {
const borderColor = useColorModeValue('blackAlpha.200', 'whiteAlpha.200');
const dataBgColor = useColorModeValue('blackAlpha.50', 'whiteAlpha.50');
return (
<Grid gridTemplateColumns="200px 1fr" gap={ 8 } py={ 8 } _notFirst={{ borderTopWidth: '1px', borderTopColor: borderColor }}>
<Grid
gridTemplateColumns={{ base: 'minmax(0, 1fr)', lg: '200px minmax(0, 1fr)' }}
gap={{ base: 2, lg: 8 }}
py={ 8 }
_notFirst={{
borderTopWidth: '1px',
borderTopColor: borderColor,
}}
_first={{
pt: 0,
}}
>
<RowHeader>Address</RowHeader>
<GridItem display="flex" alignItems="center">
<Address>
......@@ -30,12 +45,12 @@ const TxLogItem = ({ address, index, topics, data }: Props) => {
<AddressLink hash={ address } ml={ 2 }/>
</Address>
<Tooltip label="Find matches topic">
<Link ml={ 2 }>
<SearchIcon w={ 5 } h={ 5 }/>
<Link ml={ 2 } display="inline-flex">
<Icon as={ searchIcon } boxSize={ 5 }/>
</Link>
</Tooltip>
<Tooltip label="Log index">
<Button variant="outline" isActive ml="auto" size="sm" fontWeight={ 400 }>
<Button variant="outline" colorScheme="gray" isActive ml={{ base: 9, lg: 'auto' }} size="sm" fontWeight={ 400 }>
{ index }
</Button>
</Tooltip>
......
import { Flex, Button, Text, Select } from '@chakra-ui/react';
import { Flex, Button, Select, Box } from '@chakra-ui/react';
import React from 'react';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
interface Props {
hex: string;
index: number;
......@@ -17,18 +19,29 @@ const TxLogTopic = ({ hex, index }: Props) => {
}, []);
return (
<Flex alignItems="center" px={ 3 } _notFirst={{ mt: 3 }}>
<Button variant="outline" isActive size="xs" fontWeight={ 400 } mr={ 3 } w={ 6 }>
<Flex alignItems="center" px={{ base: 0, lg: 3 }} _notFirst={{ mt: 3 }} overflow="hidden" maxW="100%">
<Button variant="outline" colorScheme="gray" isActive size="xs" fontWeight={ 400 } mr={ 3 } w={ 6 }>
{ index }
</Button>
{ /* temporary condition juse to show different states of the component */ }
{ /* delete when ther will be real data */ }
{ index > 0 && (
<Select size="sm" borderRadius="base" value={ selectedDataType } onChange={ handleSelectChange } focusBorderColor="none" w="75px" mr={ 3 }>
<Select
size="sm"
borderRadius="base"
value={ selectedDataType }
onChange={ handleSelectChange }
focusBorderColor="none"
w="75px"
mr={ 3 }
flexShrink={ 0 }
>
{ OPTIONS.map((option) => <option key={ option } value={ option }>{ option }</option>) }
</Select>
) }
<Text>{ hex }</Text>
<Box overflow="hidden" whiteSpace="nowrap">
<HashStringShortenDynamic hash={ hex }/>
</Box>
</Flex>
);
};
......
import { Box } from '@chakra-ui/react';
import React from 'react';
import { data } from 'data/txState';
import TxStateListItem from 'ui/tx/state/TxStateListItem';
const TxStateList = () => {
return (
<Box mt={ 6 }>
{ data.map((item, index) => <TxStateListItem key={ index } { ...item }/>) }
</Box>
);
};
export default TxStateList;
import { AccordionItem, AccordionButton, AccordionIcon, Button, Flex, Text, Link, StatArrow, Stat, AccordionPanel } from '@chakra-ui/react';
import React from 'react';
import type ArrayElement from 'types/utils/ArrayElement';
import type { data } from 'data/txState';
import { nbsp } from 'lib/html-entities';
import AccountListItemMobile from 'ui/shared/AccountListItemMobile';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import TextSeparator from 'ui/shared/TextSeparator';
import TxStateStorageItem from './TxStateStorageItem';
type Props = ArrayElement<typeof data>;
const TxStateListItem = ({ storage, address, miner, after, before, diff }: Props) => {
const hasStorageData = Boolean(storage?.length);
return (
<AccountListItemMobile>
<AccordionItem isDisabled={ !hasStorageData } border={ 0 } w="100%" display="flex" flexDirection="column" rowGap={ 3 }>
{ ({ isExpanded }) => (
<>
<Flex>
<Address flexGrow={ 1 }>
<AddressIcon hash={ address }/>
<AddressLink hash={ address } fontWeight="500" ml={ 2 }/>
</Address>
<AccordionButton
_hover={{ background: 'unset' }}
padding="0"
ml={ 4 }
w="auto"
>
<Button
variant="outline"
borderWidth="1px"
// button can't be inside button (AccordionButton)
as="div"
isActive={ isExpanded }
size="sm"
fontWeight={ 400 }
isDisabled={ !hasStorageData }
colorScheme="gray"
// AccordionButton has its own opacity rule when disabled
_disabled={{ opacity: 1 }}
>
{ storage?.length || '0' }
</Button>
<AccordionIcon color="blue.600" width="30px"/>
</AccordionButton>
</Flex>
<Flex rowGap={ 2 } flexDir="column" fontSize="sm">
<Text fontWeight={ 600 }>Miner</Text>
<Link>{ miner }</Link>
</Flex>
<Flex rowGap={ 2 } flexDir="column" fontSize="sm">
<Text fontWeight={ 600 }>Before</Text>
<Flex>
<Text>{ before.balance } ETH</Text>
<TextSeparator/>
{ typeof before.nonce !== 'undefined' && <Text>Nonce:{ nbsp }{ before.nonce }</Text> }
</Flex>
</Flex>
<Flex rowGap={ 2 } flexDir="column" fontSize="sm">
<Text fontWeight={ 600 }>After</Text>
<Text>{ after.balance } ETH</Text>
{ typeof after.nonce !== 'undefined' && <Text>Nonce:{ nbsp }{ after.nonce }</Text> }
</Flex>
<Flex rowGap={ 2 } flexDir="column" fontSize="sm">
<Text fontWeight={ 600 }>State difference</Text>
<Stat>
{ diff } ETH
<StatArrow ml={ 2 } type={ Number(diff) > 0 ? 'increase' : 'decrease' }/>
</Stat>
</Flex>
{ hasStorageData && (
<AccordionPanel fontWeight={ 500 } p={ 0 }>
{ storage?.map((storageItem, index) => <TxStateStorageItem key={ index } storageItem={ storageItem }/>) }
</AccordionPanel>
) }
</>
) }
</AccordionItem>
</AccountListItemMobile>
);
};
export default TxStateListItem;
......@@ -2,6 +2,7 @@ import {
Grid,
GridItem,
Select,
Box,
useColorModeValue,
} from '@chakra-ui/react';
import React from 'react';
......@@ -20,7 +21,7 @@ const TxStateStorageItem = ({ storageItem }: {storageItem: TTxStateItemStorage})
const OPTIONS = [ 'Hex', 'Number', 'Text', 'Address' ];
return (
<Grid
gridTemplateColumns="auto 1fr"
gridTemplateColumns={{ base: '70px minmax(0, 1fr)', lg: 'auto minmax(0, 1fr)' }}
columnGap={ 3 }
rowGap={ 4 }
px={ 6 }
......@@ -28,11 +29,12 @@ const TxStateStorageItem = ({ storageItem }: {storageItem: TTxStateItemStorage})
background="blackAlpha.50"
borderRadius="12px"
mb={ 4 }
fontSize="sm"
>
{ gridData.map((item) => (
<>
<GridItem alignSelf="center" fontWeight={ 600 } textAlign="end">{ item.name }</GridItem>
<GridItem>
<React.Fragment key={ item.name }>
<GridItem alignSelf={{ base: 'start', lg: 'center' }} fontWeight={{ base: 500, lg: 600 }} textAlign="end">{ item.name }</GridItem>
<GridItem display="flex" flexDir={{ base: 'column', lg: 'row' }} rowGap={ 2 } alignItems={{ base: 'flex-start', lg: 'center' }} >
{ item.select && (
<Select
size="sm"
......@@ -46,9 +48,11 @@ const TxStateStorageItem = ({ storageItem }: {storageItem: TTxStateItemStorage})
{ OPTIONS.map((option) => <option key={ option } value={ option }>{ option }</option>) }
</Select>
) }
{ item.value }
<Box fontWeight={{ base: 400, lg: 500 }} maxW="100%">
{ item.value }
</Box>
</GridItem>
</>
</React.Fragment>
)) }
</Grid>
);
......
import {
Table,
Thead,
Tbody,
Tr,
Th,
TableContainer,
} from '@chakra-ui/react';
import React from 'react';
import { data } from 'data/txState';
import TxStateTableItem from 'ui/tx/state/TxStateTableItem';
const CURRENCY = 'ETH';
const TxStateTable = () => {
return (
<TableContainer width="100%" mt={ 6 }>
<Table variant="simple" minWidth="950px" size="sm">
<Thead>
<Tr>
<Th width="92px">Storage</Th>
<Th width="146px">Address</Th>
<Th width="120px">Miner</Th>
<Th width="33%" isNumeric>{ `After ${ CURRENCY }` }</Th>
<Th width="33%" isNumeric>{ `Before ${ CURRENCY }` }</Th>
<Th width="33%" isNumeric>{ `State difference ${ CURRENCY }` }</Th>
</Tr>
</Thead>
<Tbody>
{ data.map((item, index) => <TxStateTableItem txStateItem={ item } key={ index }/>) }
</Tbody>
</Table>
</TableContainer>
);
};
export default TxStateTable;
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