Commit 7f9837b0 authored by isstuev's avatar isstuev

homepage header

parent 2dc65c83
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 29 28">
<g fill="currentColor" clip-path="url(#clock-light_svg__a)">
<path d="M14.75 25.375a11.375 11.375 0 1 1 0-22.75 11.375 11.375 0 0 1 0 22.75Zm0-21a9.625 9.625 0 1 0 0 19.25 9.625 9.625 0 0 0 0-19.25Z"/>
<path d="M19.563 19.688a.875.875 0 0 1-.622-.254L14.13 14.62a.874.874 0 0 1-.254-.621V7a.875.875 0 0 1 1.75 0v6.641l4.559 4.55a.874.874 0 0 1-.622 1.497Z"/>
</g>
<defs>
<clipPath id="clock-light_svg__a">
<path fill="#fff" d="M.75 0h28v28h-28z"/>
</clipPath>
</defs>
</svg>
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<path d="M12.923 16H3.077A3.078 3.078 0 0 1 0 12.924V4.312a.615.615 0 0 1 .615-.615h12.308A3.077 3.077 0 0 1 16 6.773v6.151A3.076 3.076 0 0 1 12.923 16ZM1.23 4.927v7.997a1.845 1.845 0 0 0 1.846 1.846h9.846a1.846 1.846 0 0 0 1.846-1.846V6.773a1.845 1.845 0 0 0-1.846-1.846H1.23Z"/>
<path d="M14.154 4.927a.616.616 0 0 1-.615-.615V2.62a1.433 1.433 0 0 0-.48-1.15 1.194 1.194 0 0 0-1.028-.197L1.71 3.617a.615.615 0 0 0-.48.615.615.615 0 0 1-1.23 0 1.845 1.845 0 0 1 1.433-1.815L11.76.073a2.401 2.401 0 0 1 2.068.437 2.67 2.67 0 0 1 .941 2.11v1.692a.615.615 0 0 1-.615.615Zm1.231 7.382h-4.308a2.462 2.462 0 1 1 0-4.921h4.308a.615.615 0 0 1 .615.615v3.69a.615.615 0 0 1-.615.616Zm-4.308-3.691a1.231 1.231 0 0 0 0 2.46h3.692v-2.46h-3.692Z"/>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 31 30">
<g fill="currentColor" clip-path="url(#wallet_svg__a)">
<path d="M22.75 27.188h-15A4.688 4.688 0 0 1 3.062 22.5V9.375A.937.937 0 0 1 4 8.437h18.75a4.688 4.688 0 0 1 4.688 4.688V22.5a4.688 4.688 0 0 1-4.688 4.688ZM4.937 10.313V22.5a2.812 2.812 0 0 0 2.813 2.813h15a2.812 2.812 0 0 0 2.813-2.813v-9.375a2.812 2.812 0 0 0-2.813-2.813H4.937Z"/>
<path d="M24.625 10.312a.937.937 0 0 1-.937-.937V6.797a2.184 2.184 0 0 0-.732-1.754 1.82 1.82 0 0 0-1.565-.3L5.669 8.315a.938.938 0 0 0-.731.938.938.938 0 0 1-1.875 0 2.813 2.813 0 0 1 2.184-2.766l15.731-3.572a3.656 3.656 0 0 1 3.15.666 4.069 4.069 0 0 1 1.435 3.216v2.578a.938.938 0 0 1-.938.937ZM26.5 21.563h-6.563a3.75 3.75 0 1 1 0-7.5H26.5a.938.938 0 0 1 .938.937v5.625a.938.938 0 0 1-.938.938Zm-6.563-5.625a1.875 1.875 0 1 0 0 3.75h5.625v-3.75h-5.625Z"/>
</g>
<defs>
<clipPath id="wallet_svg__a">
<path fill="#fff" d="M.25 0h30v30h-30z"/>
</clipPath>
</defs>
</svg>
import handler from 'lib/api/handler';
const getUrl = () => '/v2/stats';
const requestHandler = handler(getUrl, [ 'GET' ]);
export default requestHandler;
......@@ -2,7 +2,7 @@ import type { NextPage } from 'next';
import Head from 'next/head';
import React from 'react';
import Home from 'ui/pages/Home';
import Home from 'ui/pages/Home2';
const HomePage: NextPage = () => {
return (
......
......@@ -14,6 +14,11 @@ const sizes = {
fontSize: '48px',
lineHeight: '60px',
}),
xl: defineStyle({
fontSize: '40px',
lineHeight: '48px',
letterSpacing: '-1px',
}),
lg: defineStyle({
fontSize: '32px',
lineHeight: '40px',
......
import { Grid, Skeleton } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query';
import React from 'react';
import { Stats } from 'types/api/stats';
import { QueryKeys } from 'types/client/queries';
import blockIcon from 'icons/block.svg';
import clockIcon from 'icons/clock-light.svg';
import txIcon from 'icons/transactions.svg';
import walletIcon from 'icons/wallet.svg';
import useFetch from 'lib/hooks/useFetch';
import StatsItem from './StatsItem';
const Stats = () => {
const fetch = useFetch();
const { data, isLoading, isError } = useQuery<unknown, unknown, Stats>(
[ QueryKeys.stats ],
async() => await fetch(`/api/stats`),
);
if (isError) {
return null;
}
let content;
if (isLoading) {
content = (
<>
<Skeleton h="80px"/>
<Skeleton h="80px"/>
<Skeleton h="80px"/>
<Skeleton h="80px"/>
</>
);
}
if (data) {
content = (
<>
<StatsItem
icon={ blockIcon }
title="Total blocks"
value={ Number(data.total_blocks).toLocaleString() }
/>
<StatsItem
icon={ clockIcon }
title="Average block time"
value={ `${ (data.average_block_time / 1000).toFixed(1) } s` }
/>
<StatsItem
icon={ txIcon }
title="Total transactions"
value={ Number(data.total_transactions).toLocaleString() }
/>
<StatsItem
icon={ walletIcon }
title="Wallet addresses"
value={ Number(data.total_addresses).toLocaleString() }
/>
</>
);
}
return (
<Grid
gridTemplateColumns={{ lg: 'repeat(4, 1fr)', base: 'none' }}
gridTemplateRows={{ lg: 'none', base: 'repeat(4, 1fr)' }}
gridGap="10px"
marginTop="32px"
>
{ content }
</Grid>
);
};
export default Stats;
import { Box, Flex, Icon, Center, Text, LightMode } from '@chakra-ui/react';
import React from 'react';
type Props = {
icon: React.FC<React.SVGAttributes<SVGElement>>;
title: string;
value: string;
}
const StatsItem = ({ icon, title, value }: Props) => {
return (
<LightMode>
<Flex background="blue.50" padding={ 5 } borderRadius="16px">
<Center backgroundColor="green.100" borderRadius="12px" w={ 10 } h={ 10 } marginRight={ 4 }>
<Icon as={ icon } boxSize={ 7 } color="black"/>
</Center>
<Box>
<Text variant="secondary" fontSize="xs" lineHeight="16px">{ title }</Text>
<Text fontWeight={ 500 } fontSize="md">{ value }</Text>
</Box>
</Flex>
</LightMode>
);
};
export default StatsItem;
import { Box, Heading } from '@chakra-ui/react';
import React from 'react';
import Stats from 'ui/home/Stats';
import Page from 'ui/shared/Page/Page';
import SearchBar from 'ui/snippets/searchBar/SearchBar';
const Home = () => {
return (
<Page hasSearch={ false }>
<Box
w="100%"
// h="236px"
backgroundImage="radial-gradient(farthest-corner at 0 0, rgba(183, 148, 244, 0.8) 0%, rgba(0, 163, 196, 0.8) 100%)"
borderRadius="24px"
padding={{ base: '24px 40px', lg: '48px' }}
>
<Heading
as="h1"
size={{ base: 'lg', ld: 'xl' }}
fontWeight={{ base: 600, lg: 500 }}
color="white"
mb={{ base: 6, lg: 8 }}
>
Welcome to Blockscout explorer
</Heading>
<SearchBar backgroundColor="white" isHomepage/>
</Box>
<Stats/>
</Page>
);
};
export default Home;
......@@ -20,12 +20,14 @@ interface Props {
children: React.ReactNode;
wrapChildren?: boolean;
hideMobileHeaderOnScrollDown?: boolean;
hasSearch?: boolean;
}
const Page = ({
children,
wrapChildren = true,
hideMobileHeaderOnScrollDown,
hasSearch = true,
}: Props) => {
const fetch = useFetch();
......@@ -51,7 +53,7 @@ const Page = ({
<Flex w="100%" minH="100vh" alignItems="stretch">
<NavigationDesktop/>
<Flex flexDir="column" width="100%">
<Header hideOnScrollDown={ hideMobileHeaderOnScrollDown }/>
<Header hasSearch={ hasSearch } hideOnScrollDown={ hideMobileHeaderOnScrollDown }/>
<ErrorBoundary renderErrorScreen={ renderErrorScreen }>
{ renderedChildren }
</ErrorBoundary>
......
......@@ -10,7 +10,12 @@ import SearchBar from 'ui/snippets/searchBar/SearchBar';
import Burger from './Burger';
import ColorModeToggler from './ColorModeToggler';
const Header = ({ hideOnScrollDown }: {hideOnScrollDown?: boolean}) => {
type Props = {
hasSearch: boolean;
hideOnScrollDown?: boolean;
}
const Header = ({ hideOnScrollDown, hasSearch }: Props) => {
const bgColor = useColorModeValue('white', 'black');
return (
......@@ -38,7 +43,7 @@ const Header = ({ hideOnScrollDown }: {hideOnScrollDown?: boolean}) => {
<NetworkLogo/>
<ProfileMenuMobile/>
</Flex>
<SearchBar withShadow={ !hideOnScrollDown }/>
{ hasSearch && <SearchBar withShadow={ !hideOnScrollDown }/> }
</Box><HStack
as="header"
width="100%"
......@@ -50,7 +55,7 @@ const Header = ({ hideOnScrollDown }: {hideOnScrollDown?: boolean}) => {
paddingTop={ 9 }
paddingBottom="52px"
>
<SearchBar/>
<Box width="100%" opacity={ hasSearch ? 1 : 0 }><SearchBar/></Box>
<ColorModeToggler/>
<ProfileMenuDesktop/>
</HStack>
......
......@@ -5,8 +5,15 @@ import link from 'lib/link/link';
import SearchBarDesktop from './SearchBarDesktop';
import SearchBarMobile from './SearchBarMobile';
import SearchBarMobileHome from './SearchBarMobileHome';
const SearchBar = ({ withShadow }: {withShadow?: boolean}) => {
type Props = {
backgroundColor?: string;
withShadow?: boolean;
isHomepage?: boolean;
}
const SearchBar = ({ backgroundColor, isHomepage, withShadow }: Props) => {
const [ value, setValue ] = React.useState('');
const handleChange = React.useCallback((event: ChangeEvent<HTMLInputElement>) => {
......@@ -21,8 +28,21 @@ const SearchBar = ({ withShadow }: {withShadow?: boolean}) => {
return (
<>
<SearchBarDesktop onChange={ handleChange } onSubmit={ handleSubmit }/>
<SearchBarMobile onChange={ handleChange } onSubmit={ handleSubmit } withShadow={ withShadow }/>
<SearchBarDesktop onChange={ handleChange } onSubmit={ handleSubmit } backgroundColor={ backgroundColor }/>
{ !isHomepage && (
<SearchBarMobile
onChange={ handleChange }
onSubmit={ handleSubmit }
withShadow={ withShadow }
/>
) }
{ isHomepage && (
<SearchBarMobileHome
onChange={ handleChange }
onSubmit={ handleSubmit }
backgroundColor={ backgroundColor }
/>
) }
</>
);
};
......
......@@ -7,11 +7,19 @@ import searchIcon from 'icons/search.svg';
interface Props {
onChange: (event: ChangeEvent<HTMLInputElement>) => void;
onSubmit: (event: FormEvent<HTMLFormElement>) => void;
backgroundColor?: string;
}
const SearchBarDesktop = ({ onChange, onSubmit }: Props) => {
const SearchBarDesktop = ({ onChange, onSubmit, backgroundColor }: Props) => {
return (
<chakra.form noValidate onSubmit={ onSubmit } display={{ base: 'none', lg: 'block' }} w="100%">
<chakra.form
noValidate
onSubmit={ onSubmit }
display={{ base: 'none', lg: 'block' }}
w="100%"
bg={ backgroundColor }
borderRadius="10px"
>
<InputGroup>
<InputLeftAddon w="111px">All filters</InputLeftAddon>
<InputLeftElement w={ 6 } ml="132px" mr={ 2.5 }>
......
import { InputGroup, Input, InputLeftElement, Icon, LightMode, chakra } from '@chakra-ui/react';
import React from 'react';
import type { ChangeEvent, FormEvent } from 'react';
import searchIcon from 'icons/search.svg';
interface Props {
onChange: (event: ChangeEvent<HTMLInputElement>) => void;
onSubmit: (event: FormEvent<HTMLFormElement>) => void;
backgroundColor?: string;
}
const SearchBarMobileHome = ({ onChange, onSubmit, backgroundColor }: Props) => {
const commonProps = {
noValidate: true,
onSubmit: onSubmit,
width: '100%',
display: { base: 'block', lg: 'none' },
};
return (
<LightMode>
<chakra.form
{ ...commonProps }
bgColor={ backgroundColor }
h="60px"
borderRadius="10px"
>
<InputGroup size="md">
<InputLeftElement >
<Icon as={ searchIcon } boxSize={ 6 } color="blackAlpha.600"/>
</InputLeftElement>
<Input
paddingInlineStart="38px"
placeholder="Search by addresses / ... "
ml="1px"
onChange={ onChange }
border="none"
/>
</InputGroup>
</chakra.form>
</LightMode>
);
};
export default SearchBarMobileHome;
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