refactor: UI improvements (#67)

* feat: add pointer on SiteCard

* fix: don`t show ens when it`s empty

* style: add icons on wallet button

* styles: align home button on same splace in all views

* style: add tooltip on attributes card

* chore: install react-icons

* feat: add paste feature on owner address field

* style: add icon of connected wallet

* style: add icon metamask

* fix: fix error message on owner address field

* chore: remove console.log

* style: add colors theme

* refactor: refactor icons. Create icon component

* refactor: remove function from component

* fix: fix husky precommit to add jsx and tsx files

* refactor: add metamask icon

* fix: fix import

* chore: remove metamask svg from assets
This commit is contained in:
Camila Sosa Morales 2023-01-11 16:34:05 -05:00 committed by GitHub
parent 482203529c
commit 66e041f63f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 436 additions and 156 deletions

View File

@ -1,3 +1,3 @@
{
"*.{js,json,sol,ts}": "prettier --write"
"*.{js,jsx,json,sol,ts,tsx}": "prettier --write"
}

View File

@ -39,6 +39,7 @@
"eslint-plugin-react": "^7.31.11",
"ethers": "^5.7.2",
"prettier": "^2.8.0",
"react-icons": "^4.7.1",
"react-query": "^3.39.2",
"ts-loader": "^9.4.1",
"typescript": "^4.9.3",

View File

@ -16,13 +16,16 @@ export const AttributesDetail = ({
return (
<HStack shouldWrapChildren display="inline" spacing="0px">
<CardAttributes heading="Owner" info={owner} />
{attributes.map((attribute) => (
<CardAttributes
key={attribute.trait_type}
heading={attribute.trait_type}
info={attribute.value}
/>
))}
{attributes.map(
(attribute) =>
attribute.value !== '' && (
<CardAttributes
key={attribute.trait_type}
heading={attribute.trait_type}
info={attribute.value}
/>
)
)}
<CardAttributes heading="Token ID" info={tokendId} />
</HStack>
);

View File

@ -1,4 +1,4 @@
import { Card, CardBody } from '@chakra-ui/react';
import { Card, CardBody, Tooltip } from '@chakra-ui/react';
import { TileInfo } from '../tile-info';
type CardAttributesProps = {
@ -15,9 +15,11 @@ export const CardAttributes = ({ heading, info }: CardAttributesProps) => (
variant="outline"
width="200px"
>
<CardBody width="200px">
<TileInfo size="sm" heading={heading} info={info} widthText={160} />
</CardBody>
<Tooltip label={info} bg="gray">
<CardBody width="200px">
<TileInfo size="sm" heading={heading} info={info} widthText={160} />
</CardBody>
</Tooltip>
</Card>
);

View File

@ -47,9 +47,10 @@ export const SiteCard: React.FC<CardSiteProps> = ({ tokenId }) => {
const { name, owner, image, external_url: externalUrl } = data as any;
return (
<Card
borderColor="#f3f3f36b !important"
_hover={{ cursor: 'pointer' }}
borderColor="custom.white.100 !important"
boxShadow="1px 10px 24px -2px #85848480"
backgroundColor="#c5c5c50a"
backgroundColor="custom.white.50"
border="1px"
borderRadius="10px"
width="350px"
@ -67,7 +68,7 @@ export const SiteCard: React.FC<CardSiteProps> = ({ tokenId }) => {
>
<Box height="180px">
<ImagePreview
backgroundColor="#161616"
backgroundColor="custom.black"
display="block"
marginLeft="auto"
marginRight="auto"

View File

@ -0,0 +1 @@
export * from './metamask-icon';

View File

@ -0,0 +1,136 @@
import { Icon, IconProps } from '@chakra-ui/react';
export const MetamaskIcon: React.FC<IconProps> = (props) => {
const { width = '1.5em', height = '1.5em' } = props;
return (
<Icon
width={width}
height={height}
{...props}
viewBox="0 0 256 240"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMidYMid"
>
<title>MetaMask</title>
<g>
<polygon
fill="#E17726"
points="250.066018 -8.89651791e-15 140.218553 81.2793133 160.645643 33.3787726"
></polygon>
<polygon
fill="#E27625"
points="6.19062016 0.0955267053 95.3715526 33.38465 114.767923 81.9132784"
></polygon>
<polygon
fill="#E27625"
points="205.859986 172.858026 254.410647 173.782023 237.442988 231.424252 178.200429 215.112736"
></polygon>
<polygon
fill="#E27625"
points="50.1391619 172.857971 77.6964289 215.11288 18.5530579 231.425317 1.68846828 173.782036"
></polygon>
<polygon
fill="#E27625"
points="112.130724 69.5516472 114.115388 133.635085 54.744344 130.933905 71.6319541 105.456448 71.8456974 105.210668"
></polygon>
<polygon
fill="#E27625"
points="143.254237 68.8369186 184.153999 105.213392 184.365514 105.45719 201.253537 130.934656 141.89632 133.635226"
></polygon>
<polygon
fill="#E27625"
points="79.4347776 173.043957 111.853145 198.302774 74.1951401 216.484384"
></polygon>
<polygon
fill="#E27625"
points="176.57078 173.040009 181.701672 216.484523 144.149363 198.301203"
></polygon>
<polygon
fill="#D5BFB2"
points="144.977922 195.921642 183.084879 214.373531 147.637779 231.220354 148.005818 220.085704"
></polygon>
<polygon
fill="#D5BFB2"
points="111.01133 195.929982 108.102093 219.90359 108.340838 231.207237 72.8105145 214.373665"
></polygon>
<polygon
fill="#233447"
points="100.007166 141.998856 109.965172 162.926822 76.0615945 152.995277"
></polygon>
<polygon
fill="#233447"
points="155.991579 142.000941 180.049716 152.994594 146.03608 162.923638"
></polygon>
<polygon
fill="#CC6228"
points="82.0263962 172.830401 76.5459821 217.870023 47.1731221 173.814952"
></polygon>
<polygon
fill="#CC6228"
points="173.976111 172.8305 208.830462 173.815081 179.347016 217.871514"
></polygon>
<polygon
fill="#CC6228"
points="202.112267 128.387342 176.746779 154.238424 157.190334 145.301352 147.82685 164.985265 141.688645 131.136429"
></polygon>
<polygon
fill="#CC6228"
points="53.8753865 128.386879 114.309585 131.136429 108.17138 164.985265 98.8061425 145.303856 79.3525107 154.238823"
></polygon>
<polygon
fill="#E27525"
points="52.165606 123.082486 80.8639084 152.203386 81.8584812 180.952278"
></polygon>
<polygon
fill="#E27525"
points="203.863346 123.029784 174.117491 181.003017 175.237428 152.202737"
></polygon>
<polygon
fill="#E27525"
points="112.906762 124.855691 114.061658 132.125682 116.915771 150.236518 115.080954 205.861884 106.405804 161.177486 106.402953 160.71542"
></polygon>
<polygon
fill="#E27525"
points="143.077997 124.755417 149.599051 160.715451 149.596194 161.177486 140.899333 205.973714 140.55515 194.76913 139.198167 149.907127"
></polygon>
<polygon
fill="#F5841F"
points="177.788479 151.045975 176.81718 176.023897 146.543342 199.61119 140.4233 195.28712 147.283427 159.951634"
></polygon>
<polygon
fill="#F5841F"
points="78.3167053 151.046455 108.716464 159.952427 115.576437 195.28712 109.456385 199.611197 79.1807344 176.021881"
></polygon>
<polygon
fill="#C0AC9D"
points="67.0180978 208.857597 105.750143 227.209502 105.586194 219.372868 108.826835 216.528328 147.160694 216.528328 150.518758 219.363342 150.271375 227.194477 188.757733 208.903978 170.030292 224.379509 147.384611 239.933315 108.516484 239.933315 85.8855503 224.315941"
></polygon>
<polygon
fill="#161616"
points="142.203502 193.479367 147.679764 197.347701 150.888964 222.952494 146.244706 219.030957 109.769299 219.030957 105.213447 223.031398 108.317268 197.349663 113.795429 193.479367"
></polygon>
<polygon
fill="#763E1A"
points="242.814251 2.24978946 256 41.8072765 247.765337 81.803692 253.629038 86.3274221 245.694407 92.3812097 251.657525 96.9865879 243.761206 104.178247 248.609106 107.688972 235.743366 122.714803 182.973386 107.350364 182.516079 107.105244 144.488982 75.0267414"
></polygon>
<polygon
fill="#763E1A"
points="13.1860054 2.24978557 111.51151 75.0267402 73.4844118 107.105244 73.0271023 107.350365 20.2567388 122.714804 7.39121291 107.688927 12.2352706 104.180751 4.34251001 96.9865923 10.2945566 92.3862179 2.24133703 86.315099 8.32629691 81.7886671 -8.89651791e-15 41.8087534"
></polygon>
<polygon
fill="#F5841F"
points="180.391638 103.990363 236.304873 120.269177 254.470245 176.254719 206.546445 176.25462 173.525532 176.671282 197.539657 129.863284"
></polygon>
<polygon
fill="#F5841F"
points="75.6080363 103.990376 58.4568191 129.863284 82.4741865 176.671282 49.4693913 176.254719 1.63053271 176.254719 19.6938968 120.269548"
></polygon>
<polygon
fill="#F5841F"
points="163.383898 33.1117385 147.744691 75.3505047 144.425852 132.411352 143.155934 150.295986 143.055195 195.983514 112.943788 195.983514 112.846176 150.381702 111.572114 132.395585 108.251786 75.3505047 92.6150854 33.1117385"
></polygon>
</g>
</Icon>
);
};

View File

@ -0,0 +1,15 @@
import { FaWallet } from 'react-icons/fa';
import { IoExitOutline } from 'react-icons/io5';
import { AiOutlineCopy } from 'react-icons/ai';
import { MetamaskIcon } from './custom';
export const IconLibrary = Object.freeze({
copy: AiOutlineCopy,
'log-out': IoExitOutline,
metamask: MetamaskIcon,
wallet: FaWallet,
});
export type IconName = keyof typeof IconLibrary;
export type IconType<Name extends IconName> = typeof IconLibrary[Name];

View File

@ -0,0 +1,16 @@
import {
forwardRef,
IconProps as IconPropsChakra,
Icon as IconChakra,
} from '@chakra-ui/react';
import { IconLibrary, IconName, IconType } from './icon-types';
export type IconComponentProps = IconPropsChakra & { name: IconName };
export const Icon = forwardRef<IconComponentProps, 'svg'>(
({ name, ...iconProps }, ref) => {
const IconElement: IconType<typeof name> = IconLibrary[name];
return <IconChakra as={IconElement} {...iconProps} ref={ref} />;
}
);

View File

@ -0,0 +1 @@
export * from './icon';

View File

@ -1 +1,2 @@
export * from './wallet-button';
export * from './wallet.utils';

View File

@ -1,7 +1,16 @@
import { useCallback } from 'react';
import { useAppDispatch, useWalletStore, walletActions } from '@/store';
import { contractAddress } from '@/utils';
import { Menu, MenuButton, MenuList, MenuItem, Button } from '@chakra-ui/react';
import {
Menu,
MenuButton,
MenuList,
MenuItem,
Button,
Flex,
} from '@chakra-ui/react';
import { Icon } from '../icon';
import { WalletType } from './wallet.utils';
const WalletMenu: React.FC = () => {
const { account = '', provider } = useWalletStore();
@ -17,13 +26,33 @@ const WalletMenu: React.FC = () => {
}, [dispatch]);
return (
<Menu>
<MenuButton as={Button}>
{`${provider} (${contractAddress(account)})`}
</MenuButton>
<MenuList>
<MenuItem onClick={handleCopyAccount}>Copy Account</MenuItem>
<MenuItem onClick={handleDisconnect}>Disconnect</MenuItem>
<Menu colorScheme={'custom.gray.200'}>
<Button borderRadius="50px" as={MenuButton}>
<Flex alignItems={'center'}>
<Icon
name={WalletType[provider?.toString() as keyof typeof WalletType]}
mr="0.5em"
/>
{contractAddress(account)}
</Flex>
</Button>
<MenuList bg={'custom.gray.200'}>
<MenuItem
_hover={{ bg: 'custom.gray.100' }}
bg={'custom.gray.200'}
onClick={handleCopyAccount}
icon={<Icon name="copy" />}
>
Copy Account
</MenuItem>
<MenuItem
_hover={{ bg: 'custom.gray.100' }}
bg={'custom.gray.200'}
onClick={handleDisconnect}
icon={<Icon name="log-out" />}
>
Disconnect
</MenuItem>
</MenuList>
</Menu>
);
@ -40,15 +69,24 @@ const ConnectionMenu: React.FC = () => {
return (
<Menu>
<MenuButton
as={Button}
<Button
borderRadius="50px"
as={MenuButton}
leftIcon={<Icon name="wallet" />}
isLoading={state === 'loading'}
disabled={state === 'loading'}
>
Connect Wallet
</MenuButton>
<MenuList>
<MenuItem onClick={handleConnectWallet}>Metamask</MenuItem>
Connect
</Button>
<MenuList bg={'custom.gray.200'}>
<MenuItem
_hover={{ bg: 'custom.gray.100' }}
bg={'custom.gray.200'}
onClick={handleConnectWallet}
icon={<Icon name={WalletType.metamask} />}
>
Metamask
</MenuItem>
</MenuList>
</Menu>
);

View File

@ -0,0 +1,3 @@
export enum WalletType {
metamask = 'metamask',
}

View File

@ -0,0 +1,16 @@
export const colors = {
custom: {
gray: {
100: '#4e4e4e',
200: '#282824',
},
black: '#161616',
blue: {
100: '#1d4ed8',
},
white: {
50: '#c5c5c50a',
100: '#f3f3f36b',
},
},
};

View File

@ -0,0 +1 @@
export * from './colors';

View File

@ -1,12 +1,13 @@
import { extendTheme } from '@chakra-ui/react';
import { colors } from './foundations';
const appTheme = {
styles: {
global: {
body: {
color: 'rgba(255, 255, 255)',
bg: '#161616',
margin: '50px',
bg: 'custom.black',
margin: '25px 50px',
},
},
},
@ -17,6 +18,7 @@ const appTheme = {
sizes: {
modalHeight: '345px',
},
colors,
};
export const theme = extendTheme(appTheme);

View File

@ -44,7 +44,7 @@ export const MintedSiteDetail = () => {
return (
<>
<Flex width="full" align="center" justifyContent="center">
<Flex width="full" align="center" justifyContent="center" mt="30px">
<Box width={{ base: '100%' }}>
<HomeButton />
<Box

View File

@ -1,4 +1,4 @@
import { useCallback } from 'react';
import React, { useCallback } from 'react';
import {
Heading,
Flex,
@ -7,17 +7,19 @@ import {
FormLabel,
Button,
FormErrorMessage,
IconButton,
Textarea,
Grid,
GridItem,
VStack,
InputGroup,
Input,
InputRightElement,
InputProps,
} from '@chakra-ui/react';
import { Formik, Field } from 'formik';
import { ArrowBackIcon } from '@chakra-ui/icons';
import { Link } from 'react-router-dom';
import { Formik, Field, useFormikContext } from 'formik';
import { getRepoAndCommit } from '@/utils';
import { validateFields } from './mint-site.utils';
import { InputFieldForm } from '@/components';
import { HomeButton, InputFieldForm } from '@/components';
import { FleekERC721 } from '@/integrations';
import { useWalletStore } from '@/store';
import { useToast } from '@/hooks';
@ -42,6 +44,29 @@ const initialValues = {
ens: '',
} as FormValues;
const OwnerAdress = (props: InputProps) => {
const { setFieldValue } = useFormikContext();
const handlePasteAddress = () => {
if (setFieldValue && props.name) {
navigator.clipboard
.readText()
.then((text) => setFieldValue(props.name as string, text));
}
};
return (
<InputGroup size="md">
<Input {...props} pr="4.5rem" />
<InputRightElement width="4.5rem">
<Button h="1.75rem" size="sm" onClick={handlePasteAddress}>
Paste
</Button>
</InputRightElement>
</InputGroup>
);
};
export const MintSite = () => {
const setToastInfo = useToast();
const { provider } = useWalletStore();
@ -94,128 +119,141 @@ export const MintSite = () => {
return (
<>
<Flex width="full" align="center" justifyContent="center" mt="50px">
<Box width={{ base: '100%', md: '80%' }}>
<IconButton
as={Link}
to="/home"
aria-label="back home"
icon={<ArrowBackIcon />}
variant="link"
size={'xl'}
textDecoration={'none'}
/>
<Box textAlign="center" mt={2}>
<Heading>Mint your Site</Heading>
</Box>
<Box my={4} textAlign="left">
<Formik
validate={validateFields}
initialValues={initialValues}
onSubmit={handleSubmitForm}
>
{({ values, touched, handleSubmit, isSubmitting, errors }) => (
<form onSubmit={handleSubmit}>
<Box
display="flex"
flexDirection={{ base: 'column', md: 'row' }}
>
<InputFieldForm
label="Name"
fieldName="name"
mr={5}
error={errors.name}
isInvalid={!!errors.name && touched.name}
isRequired
/>
<InputFieldForm
label="Owner address"
fieldName="ownerAddress"
error={errors.ownerAddress}
isInvalid={!!errors.ownerAddress && touched.ownerAddress}
isRequired
/>
</Box>
<FormControl
mt={6}
isRequired
isInvalid={!!errors.description && touched.description}
>
<FormLabel htmlFor="description">Description</FormLabel>
<Field as={Textarea} name="description" id="description" />
{errors.description && (
<FormErrorMessage>{errors.description}</FormErrorMessage>
)}
</FormControl>
<Box
display="flex"
flexDirection={{ base: 'column', md: 'row' }}
mt={6}
>
<InputFieldForm
label="Image (IPFS Link)"
fieldName="image"
mr={5}
error={errors.image}
isInvalid={!!errors.image && touched.image}
isRequired
/>
<InputFieldForm
label="External url"
fieldName="externalUrl"
error={errors.externalUrl}
isInvalid={!!errors.externalUrl && touched.externalUrl}
isRequired
/>
</Box>
<Grid
templateColumns={{
md: 'repeat(3, 1fr)',
}}
gap={4}
mt={6}
>
<GridItem colSpan={2}>
<Flex width="full" align="center" justifyContent="center" mt="30px">
<Box width={{ base: '100%' }}>
<HomeButton />
<VStack spacing="10px">
<Box textAlign="center" mt={2}>
<Heading>Mint your Site</Heading>
</Box>
<Box textAlign="left" width="80%" justifyContent="center">
<Formik
validate={validateFields}
initialValues={initialValues}
onSubmit={handleSubmitForm}
>
{({ values, touched, handleSubmit, isSubmitting, errors }) => (
<form onSubmit={handleSubmit}>
<Box
display="flex"
flexDirection={{ base: 'column', md: 'row' }}
>
<InputFieldForm
label="Github commit url"
fieldName="githubCommit"
label="Name"
fieldName="name"
mr={5}
error={errors.githubCommit}
isInvalid={
!!errors.githubCommit && touched.githubCommit
}
error={errors.name}
isInvalid={!!errors.name && touched.name}
isRequired
/>
</GridItem>
<GridItem colSpan={{ base: 2, md: 1 }}>
<InputFieldForm label="ENS" fieldName="ens" />
</GridItem>
</Grid>
<Button
colorScheme="blue"
backgroundColor="#1d4ed8"
width="full"
mt={4}
type="submit"
isLoading={isSubmitting}
loadingText="Minting..."
disabled={
isSubmitting ||
!values.name ||
!values.description ||
!values.githubCommit ||
!values.ownerAddress ||
!values.image ||
!values.externalUrl ||
!provider
}
>
Mint
</Button>
</form>
)}
</Formik>
</Box>
<FormControl
isRequired
isInvalid={
!!errors.ownerAddress && touched.ownerAddress
}
>
<FormLabel htmlFor="ownerAddress">
Owner address
</FormLabel>
<Field
as={OwnerAdress}
name="ownerAddress"
id="ownerAddress"
/>
{errors.ownerAddress && (
<FormErrorMessage>
{errors.ownerAddress}
</FormErrorMessage>
)}
</FormControl>
</Box>
<FormControl
mt={6}
isRequired
isInvalid={!!errors.description && touched.description}
>
<FormLabel htmlFor="description">Description</FormLabel>
<Field
as={Textarea}
name="description"
id="description"
/>
{errors.description && (
<FormErrorMessage>
{errors.description}
</FormErrorMessage>
)}
</FormControl>
<Box
display="flex"
flexDirection={{ base: 'column', md: 'row' }}
mt={6}
>
<InputFieldForm
label="Image (IPFS Link)"
fieldName="image"
mr={5}
error={errors.image}
isInvalid={!!errors.image && touched.image}
isRequired
/>
<InputFieldForm
label="External url"
fieldName="externalUrl"
error={errors.externalUrl}
isInvalid={!!errors.externalUrl && touched.externalUrl}
isRequired
/>
</Box>
<Grid
templateColumns={{
md: 'repeat(3, 1fr)',
}}
gap={4}
mt={6}
>
<GridItem colSpan={2}>
<InputFieldForm
label="Github commit url"
fieldName="githubCommit"
mr={5}
error={errors.githubCommit}
isInvalid={
!!errors.githubCommit && touched.githubCommit
}
isRequired
/>
</GridItem>
<GridItem colSpan={{ base: 2, md: 1 }}>
<InputFieldForm label="ENS" fieldName="ens" />
</GridItem>
</Grid>
<Button
colorScheme="blue"
backgroundColor="custom.blue.100"
width="full"
mt={4}
type="submit"
isLoading={isSubmitting}
loadingText="Minting..."
disabled={
isSubmitting ||
!values.name ||
!values.description ||
!values.githubCommit ||
!values.ownerAddress ||
!values.image ||
!values.externalUrl ||
!provider
}
>
Mint
</Button>
</form>
)}
</Formik>
</Box>
</VStack>
</Box>
</Flex>
</>

View File

@ -3717,6 +3717,11 @@ react-focus-lock@^2.9.1:
use-callback-ref "^1.3.0"
use-sidecar "^1.1.2"
react-icons@^4.7.1:
version "4.7.1"
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.7.1.tgz#0f4b25a5694e6972677cb189d2a72eabea7a8345"
integrity sha512-yHd3oKGMgm7zxo3EA7H2n7vxSoiGmHk5t6Ou4bXsfcgWyhfDKMpyKfhHR6Bjnn63c+YXBLBPUql9H4wPJM6sXw==
react-is@^16.13.1, react-is@^16.7.0:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"