chore: remove chakra (#86)

* refactor: remove chakra

* refactor: remove stitches folder
This commit is contained in:
Camila Sosa Morales 2023-01-26 16:02:08 -05:00 committed by GitHub
parent 75a6de5ac7
commit 0553bef914
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
59 changed files with 76 additions and 2151 deletions

View File

@ -1,4 +1,4 @@
import { dripStitches } from '../src/theme/stitches';
import { dripStitches } from '../src/theme';
import addons from '@storybook/addons';
import { useEffect } from 'react';
import { themes } from '@storybook/theming';

View File

@ -13,17 +13,12 @@
},
"author": "Fleek",
"dependencies": {
"@chakra-ui/icons": "^2.0.13",
"@chakra-ui/react": "^2.4.2",
"@emotion/react": "^11.10.5",
"@emotion/styled": "^11.10.5",
"@ethersproject/providers": "^5.7.2",
"@radix-ui/colors": "^0.1.8",
"@react-icons/all-files": "^4.1.0",
"@reduxjs/toolkit": "^1.9.1",
"@stitches/react": "^1.2.8",
"formik": "^2.2.9",
"framer-motion": "^7.6.17",
"path": "^0.12.7",
"react": "^18.2.0",
"react-dom": "^18.2.0",

View File

@ -1,8 +1,7 @@
import { BrowserRouter, Route, Routes, Navigate } from 'react-router-dom';
import { WalletButton } from './components';
import { initializeWallet } from './store';
import { themeGlobals } from 'theme/stitches/globals';
import { Home, MintSite, MintedSiteDetail } from './views';
import { themeGlobals } from '@/theme/globals';
import { Home } from './views';
initializeWallet();
@ -10,12 +9,9 @@ export const App = () => {
themeGlobals();
return (
<>
<WalletButton />
<BrowserRouter>
<Routes>
<Route path="/mint-site" element={<MintSite />} />
<Route path="/home" element={<Home />} />
<Route path="/detail" element={<MintedSiteDetail />} />
<Route path="*" element={<Navigate to="/home" />} />
</Routes>
</BrowserRouter>

View File

@ -1,34 +0,0 @@
import {
AccordionItem as AccordionItemChakra,
AccordionButton,
Box,
Heading,
AccordionIcon,
AccordionPanel,
AccordionPanelProps,
forwardRef,
} from '@chakra-ui/react';
import React from 'react';
type AccordionProps = AccordionPanelProps & {
children: React.ReactNode;
heading: string;
};
export const AccordionItem = forwardRef<AccordionProps, 'div'>(
({ children, heading, ...panelProps }, ref) => {
return (
<AccordionItemChakra>
<AccordionButton borderBottomWidth="1px">
<Box flex="1" textAlign="left">
<Heading size="md"> {heading}</Heading>
</Box>
<AccordionIcon />
</AccordionButton>
<AccordionPanel ref={ref} {...panelProps} pb={4} overflowY="scroll">
{children}
</AccordionPanel>
</AccordionItemChakra>
);
}
);

View File

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

View File

@ -1,33 +0,0 @@
import { SiteNFTDetail } from '@/types';
import { HStack } from '@chakra-ui/react';
import { CardAttributes } from '../card';
type AttributesDetailProps = {
owner: string;
attributes: SiteNFTDetail['attributes'];
tokendId: string;
};
export const AttributesDetail = ({
owner,
attributes,
tokendId,
}: AttributesDetailProps) => {
return (
<HStack shouldWrapChildren display="inline" spacing="0px">
<CardAttributes heading="Owner" info={owner} />
{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 +0,0 @@
export * from './attributes-detail';

View File

@ -1,25 +0,0 @@
import { Card, CardBody, Tooltip } from '@chakra-ui/react';
import { TileInfo } from '../tile-info';
type CardAttributesProps = {
heading: string;
info: string;
};
export const CardAttributes = ({ heading, info }: CardAttributesProps) => (
<Card
mr="10px"
mb="5px"
direction={{ base: 'column', sm: 'row' }}
overflow="hidden"
variant="outline"
width="200px"
>
<Tooltip label={info} bg="gray">
<CardBody width="200px">
<TileInfo size="sm" heading={heading} info={info} widthText={160} />
</CardBody>
</Tooltip>
</Card>
);

View File

@ -1,105 +0,0 @@
import { ImagePreview, TileInfo } from '@/components';
import { SiteNFTDetail } from '@/types';
import { useNavigate } from 'react-router-dom';
import {
Box,
Card,
CardBody,
Heading,
LayoutProps,
Link,
Stack,
} from '@chakra-ui/react';
import React from 'react';
import { FleekERC721 } from '@/integrations';
import { useQuery } from 'react-query';
interface CardSiteProps {
tokenId: number;
}
type InfoContainerProps = {
heading: string;
info: React.ReactNode;
width: LayoutProps['width'];
};
const InfoContainer = ({ heading, info, width }: InfoContainerProps) => (
<TileInfo
size="xs"
direction="row"
mr="5px"
width={width}
heading={heading}
textAlignText="left"
info={info}
/>
);
export const SiteCard: React.FC<CardSiteProps> = ({ tokenId }) => {
const navigate = useNavigate();
const { data, isLoading } = useQuery<SiteNFTDetail>(
`fetchDetail${tokenId}`,
() => FleekERC721.tokenMetadata(tokenId)
);
if (!data || isLoading) return null;
const { name, owner, image, external_url: externalUrl } = data as any;
return (
<Card
_hover={{ cursor: 'pointer' }}
borderColor="custom.white.100 !important"
boxShadow="1px 10px 24px -2px #85848480"
backgroundColor="custom.white.50"
border="1px"
borderRadius="10px"
width="350px"
height="350px"
onClick={() => navigate(`/detail?tokenId=${tokenId}`)}
>
<CardBody width="350px" height="350px" paddingTop="10px">
<Heading size="md" textAlign="center" marginBottom="10px">
{name}
</Heading>
<Link
href={externalUrl}
isExternal
onClick={(e) => e.stopPropagation()}
>
<Box height="180px">
<ImagePreview
backgroundColor="custom.black"
display="block"
marginLeft="auto"
marginRight="auto"
image={image}
objectFit="contain"
width="100%"
height="100%"
borderRadius="20px"
boxShadow="0px 12px 24px -5px #5a575761"
/>
</Box>
</Link>
<Stack mt="10px" spacing="3" overflowY="scroll">
<InfoContainer heading="Owner" info={owner} width="auto" />
<InfoContainer heading="Token ID" info={tokenId} width="100px" />
<InfoContainer
heading="External url"
width="100px"
info={
<Link
href={externalUrl}
isExternal
onClick={(e) => e.stopPropagation()}
>
<u>{externalUrl}</u>
</Link>
}
/>
</Stack>
</CardBody>
</Card>
);
};

View File

@ -1,2 +0,0 @@
export * from './card-attributes';
export * from './card-site';

View File

@ -1,4 +1,4 @@
import { dripStitches } from '../../../theme/stitches'; //TODO replace with absolute path
import { dripStitches } from '../../../theme'; //TODO replace with absolute path
import { Flex, Grid } from '../../layout';
const { styled } = dripStitches;

View File

@ -1,4 +1,4 @@
import { dripStitches } from '../../../theme/stitches'; //TODO replace with absolute path
import { dripStitches } from '../../../theme'; //TODO replace with absolute path
const { styled } = dripStitches;

View File

@ -1,4 +1,4 @@
import { dripStitches } from '../../../theme/stitches'; //TODO replace with absolute path
import { dripStitches } from '../../../theme'; //TODO replace with absolute path
import { Flex, Grid } from '../../layout';
const { styled } = dripStitches;

View File

@ -1,4 +1,4 @@
import { dripStitches } from '../../../theme/stitches'; //TODO replace with absolute path
import { dripStitches } from '../../../theme'; //TODO replace with absolute path
import React, { HTMLAttributes, useMemo } from 'react';
import {

View File

@ -2,7 +2,7 @@ import { Flex } from '../../layout';
import { Button } from './button';
import { IconButton } from './icon-button';
import { Icon as IconComponent } from '../icon';
import { dripStitches } from '../../../theme/stitches';
import { dripStitches } from '../../../theme';
export default {
title: 'Components/Button',

View File

@ -1,4 +1,4 @@
import { dripStitches } from '../../../theme/stitches';
import { dripStitches } from '../../../theme';
import { CSS } from '@stitches/react';
type StyledButtonProps = React.ComponentProps<typeof StyledButton>;

View File

@ -1,4 +1,4 @@
import { dripStitches } from '../../../theme/stitches'; //TODO replace for absolute path
import { dripStitches } from '../../../theme'; //TODO replace for absolute path
const { styled } = dripStitches;

View File

@ -1,18 +0,0 @@
import { ArrowBackIcon } from '@chakra-ui/icons';
import { IconButton } from '@chakra-ui/react';
import { Link } from 'react-router-dom';
export const HomeButton = () => {
return (
<IconButton
as={Link}
to="/home"
aria-label="back home"
icon={<ArrowBackIcon />}
variant="link"
size={'xl'}
textDecoration={'none'}
/>
);
};

View File

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

View File

@ -1,22 +0,0 @@
import { forwardRef, Image, ImageProps } from '@chakra-ui/react';
type ImagePreviewProps = ImageProps & {
image: string;
};
export const ImagePreview = forwardRef<ImagePreviewProps, 'img'>(
({ image, ...imageProps }, ref) => {
return (
<>
{/* TODO add fallback Image */}
<Image
ref={ref}
src={image}
{...imageProps}
fallbackSrc="https://via.placeholder.com/150"
/>
</>
);
}
);

View File

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

View File

@ -1,10 +1,2 @@
export * from './wallet-button';
export * from './loading';
export * from './home-button';
export * from './image-preview';
export * from './tile-info';
export * from './card';
export * from './accordion-item';
export * from './input-field-form';
export * from './attributes-detail';
export * from './core';
export * from './layout';

View File

@ -1 +0,0 @@
export * from './input-field-form';

View File

@ -1,25 +0,0 @@
import {
FormControl,
FormControlProps,
FormErrorMessage,
FormLabel,
forwardRef,
Input,
} from '@chakra-ui/react';
import { Field } from 'formik';
type InputFieldFormProps = FormControlProps & {
label: string;
fieldName: string;
error?: string;
};
export const InputFieldForm = forwardRef<InputFieldFormProps, 'div'>(
({ label, fieldName, error, ...formControlProps }, ref) => (
<FormControl ref={ref} {...formControlProps}>
<FormLabel htmlFor={fieldName}>{label}</FormLabel>
<Field as={Input} name={fieldName} id={fieldName} type="text" />
{error && <FormErrorMessage>{error}</FormErrorMessage>}
</FormControl>
)
);

View File

@ -1,4 +1,4 @@
import { dripStitches } from '../../theme/stitches'; //TODO replace with absolute path
import { dripStitches } from '../../theme'; //TODO replace with absolute path
import React from 'react';
const { styled } = dripStitches;

View File

@ -1,4 +1,4 @@
import { dripStitches } from '../../theme/stitches'; //TODO replace with absolute path
import { dripStitches } from '../../theme'; //TODO replace with absolute path
const { styled } = dripStitches;

View File

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

View File

@ -1,16 +0,0 @@
import { Flex, Spinner } from '@chakra-ui/react';
export const Loading = () => {
return (
<Flex justifyContent="center" height="80vh" alignItems="center">
<Spinner
thickness="3px"
speed="0.65s"
emptyColor="gray.200"
color="blue.500"
size="xl"
/>
</Flex>
);
};

View File

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

View File

@ -1,47 +0,0 @@
import {
Flex,
forwardRef,
Heading,
HeadingProps,
Text,
} from '@chakra-ui/react';
type TileInfoProps = HeadingProps & {
heading: string;
info: React.ReactNode;
widthText?: number;
textAlignText?: 'center' | 'left'; //TODO allow more text alignment types
direction?: 'column' | 'row';
alignItems?: string;
};
export const TileInfo = forwardRef<TileInfoProps, 'h2'>(
(
{
heading,
info,
widthText = 250,
textAlignText = 'center',
direction = 'column',
alignItems = 'center',
...headingProps
},
ref
) => (
<Flex direction={direction} alignItems={alignItems}>
<Heading ref={ref} {...headingProps}>
{heading}
</Heading>
<Text
width={widthText}
whiteSpace="nowrap"
overflow="hidden"
textOverflow="ellipsis"
textAlign={textAlignText}
>
{info}
</Text>
</Flex>
)
);

View File

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

View File

@ -1,99 +0,0 @@
import { useCallback } from 'react';
import { useAppDispatch, useWalletStore, walletActions } from '@/store';
import { contractAddress } from '@/utils';
import {
Menu,
MenuButton,
MenuList,
MenuItem,
Button,
Flex,
} from '@chakra-ui/react';
import { Icon } from '../core/icon';
import { WalletType } from './wallet.utils';
const WalletMenu: React.FC = () => {
const { account = '', provider } = useWalletStore();
const dispatch = useAppDispatch();
const handleCopyAccount = useCallback(() => {
navigator.clipboard.writeText(account);
}, [account]);
const handleDisconnect = useCallback(() => {
dispatch(walletActions.disconnect());
}, [dispatch]);
return (
<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}
>
Copy Account
</MenuItem>
<MenuItem
_hover={{ bg: 'custom.gray.100' }}
bg={'custom.gray.200'}
onClick={handleDisconnect}
>
Disconnect
</MenuItem>
</MenuList>
</Menu>
);
};
const ConnectionMenu: React.FC = () => {
const { state } = useWalletStore();
const dispatch = useAppDispatch();
const handleConnectWallet = useCallback(() => {
dispatch(walletActions.connect('metamask'));
}, [dispatch]);
return (
<Menu>
<Button
borderRadius="50px"
as={MenuButton}
isLoading={state === 'loading'}
disabled={state === 'loading'}
>
Connect
</Button>
<MenuList bg={'custom.gray.200'}>
<MenuItem
_hover={{ bg: 'custom.gray.100' }}
bg={'custom.gray.200'}
onClick={handleConnectWallet}
>
Metamask
</MenuItem>
</MenuList>
</Menu>
);
};
export const WalletButton: React.FC = () => {
const { state } = useWalletStore();
if (state === 'connected') {
return <WalletMenu />;
}
return <ConnectionMenu />;
};

View File

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

View File

@ -1,16 +1,32 @@
import {
amber,
amberDark,
blue,
blueDark,
gray,
grayDark,
green,
greenDark,
red,
redDark,
slate,
slateDark,
} from '@radix-ui/colors';
//not in usage yet
export const colors = {
custom: {
gray: {
100: '#4e4e4e',
200: '#282824',
},
black: '#161616',
blue: {
100: '#1d4ed8',
},
white: {
50: '#c5c5c50a',
100: '#f3f3f36b',
},
},
...gray,
...slate,
...blue,
...red,
...green,
...amber,
};
export const darkColors = {
...grayDark,
...slateDark,
...blueDark,
...redDark,
...greenDark,
...amberDark,
};

View File

@ -1 +1,8 @@
export * from './media';
export * from './colors';
export * from './radii';
export * from './shadows';
export * from './sizes';
export * from './spacing';
export * from './typography';
export * from './z-indices';

View File

@ -1,24 +1,2 @@
import { extendTheme } from '@chakra-ui/react';
import { colors } from './foundations';
const appTheme = {
styles: {
global: {
body: {
color: 'rgba(255, 255, 255)',
bg: 'custom.black',
margin: '25px 50px',
},
},
},
fonts: {
heading: 'Nunito Sans,Helvetica,Arial,Lucida,sans-serif',
body: 'Nunito Sans,Helvetica,Arial,Lucida,sans-serif',
},
sizes: {
modalHeight: '345px',
},
colors,
};
export const theme = extendTheme(appTheme);
export * from './themes';
export * from './foundations';

View File

@ -1,32 +0,0 @@
import {
amber,
amberDark,
blue,
blueDark,
gray,
grayDark,
green,
greenDark,
red,
redDark,
slate,
slateDark,
} from '@radix-ui/colors';
//not in usage yet
export const colors = {
...gray,
...slate,
...blue,
...red,
...green,
...amber,
};
export const darkColors = {
...grayDark,
...slateDark,
...blueDark,
...redDark,
...greenDark,
...amberDark,
};

View File

@ -1,8 +0,0 @@
export * from './media';
export * from './colors';
export * from './radii';
export * from './shadows';
export * from './sizes';
export * from './spacing';
export * from './typography';
export * from './z-indices';

View File

@ -1,2 +0,0 @@
export * from './themes';
export * from './foundations';

View File

@ -2,7 +2,7 @@
import { createStitches, DefaultThemeMap } from '@stitches/react';
import type { ConfigType } from '@stitches/react/types/config';
import { allToNegative } from '../../utils';
import { allToNegative } from '../utils';
import {
colors,
darkColors,

View File

@ -1,135 +0,0 @@
import { useSearchParams } from 'react-router-dom';
import { useQuery } from 'react-query';
import {
Accordion,
Box,
Card,
CardBody,
Flex,
Heading,
Link,
VStack,
} from '@chakra-ui/react';
import {
HomeButton,
ImagePreview,
AccordionItem,
Loading,
AttributesDetail,
} from '@/components';
import { ExternalLinkIcon } from '@chakra-ui/icons';
import { ErrorScreen } from '@/views';
import { SiteNFTDetail } from '@/types';
import { FleekERC721 } from '@/integrations';
export const MintedSiteDetail = () => {
const [searchParams] = useSearchParams();
const tokenIdParam = searchParams.get('tokenId');
//TODO handle response type
const { data, status } = useQuery<SiteNFTDetail>(
`fetchDetail${tokenIdParam}`,
async () => FleekERC721.tokenMetadata(Number(tokenIdParam))
);
if (status === 'loading') {
return <Loading />;
}
if (status === 'error') {
return <ErrorScreen />;
}
const { owner, name, description, image, externalUrl, attributes } =
data as SiteNFTDetail;
return (
<>
<Flex width="full" align="center" justifyContent="center" mt="30px">
<Box width={{ base: '100%' }}>
<HomeButton />
<Box
flexDirection="row"
display="flex"
justifyContent="space-evenly"
mt={10}
>
<Box mr={5}>
<Box
display="flex"
flexDirection="row"
alignItems="flex-end"
mb={5}
>
<Heading mr={5}>{name}</Heading>
</Box>
<Card backgroundColor="transparent" border="1px">
<CardBody padding="1px 8px 10px 8px">
<Accordion defaultIndex={[0, 1]} allowMultiple width="45vw">
<AccordionItem
heading="Description"
minH={120}
maxH="auto"
children={
<p
style={{
whiteSpace: 'pre-line',
}}
>
{description}
</p>
}
/>
<AccordionItem
heading="Attributes"
children={
<AttributesDetail
owner={owner}
attributes={attributes}
tokendId={tokenIdParam as string}
/>
}
padding="16px"
/>
</Accordion>
<Box ml={5} mt={2}>
<Link href={externalUrl} isExternal>
Visit site <ExternalLinkIcon mx="2px" />
</Link>
</Box>
</CardBody>
</Card>
</Box>
<VStack alignItems="flex-start">
<Box
border="1px"
width="-webkit-fill-available"
padding="5px 10px"
borderTopRadius={10}
>
<Heading size="md">Preview</Heading>
</Box>
<Box
mt="0px !important"
boxSize="md"
border="1px"
padding={10}
borderRadius={20}
borderTopRadius={0}
boxShadow="12px 10px 14px 6px #868686d1"
>
<ImagePreview
image={image}
width="auto"
height="auto"
maxW="100%"
maxH="100%"
/>
</Box>
</VStack>
</Box>
</Box>
</Flex>
</>
);
};

View File

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

View File

@ -1,9 +0,0 @@
import { Flex, Heading } from '@chakra-ui/react';
export const ErrorScreen = () => {
return (
<Flex justifyContent="center" height="80vh" alignItems="center">
<Heading size="md">Something went wrong</Heading>
</Flex>
);
};

View File

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

View File

@ -1,18 +1,10 @@
import React from 'react';
import { Heading, Button } from '@chakra-ui/react';
import { Link } from 'react-router-dom';
import { Flex } from '@chakra-ui/react';
import { ListSites } from './list';
export const Home = () => {
return (
<Flex flexDirection="column" alignItems="center">
<Heading marginTop="80px">Welcome to Sites as NFTs by Fleek</Heading>
<Button as={Link} to="/mint-site" mt="20px" mb="50px">
Mint your site
</Button>
<ListSites />
</Flex>
);
};
import { Flex } from '@/components';
import React from 'react';
export const Home = () => {
return (
<Flex css={{ justifyContent: 'center' }}>
<h1>Home</h1>
</Flex>
);
};

View File

@ -1,4 +1 @@
export * from './home';
export * from './mint-site';
export * from './detail';
export * from './error-screen';

View File

@ -1,2 +0,0 @@
export * from './mint-site';
export * from './mint-site.utils';

View File

@ -1,262 +0,0 @@
import React, { useCallback } from 'react';
import {
Heading,
Flex,
Box,
FormControl,
FormLabel,
Button,
FormErrorMessage,
Textarea,
Grid,
GridItem,
VStack,
InputGroup,
Input,
InputRightElement,
InputProps,
} from '@chakra-ui/react';
import { Formik, Field, useFormikContext } from 'formik';
import { getRepoAndCommit } from '@/utils';
import { validateFields } from './mint-site.utils';
import { HomeButton, InputFieldForm } from '@/components';
import { FleekERC721 } from '@/integrations';
import { useWalletStore } from '@/store';
import { useToast } from '@/hooks';
interface FormValues {
name: string;
description: string;
githubCommit: string;
ownerAddress: string;
externalUrl: string;
image: string;
ens?: string;
}
const initialValues = {
name: '',
description: '',
githubCommit: '',
ownerAddress: '',
externalUrl: '',
image: '',
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();
const handleSubmitForm = useCallback(
async (values: FormValues) => {
const {
name,
description,
githubCommit,
ownerAddress,
externalUrl,
image,
ens,
} = values;
const { repo, commit_hash } = getRepoAndCommit(githubCommit);
try {
if (!provider) throw new Error('No provider found');
await FleekERC721.mint(
{
name,
description,
owner: ownerAddress,
externalUrl,
image,
ens,
commitHash: commit_hash,
repo,
},
provider
);
setToastInfo({
title: 'Success!',
description: 'Your site has been minted.',
status: 'success',
});
} catch (err) {
setToastInfo({
title: 'Error!',
description:
'We had an error while minting your site. Please try again later',
status: 'error',
});
}
},
[provider]
);
return (
<>
<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="Name"
fieldName="name"
mr={5}
error={errors.name}
isInvalid={!!errors.name && touched.name}
isRequired
/>
<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

@ -1,35 +0,0 @@
import { isValidImageUrl, isValidUrl } from '@/utils';
import { ethers } from 'ethers';
import { FormikValues } from 'formik';
export const validateFields = (values: FormikValues) => {
const errors: FormikValues = {};
if (!values.name) {
errors.name = 'Name cannot be empty';
}
if (!values.description) {
errors.description = 'Description cannot be empty';
}
if (!values.githubCommit) {
errors.githubCommit = 'Github commit cannot be empty';
} else if (!isValidUrl(values.githubCommit)) {
errors.githubCommit = 'Github commit is not a valid url';
}
if (!values.ownerAddress) {
errors.ownerAddress = 'Owner address cannot be empty';
} else if (!ethers.utils.isAddress(values.ownerAddress)) {
errors.ownerAddress = 'Owner address is not a valid address';
}
if (!values.externalUrl) {
errors.externalUrl = 'External url cannot be empty';
} else if (!isValidUrl(values.externalUrl)) {
errors.externalUrl = 'External url is not a valid url';
}
if (!values.image) {
errors.image = 'Image cannot be empty';
} else if (!isValidImageUrl(values.image)) {
errors.image = 'Image url is not a valid url';
}
//TODO check if ENS is a valid ens name
return errors;
};

File diff suppressed because it is too large Load Diff