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

Merge pull request #138 from blockscout/tx-page-setup

transaction page setup
parents d8b26a99 52525c12
......@@ -19,7 +19,7 @@ export default function useNavItems() {
return React.useMemo(() => {
const mainNavItems = [
{ text: 'Blocks', pathname: basePath + '/blocks', icon: blocksIcon },
{ text: 'Transactions', pathname: basePath + '/transactions', icon: transactionsIcon },
{ text: 'Transactions', pathname: basePath + '/tx', icon: transactionsIcon },
{ text: 'Tokens', pathname: basePath + '/tokens', icon: tokensIcon },
{ text: 'Apps', pathname: basePath + '/apps', icon: appsIcon },
{ text: 'Other', pathname: basePath + '/other', icon: gearIcon },
......
import NETWORKS from './availableNetworks';
export default function getNetworkTitle({ network_type: type, network_sub_type: subType }: {network_type: string; network_sub_type: string}) {
export default function getNetworkTitle({ network_type: type, network_sub_type: subType }: {network_type?: string; network_sub_type?: string}) {
const currentNetwork = NETWORKS.find(n => n.type === type && n.subType === subType);
if (currentNetwork) {
return currentNetwork.name + (currentNetwork.shortName ? ` (${ currentNetwork.shortName })` : '') + ' Explorer';
......
import type { GetStaticPaths } from 'next';
import getAvailablePaths from 'lib/networks/getAvailablePaths';
export const getStaticPaths: GetStaticPaths = async() => {
return { paths: getAvailablePaths(), fallback: false };
};
import type { GetStaticProps, GetStaticPropsResult } from 'next';
export const getStaticProps: GetStaticProps = async(context): Promise<GetStaticPropsResult<{ [key: string]: unknown }>> => {
return {
props: {
pageParams: context.params,
},
};
};
import type { NextPage } from 'next';
import Head from 'next/head';
import React from 'react';
import type { PageParams } from './types';
import type { Props as TransactionProps } from 'ui/pages/Transaction';
import Transaction from 'ui/pages/Transaction';
import getSeo from './getSeo';
type Props = {
pageParams: PageParams;
tab: TransactionProps['tab'];
}
const TransactionNextPage: NextPage<Props> = ({ pageParams, tab }: Props) => {
const { title } = getSeo(pageParams);
return (
<>
<Head><title>{ title }</title></Head>
<Transaction tab={ tab }/>
</>
);
};
export default TransactionNextPage;
import type { PageParams } from './types';
import getNetworkTitle from 'lib/networks/getNetworkTitle';
export default function getSeo(params?: PageParams) {
const networkTitle = getNetworkTitle(params || {});
return {
title: params ? `Transaction ${ params.id } - ${ networkTitle }` : '',
description: params ? `View transaction ${ params.id } on ${ networkTitle }` : '',
};
}
import type { GetStaticPaths } from 'next';
export const getStaticPaths: GetStaticPaths = async() => {
return { paths: [], fallback: true };
};
export type PageParams = {
network_type: string;
network_sub_type: string;
id: string;
}
import type { NextPage, GetStaticPaths, GetStaticProps, GetStaticPropsResult } from 'next';
import type { NextPage } from 'next';
import Head from 'next/head';
import React from 'react';
import getAvailablePaths from 'lib/networks/getAvailablePaths';
import getNetworkTitle from 'lib/networks/getNetworkTitle';
import ApiKeys from 'ui/pages/ApiKeys';
......@@ -27,14 +26,5 @@ const ApiKeysPage: NextPage<Props> = ({ pageParams }: Props) => {
export default ApiKeysPage;
export const getStaticPaths: GetStaticPaths = async() => {
return { paths: getAvailablePaths(), fallback: false };
};
export const getStaticProps: GetStaticProps = async(context): Promise<GetStaticPropsResult<Props>> => {
return {
props: {
pageParams: context.params as PageParams,
},
};
};
export { getStaticPaths } from 'lib/next/account/getStaticPaths';
export { getStaticProps } from 'lib/next/getStaticProps';
import type { NextPage, GetStaticPaths, GetStaticProps, GetStaticPropsResult } from 'next';
import type { NextPage } from 'next';
import Head from 'next/head';
import React from 'react';
import getAvailablePaths from 'lib/networks/getAvailablePaths';
import getNetworkTitle from 'lib/networks/getNetworkTitle';
import CustomAbi from 'ui/pages/CustomAbi';
......@@ -27,14 +26,5 @@ const CustomAbiPage: NextPage<Props> = ({ pageParams }: Props) => {
export default CustomAbiPage;
export const getStaticPaths: GetStaticPaths = async() => {
return { paths: getAvailablePaths(), fallback: false };
};
export const getStaticProps: GetStaticProps = async(context): Promise<GetStaticPropsResult<Props>> => {
return {
props: {
pageParams: context.params as PageParams,
},
};
};
export { getStaticPaths } from 'lib/next/account/getStaticPaths';
export { getStaticProps } from 'lib/next/getStaticProps';
import type { NextPage, GetStaticPaths, GetStaticProps, GetStaticPropsResult } from 'next';
import type { NextPage } from 'next';
import Head from 'next/head';
import React from 'react';
import getAvailablePaths from 'lib/networks/getAvailablePaths';
import getNetworkTitle from 'lib/networks/getNetworkTitle';
import PublicTags from 'ui/pages/PublicTags';
......@@ -27,14 +26,5 @@ const PublicTagsPage: NextPage<Props> = ({ pageParams }: Props) => {
export default PublicTagsPage;
export const getStaticPaths: GetStaticPaths = async() => {
return { paths: getAvailablePaths(), fallback: false };
};
export const getStaticProps: GetStaticProps = async(context): Promise<GetStaticPropsResult<Props>> => {
return {
props: {
pageParams: context.params as PageParams,
},
};
};
export { getStaticPaths } from 'lib/next/account/getStaticPaths';
export { getStaticProps } from 'lib/next/getStaticProps';
import type { NextPage, GetStaticPaths, GetStaticProps, GetStaticPropsResult } from 'next';
import type { NextPage } from 'next';
import Head from 'next/head';
import React from 'react';
import getAvailablePaths from 'lib/networks/getAvailablePaths';
import getNetworkTitle from 'lib/networks/getNetworkTitle';
import PrivateTags from 'ui/pages/PrivateTags';
......@@ -27,14 +26,5 @@ const AddressTagsPage: NextPage<Props> = ({ pageParams }: Props) => {
export default AddressTagsPage;
export const getStaticPaths: GetStaticPaths = async() => {
return { paths: getAvailablePaths(), fallback: false };
};
export const getStaticProps: GetStaticProps = async(context): Promise<GetStaticPropsResult<Props>> => {
return {
props: {
pageParams: context.params as PageParams,
},
};
};
export { getStaticPaths } from 'lib/next/account/getStaticPaths';
export { getStaticProps } from 'lib/next/getStaticProps';
import type { NextPage, GetStaticPaths, GetStaticProps, GetStaticPropsResult } from 'next';
import type { NextPage } from 'next';
import Head from 'next/head';
import React from 'react';
import getAvailablePaths from 'lib/networks/getAvailablePaths';
import getNetworkTitle from 'lib/networks/getNetworkTitle';
import PrivateTags from 'ui/pages/PrivateTags';
......@@ -27,14 +26,5 @@ const TransactionTagsPage: NextPage<Props> = ({ pageParams }: Props) => {
export default TransactionTagsPage;
export const getStaticPaths: GetStaticPaths = async() => {
return { paths: getAvailablePaths(), fallback: false };
};
export const getStaticProps: GetStaticProps = async(context): Promise<GetStaticPropsResult<Props>> => {
return {
props: {
pageParams: context.params as PageParams,
},
};
};
export { getStaticPaths } from 'lib/next/account/getStaticPaths';
export { getStaticProps } from 'lib/next/getStaticProps';
import type { NextPage, GetStaticPaths, GetStaticProps, GetStaticPropsResult } from 'next';
import type { NextPage } from 'next';
import Head from 'next/head';
import React from 'react';
import getAvailablePaths from 'lib/networks/getAvailablePaths';
import getNetworkTitle from 'lib/networks/getNetworkTitle';
import WatchList from 'ui/pages/Watchlist';
......@@ -29,14 +28,5 @@ const WatchListPage: NextPage<Props> = ({ pageParams }: Props) => {
export default WatchListPage;
export const getStaticPaths: GetStaticPaths = async() => {
return { paths: getAvailablePaths(), fallback: false };
};
export const getStaticProps: GetStaticProps = async(context): Promise<GetStaticPropsResult<Props>> => {
return {
props: {
pageParams: context.params as PageParams,
},
};
};
export { getStaticPaths } from 'lib/next/account/getStaticPaths';
export { getStaticProps } from 'lib/next/getStaticProps';
import type { NextPage, GetStaticPaths } from 'next';
import type { NextPage } from 'next';
import Head from 'next/head';
import React from 'react';
import getAvailablePaths from 'lib/networks/getAvailablePaths';
import MyProfile from 'ui/pages/MyProfile';
const MyProfilePage: NextPage = () => {
......@@ -16,12 +15,5 @@ const MyProfilePage: NextPage = () => {
export default MyProfilePage;
export const getStaticPaths: GetStaticPaths = async() => {
return { paths: getAvailablePaths(), fallback: false };
};
export const getStaticProps = async() => {
return {
props: {},
};
};
export { getStaticPaths } from 'lib/next/account/getStaticPaths';
export { getStaticProps } from 'lib/next/getStaticProps';
import type { NextPage } from 'next';
import React from 'react';
import type { PageParams } from 'lib/next/tx/types';
import TransactionNextPage from 'lib/next/tx/TransactionNextPage';
type Props = {
pageParams: PageParams;
}
const TransactionPage: NextPage<Props> = ({ pageParams }: Props) => {
return (
<TransactionNextPage tab="details" pageParams={ pageParams }/>
);
};
export default TransactionPage;
export { getStaticPaths } from 'lib/next/tx/getStaticPaths';
export { getStaticProps } from 'lib/next/getStaticProps';
import type { NextPage } from 'next';
import React from 'react';
import type { PageParams } from 'lib/next/tx/types';
import TransactionNextPage from 'lib/next/tx/TransactionNextPage';
type Props = {
pageParams: PageParams;
}
const TransactionPage: NextPage<Props> = ({ pageParams }: Props) => {
return <TransactionNextPage pageParams={ pageParams } tab="internal_txn"/>;
};
export default TransactionPage;
export { getStaticPaths } from 'lib/next/tx/getStaticPaths';
export { getStaticProps } from 'lib/next/getStaticProps';
import type { NextPage } from 'next';
import React from 'react';
import type { PageParams } from 'lib/next/tx/types';
import TransactionNextPage from 'lib/next/tx/TransactionNextPage';
type Props = {
pageParams: PageParams;
}
const TransactionPage: NextPage<Props> = ({ pageParams }: Props) => {
return <TransactionNextPage pageParams={ pageParams } tab="logs"/>;
};
export default TransactionPage;
export { getStaticPaths } from 'lib/next/tx/getStaticPaths';
export { getStaticProps } from 'lib/next/getStaticProps';
import type { NextPage } from 'next';
import React from 'react';
import type { PageParams } from 'lib/next/tx/types';
import TransactionNextPage from 'lib/next/tx/TransactionNextPage';
type Props = {
pageParams: PageParams;
}
const TransactionPage: NextPage<Props> = ({ pageParams }: Props) => {
return <TransactionNextPage pageParams={ pageParams } tab="raw_trace"/>;
};
export default TransactionPage;
export { getStaticPaths } from 'lib/next/tx/getStaticPaths';
export { getStaticProps } from 'lib/next/getStaticProps';
import type { NextPage } from 'next';
import React from 'react';
import type { PageParams } from 'lib/next/tx/types';
import TransactionNextPage from 'lib/next/tx/TransactionNextPage';
type Props = {
pageParams: PageParams;
}
const TransactionPage: NextPage<Props> = ({ pageParams }: Props) => {
return <TransactionNextPage pageParams={ pageParams } tab="state"/>;
};
export default TransactionPage;
export { getStaticPaths } from 'lib/next/tx/getStaticPaths';
export { getStaticProps } from 'lib/next/getStaticProps';
......@@ -66,12 +66,14 @@ const NavigationDesktop = () => {
</Box>
<Box as="nav" mt={ 14 }>
<VStack as="ul" spacing="2" alignItems="flex-start" overflow="hidden">
{ mainNavItems.map((item) => <NavLink key={ item.text } { ...item } isCollapsed={ isCollapsed } isActive={ router.asPath === item.pathname }/>) }
{ mainNavItems.map((item) =>
<NavLink key={ item.text } { ...item } isCollapsed={ isCollapsed } isActive={ router.asPath.startsWith(item.pathname) }/>) }
</VStack>
</Box>
<Box as="nav" mt={ 12 }>
<VStack as="ul" spacing="2" alignItems="flex-start" overflow="hidden">
{ accountNavItems.map((item) => <NavLink key={ item.text } { ...item } isCollapsed={ isCollapsed } isActive={ router.asPath === item.pathname }/>) }
{ accountNavItems.map((item) =>
<NavLink key={ item.text } { ...item } isCollapsed={ isCollapsed } isActive={ router.asPath.startsWith(item.pathname) }/>) }
</VStack>
</Box>
<NavFooter isCollapsed={ isCollapsed }/>
......
......@@ -14,12 +14,12 @@ const NavigationMobile = () => {
<>
<Box as="nav" mt={ 6 }>
<VStack as="ul" spacing="2" alignItems="flex-start" overflow="hidden">
{ mainNavItems.map((item) => <NavLink key={ item.text } { ...item } isActive={ router.asPath === item.pathname }/>) }
{ mainNavItems.map((item) => <NavLink key={ item.text } { ...item } isActive={ router.asPath.startsWith(item.pathname) }/>) }
</VStack>
</Box>
<Box as="nav" mt={ 6 }>
<VStack as="ul" spacing="2" alignItems="flex-start" overflow="hidden">
{ accountNavItems.map((item) => <NavLink key={ item.text } { ...item } isActive={ router.asPath === item.pathname }/>) }
{ accountNavItems.map((item) => <NavLink key={ item.text } { ...item } isActive={ router.asPath.startsWith(item.pathname) }/>) }
</VStack>
</Box>
<NavFooter/>
......
......@@ -12,8 +12,8 @@ import ApiKeyListItem from 'ui/apiKey/ApiKeyTable/ApiKeyListItem';
import ApiKeyTable from 'ui/apiKey/ApiKeyTable/ApiKeyTable';
import DeleteApiKeyModal from 'ui/apiKey/DeleteApiKeyModal';
import AccountPageDescription from 'ui/shared/AccountPageDescription';
import AccountPageHeader from 'ui/shared/AccountPageHeader';
import Page from 'ui/shared/Page';
import PageHeader from 'ui/shared/PageHeader';
import SkeletonAccountMobile from 'ui/shared/SkeletonAccountMobile';
import SkeletonTable from 'ui/shared/SkeletonTable';
......@@ -127,7 +127,7 @@ const ApiKeysPage: React.FC = () => {
return (
<Page>
<Box h="100%">
<AccountPageHeader text="API keys"/>
<PageHeader text="API keys"/>
{ content }
</Box>
</Page>
......
......@@ -11,8 +11,8 @@ import CustomAbiListItem from 'ui/customAbi/CustomAbiTable/CustomAbiListItem';
import CustomAbiTable from 'ui/customAbi/CustomAbiTable/CustomAbiTable';
import DeleteCustomAbiModal from 'ui/customAbi/DeleteCustomAbiModal';
import AccountPageDescription from 'ui/shared/AccountPageDescription';
import AccountPageHeader from 'ui/shared/AccountPageHeader';
import Page from 'ui/shared/Page';
import PageHeader from 'ui/shared/PageHeader';
import SkeletonAccountMobile from 'ui/shared/SkeletonAccountMobile';
import SkeletonTable from 'ui/shared/SkeletonTable';
......@@ -115,7 +115,7 @@ const CustomAbiPage: React.FC = () => {
return (
<Page>
<Box h="100%">
<AccountPageHeader text="Custom ABI"/>
<PageHeader text="Custom ABI"/>
{ content }
</Box>
</Page>
......
......@@ -2,10 +2,10 @@ import { VStack, FormControl, FormLabel, Input } from '@chakra-ui/react';
import React from 'react';
import useFetchProfileInfo from 'lib/hooks/useFetchProfileInfo';
import AccountPageHeader from 'ui/shared/AccountPageHeader';
import ContentLoader from 'ui/shared/ContentLoader';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
import Page from 'ui/shared/Page';
import PageHeader from 'ui/shared/PageHeader';
import UserAvatar from 'ui/shared/UserAvatar';
const MyProfile = () => {
......@@ -56,7 +56,7 @@ const MyProfile = () => {
return (
<Page>
<AccountPageHeader text="My profile"/>
<PageHeader text="My profile"/>
{ content }
</Page>
);
......
......@@ -11,8 +11,8 @@ import React, { useCallback, useState } from 'react';
import useBasePath from 'lib/hooks/useBasePath';
import PrivateAddressTags from 'ui/privateTags/PrivateAddressTags';
import PrivateTransactionTags from 'ui/privateTags/PrivateTransactionTags';
import AccountPageHeader from 'ui/shared/AccountPageHeader';
import Page from 'ui/shared/Page';
import PageHeader from 'ui/shared/PageHeader';
const TABS = [ 'address', 'transaction' ] as const;
......@@ -36,7 +36,7 @@ const PrivateTags = ({ tab }: Props) => {
return (
<Page>
<Box h="100%">
<AccountPageHeader text="Private tags"/>
<PageHeader text="Private tags"/>
<Tabs variant="soft-rounded" colorScheme="blue" isLazy onChange={ onChangeTab } defaultIndex={ TABS.indexOf(tab) }>
<TabList marginBottom={{ base: 6, lg: 8 }}>
<Tab>Address</Tab>
......
......@@ -9,8 +9,8 @@ import useIsMobile from 'lib/hooks/useIsMobile';
import useToast from 'lib/hooks/useToast';
import PublicTagsData from 'ui/publicTags/PublicTagsData';
import PublicTagsForm from 'ui/publicTags/PublicTagsForm/PublicTagsForm';
import AccountPageHeader from 'ui/shared/AccountPageHeader';
import Page from 'ui/shared/Page';
import PageHeader from 'ui/shared/PageHeader';
type TScreen = 'data' | 'form';
......@@ -84,7 +84,7 @@ const PublicTagsComponent: React.FC = () => {
<Text variant="inherit" fontSize="sm" ml={ 2 }>Public tags</Text>
</Link>
) }
<AccountPageHeader text={ header }/>
<PageHeader text={ header }/>
{ content }
</Box>
</Page>
......
import {
Tab,
Tabs,
TabList,
TabPanel,
TabPanels,
} from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
import useBasePath from 'lib/hooks/useBasePath';
import Page from 'ui/shared/Page';
import PageHeader from 'ui/shared/PageHeader';
import TxDetails from 'ui/tx/TxDetails';
interface Tab {
type: 'details' | 'internal_txn' | 'logs' | 'raw_trace' | 'state';
path: string;
name: string;
component?: React.ReactNode;
}
const TABS: Array<Tab> = [
{ type: 'details', path: '', name: 'Details', component: <TxDetails/> },
{ type: 'internal_txn', path: '/internal-transactions', name: 'Internal txn' },
{ type: 'logs', path: '/logs', name: 'Logs' },
{ type: 'state', path: '/state', name: 'State' },
{ type: 'raw_trace', path: '/raw-trace', name: 'Raw trace' },
];
export interface Props {
tab: Tab['type'];
}
const TransactionPageContent = ({ tab }: Props) => {
const [ , setActiveTab ] = React.useState<Tab['type']>(tab);
const router = useRouter();
const basePath = useBasePath();
const handleTabChange = React.useCallback((index: number) => {
const nextTab = TABS[index];
setActiveTab(nextTab.type);
const newUrl = basePath + '/tx/' + router.query.id + nextTab.path;
window.history.replaceState(history.state, '', newUrl);
}, [ setActiveTab, basePath, router ]);
const defaultIndex = TABS.map(({ type }) => type).indexOf(tab);
return (
<Page>
<PageHeader text="Transaction details"/>
<Tabs variant="soft-rounded" colorScheme="blue" isLazy onChange={ handleTabChange } defaultIndex={ defaultIndex }>
<TabList marginBottom={{ base: 6, lg: 8 }} flexWrap="wrap">
{ TABS.map((tab) => <Tab key={ tab.type }>{ tab.name }</Tab>) }
</TabList>
<TabPanels>
{ TABS.map((tab) => <TabPanel padding={ 0 } key={ tab.type }>{ tab.component || tab.name }</TabPanel>) }
</TabPanels>
</Tabs>
</Page>
);
};
export default TransactionPageContent;
......@@ -7,9 +7,9 @@ import type { TWatchlist, TWatchlistItem } from 'types/client/account';
import fetch from 'lib/client/fetch';
import useIsMobile from 'lib/hooks/useIsMobile';
import AccountPageDescription from 'ui/shared/AccountPageDescription';
import AccountPageHeader from 'ui/shared/AccountPageHeader';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
import Page from 'ui/shared/Page';
import PageHeader from 'ui/shared/PageHeader';
import SkeletonAccountMobile from 'ui/shared/SkeletonAccountMobile';
import SkeletonTable from 'ui/shared/SkeletonTable';
import AddressModal from 'ui/watchlist/AddressModal/AddressModal';
......@@ -112,7 +112,7 @@ const WatchList: React.FC = () => {
return (
<Page>
<Box h="100%">
<AccountPageHeader text="Watch list"/>
<PageHeader text="Watch list"/>
{ content }
</Box>
</Page>
......
import { Heading } from '@chakra-ui/react';
import React from 'react';
const AccountPageHeader = ({ text }: {text: string}) => {
const PageHeader = ({ text }: {text: string}) => {
return (
<Heading as="h1" size="lg" marginBottom={{ base: 6, lg: 8 }}>{ text }</Heading>
);
};
export default AccountPageHeader;
export default PageHeader;
import React from 'react';
const TxDetails = () => {
return <div>TxDetails component</div>;
};
export default TxDetails;
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