feat: UI refactor card header (#250)

* reactor: refactor card header

* feat: custom card component

* chore: animation on create ap success card

* chore: remove old file

* chore: global responsiveness (#251)

* Update ui/src/components/card/card.tsx

Co-authored-by: Felipe Mendes <zo.fmendes@gmail.com>

---------

Co-authored-by: Felipe Mendes <zo.fmendes@gmail.com>
This commit is contained in:
Camila Sosa Morales 2023-05-10 11:44:40 -03:00 committed by GitHub
parent d2bad871c3
commit cda25b9a3b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 349 additions and 343 deletions

View File

@ -11,10 +11,8 @@ export abstract class CardStyles {
borderWidth: '$default', borderWidth: '$default',
}); });
static readonly Heading = styled('h3', { static readonly Header = styled('div', {
color: '$slate12', width: '$full',
fontSize: '$xl',
fontWeight: '$medium',
}); });
static readonly Body = styled('div', { static readonly Body = styled('div', {

View File

@ -1,7 +1,6 @@
/* eslint-disable react/display-name */ /* eslint-disable react/display-name */
import React, { forwardRef } from 'react'; import React, { forwardRef } from 'react';
import { Flex } from '../layout';
import { CardStyles } from './card.styles'; import { CardStyles } from './card.styles';
export abstract class Card { export abstract class Card {
@ -15,18 +14,12 @@ export abstract class Card {
} }
); );
static readonly Heading = forwardRef<HTMLHeadingElement, Card.HeadingProps>( static readonly Header = CardStyles.Header;
({ title, leftIcon, rightIcon, css, ...props }, ref) => { ({ children, ...props }, ref) => {
return ( return (
<Flex css={{ justifyContent: 'space-between', ...css }}> <CardStyles.Header ref={ref} {...props}>
<Flex> {children}
{leftIcon} </CardStyles.Header>
<CardStyles.Heading ref={ref} {...props}>
{title}
</CardStyles.Heading>
</Flex>
{rightIcon}
</Flex>
); );
} }
); );
@ -57,12 +50,7 @@ export namespace Card {
typeof CardStyles.Container typeof CardStyles.Container
>; >;
export type HeadingProps = { export type HeadingProps = React.ComponentProps<typeof CardStyles.Header>;
title: string;
css?: React.CSSProperties;
leftIcon?: React.ReactNode;
rightIcon?: React.ReactNode;
} & React.ComponentProps<typeof CardStyles.Heading>;
export type BodyProps = React.ComponentProps<typeof CardStyles.Body>; export type BodyProps = React.ComponentProps<typeof CardStyles.Body>;

View File

@ -0,0 +1,18 @@
import { Card, Flex } from '@/components';
import { styled } from '@/theme';
export const CustomCardStyles = {
Container: styled(Card.Container, {
maxWidth: '$107h',
}),
Title: {
Container: styled(Flex, {
justifyContent: 'space-between',
}),
Text: styled('h3', {
color: '$slate12',
fontSize: '$xl',
fontWeight: '$medium',
}),
},
};

View File

@ -0,0 +1,65 @@
import { Card, Flex, Icon, IconButton } from '@/components';
import { forwardStyledRef } from '@/theme';
import { CardStyles } from '../card.styles';
import { CustomCardStyles as S } from './custom-card.styles';
export const CustomCardContainer = S.Container;
export abstract class CustomCardHeader {
static readonly Default = forwardStyledRef<
HTMLHeadingElement,
CustomCard.HeadingProps
>(({ title, onClickBack, ...props }, ref) => {
return (
<Card.Header ref={ref} {...props}>
<S.Title.Container>
<Flex css={{ gap: '$2' }}>
{onClickBack && (
<IconButton
aria-label="back"
colorScheme="gray"
variant="link"
icon={<Icon name="back" />}
onClick={onClickBack}
/>
)}
<S.Title.Text>{title}</S.Title.Text>
</Flex>
<IconButton
aria-label="Add"
colorScheme="gray"
variant="link"
icon={<Icon name="info" />}
/>
</S.Title.Container>
</Card.Header>
);
});
static readonly Success = forwardStyledRef<
HTMLHeadingElement,
Omit<CustomCard.HeadingProps, 'onClickBack'>
>(({ title, ...props }, ref) => {
return (
<Card.Header ref={ref} {...props}>
<Flex css={{ gap: '$2' }}>
<Icon
name="check-circle"
css={{ color: '$green11', fontSize: '$xl' }}
/>
<S.Title.Text>{title}</S.Title.Text>
</Flex>
</Card.Header>
);
});
}
export namespace CustomCard {
export type ContainerProps = React.ComponentProps<typeof S.Container>;
export type HeadingProps = {
title: string;
onClickBack?: () => void;
} & React.ComponentProps<typeof CardStyles.Header>;
}

View File

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

View File

@ -1 +1,2 @@
export * from './card'; export * from './card';
export * from './custom-card';

View File

@ -10,8 +10,11 @@ export abstract class PageStyles {
width: '100%', width: '100%',
minHeight: '85vh', minHeight: '85vh',
maxWidth: '$6xl', maxWidth: '$6xl',
padding: '0 $6', padding: '$6',
margin: '0 auto', margin: '0 auto',
display: 'grid',
'@md': {
padding: '0 $6',
},
}); });
} }

View File

@ -1,3 +1,4 @@
import { Text } from '@/components';
import { keyframes, styled } from '@/theme'; import { keyframes, styled } from '@/theme';
const Loading = keyframes({ const Loading = keyframes({
@ -13,7 +14,7 @@ const Loading = keyframes({
}); });
export const ResolvedAddressStyles = { export const ResolvedAddressStyles = {
Container: styled('span', { Container: styled(Text, {
'&[data-loading="true"]': { '&[data-loading="true"]': {
animation: `${Loading} 1s ease-in-out infinite`, animation: `${Loading} 1s ease-in-out infinite`,
}, },

View File

@ -1,5 +1,6 @@
export const media = { export const media = {
// Breakpoints // Breakpoints
xs: '(min-width: 375px)',
sm: '(min-width: 640px)', sm: '(min-width: 640px)',
md: '(min-width: 768px)', md: '(min-width: 768px)',
lg: '(min-width: 1024px)', lg: '(min-width: 1024px)',

View File

@ -1,4 +1,10 @@
import { Card, Flex, Icon, IconButton, Stepper } from '@/components'; import {
Card,
CustomCardContainer,
CustomCardHeader,
Flex,
Stepper,
} from '@/components';
import { CreateAccessPointFormBody } from './create-ap-form-body'; import { CreateAccessPointFormBody } from './create-ap-form-body';
@ -6,28 +12,8 @@ export const CreateAccessPointForm: React.FC = () => {
const { prevStep } = Stepper.useContext(); const { prevStep } = Stepper.useContext();
return ( return (
<Card.Container css={{ maxWidth: '$107h' }}> <CustomCardContainer>
<Card.Heading <CustomCardHeader.Default title="Enter Domain" onClickBack={prevStep} />
title="Enter Domain"
leftIcon={
<IconButton
aria-label="Add"
colorScheme="gray"
variant="link"
icon={<Icon name="back" />}
css={{ mr: '$2' }}
onClick={prevStep}
/>
}
rightIcon={
<IconButton
aria-label="Add"
colorScheme="gray"
variant="link"
icon={<Icon name="info" />}
/>
}
/>
<Card.Body> <Card.Body>
<Flex <Flex
css={{ css={{
@ -38,6 +24,6 @@ export const CreateAccessPointForm: React.FC = () => {
<CreateAccessPointFormBody /> <CreateAccessPointFormBody />
</Flex> </Flex>
</Card.Body> </Card.Body>
</Card.Container> </CustomCardContainer>
); );
}; };

View File

@ -1,6 +1,6 @@
import { useEffect, useMemo } from 'react'; import { useEffect, useMemo } from 'react';
import { Button, Card, Grid, SpinnerDot, Stepper, Text } from '@/components'; import { Button, Card, Flex, SpinnerDot, Stepper, Text } from '@/components';
import { bunnyCDNActions, useAppDispatch, useBunnyCDNStore } from '@/store'; import { bunnyCDNActions, useAppDispatch, useBunnyCDNStore } from '@/store';
import { useAccessPointFormContext } from '../ap-form-step'; import { useAccessPointFormContext } from '../ap-form-step';
@ -49,9 +49,10 @@ export const APRecordCardBody: React.FC = () => {
</Text> </Text>
</Card.Text> </Card.Text>
) : ( ) : (
<Grid <Flex
css={{ css={{
rowGap: '$6', gap: '$6',
flexDirection: 'column',
}} }}
> >
<Text> <Text>
@ -73,7 +74,7 @@ export const APRecordCardBody: React.FC = () => {
> >
I added the record I added the record
</Button> </Button>
</Grid> </Flex>
)} )}
</Card.Body> </Card.Body>
); );

View File

@ -1,29 +1,9 @@
import { Card, Icon, IconButton, Stepper } from '@/components'; import { CustomCardHeader, Stepper } from '@/components';
export const APRecordCardHeader: React.FC = () => { export const APRecordCardHeader: React.FC = () => {
const { prevStep } = Stepper.useContext(); const { prevStep } = Stepper.useContext();
return ( return (
<Card.Heading <CustomCardHeader.Default title="Create Record" onClickBack={prevStep} />
title="Create Record"
leftIcon={
<IconButton
aria-label="Add"
colorScheme="gray"
variant="link"
icon={<Icon name="back" />}
css={{ mr: '$2' }}
onClick={prevStep}
/>
}
rightIcon={
<IconButton
aria-label="Add"
colorScheme="gray"
variant="link"
icon={<Icon name="info" />}
/>
}
/>
); );
}; };

View File

@ -1,13 +1,13 @@
import { Card } from '@/components'; import { CustomCardContainer } from '@/components';
import { APRecordCardBody } from './ap-record-body'; import { APRecordCardBody } from './ap-record-body';
import { APRecordCardHeader } from './ap-record-header'; import { APRecordCardHeader } from './ap-record-header';
export const APRecordStep: React.FC = () => { export const APRecordStep: React.FC = () => {
return ( return (
<Card.Container css={{ maxWidth: '$107h' }}> <CustomCardContainer>
<APRecordCardHeader /> <APRecordCardHeader />
<APRecordCardBody /> <APRecordCardBody />
</Card.Container> </CustomCardContainer>
); );
}; };

View File

@ -5,9 +5,9 @@ import { useAccount } from 'wagmi';
import { import {
Button, Button,
Card, Card,
CustomCardContainer,
CustomCardHeader,
Flex, Flex,
Icon,
IconButton,
ResolvedAddress, ResolvedAddress,
Stepper, Stepper,
Text, Text,
@ -40,7 +40,9 @@ export const AccessPointDataFragment: React.FC = () => {
label="Owner" label="Owner"
value={ value={
address ? ( address ? (
<ResolvedAddress truncated={false}>{address || ''}</ResolvedAddress> <ResolvedAddress truncated={false} ellipsis>
{address}
</ResolvedAddress>
) : ( ) : (
'Please connect to wallet' 'Please connect to wallet'
) )
@ -111,28 +113,8 @@ export const CreateAccessPointPreview: React.FC = () => {
}, [writeStatus, transactionStatus]); }, [writeStatus, transactionStatus]);
return ( return (
<Card.Container css={{ maxWidth: '$107h' }}> <CustomCardContainer>
<Card.Heading <CustomCardHeader.Default title="Review Details" onClickBack={prevStep} />
title="Review Details"
leftIcon={
<IconButton
aria-label="Add"
colorScheme="gray"
variant="link"
icon={<Icon name="back" />}
css={{ mr: '$2' }}
onClick={prevStep}
/>
}
rightIcon={
<IconButton
aria-label="Add"
colorScheme="gray"
variant="link"
icon={<Icon name="info" />}
/>
}
/>
<Card.Body> <Card.Body>
<Flex css={{ flexDirection: 'column', gap: '$6' }}> <Flex css={{ flexDirection: 'column', gap: '$6' }}>
<AccessPointDataFragment /> <AccessPointDataFragment />
@ -148,6 +130,6 @@ export const CreateAccessPointPreview: React.FC = () => {
</Button> </Button>
</Flex> </Flex>
</Card.Body> </Card.Body>
</Card.Container> </CustomCardContainer>
); );
}; };

View File

@ -0,0 +1,13 @@
import { CustomCardContainer } from '@/components';
import { keyframes, styled } from '@/theme';
const CardKeyFrames = keyframes({
'0%': { opacity: 0 },
'100%': { opacity: 1 },
});
export const CreateApSuccessStyles = {
Container: styled(CustomCardContainer, {
animation: `${CardKeyFrames} 0.5s ease-in-out 0s`,
}),
};

View File

@ -1,29 +1,14 @@
import { Button, Card, Flex, Icon, IconButton, Text } from '@/components'; import { Button, Card, CustomCardHeader, Flex, Icon, Text } from '@/components';
import { CreateAccessPoint } from './create-ap.context'; import { CreateAccessPoint } from '../create-ap.context';
import { AccessPointDataFragment } from './create-ap-preview'; import { AccessPointDataFragment } from '../create-ap-preview';
import { CreateApSuccessStyles as S } from './create-ap-success.styles';
export const CreateAccessPointSuccess: React.FC = () => { export const CreateAccessPointSuccess: React.FC = () => {
const { nfa } = CreateAccessPoint.useContext(); const { nfa } = CreateAccessPoint.useContext();
return ( return (
<Card.Container css={{ maxWidth: '$107h' }}> <S.Container>
<Card.Heading <CustomCardHeader.Success title="Hosting Successful" />
title="Hosting Successful"
leftIcon={
<Icon
name="check-circle"
css={{ color: '$green11', fontSize: '$xl', mr: '$2' }}
/>
}
rightIcon={
<IconButton
aria-label="Add"
colorScheme="gray"
variant="link"
icon={<Icon name="info" />}
/>
}
/>
<Card.Body> <Card.Body>
<Flex css={{ flexDirection: 'column', gap: '$6' }}> <Flex css={{ flexDirection: 'column', gap: '$6' }}>
<Text css={{ fontSize: '$sm', color: '$slate11' }}> <Text css={{ fontSize: '$sm', color: '$slate11' }}>
@ -42,6 +27,6 @@ export const CreateAccessPointSuccess: React.FC = () => {
</Flex> </Flex>
</Flex> </Flex>
</Card.Body> </Card.Body>
</Card.Container> </S.Container>
); );
}; };

View File

@ -0,0 +1 @@
export * from './create-ap-success';

View File

@ -1,7 +1,6 @@
import { Flex, Text } from '@/components';
import { styled } from '@/theme'; import { styled } from '@/theme';
import { Flex } from '../../../components/layout';
export const DisplayTextStyles = { export const DisplayTextStyles = {
Container: styled(Flex, { Container: styled(Flex, {
flexDirection: 'column', flexDirection: 'column',
@ -13,7 +12,7 @@ export const DisplayTextStyles = {
fontSize: '$xs', fontSize: '$xs',
//TODO add variants //TODO add variants
}), }),
Input: styled('span', { Input: styled(Text, {
backgroundColor: '$slate1', backgroundColor: '$slate1',
borderColor: '$slate1', borderColor: '$slate1',
color: '$slate12', color: '$slate12',

View File

@ -11,7 +11,7 @@ export const DisplayText: React.FC<DisplayTextProps> = ({
return ( return (
<S.Container> <S.Container>
<S.Label>{label}</S.Label> <S.Label>{label}</S.Label>
<S.Input>{value}</S.Input> <S.Input ellipsis>{value}</S.Input>
</S.Container> </S.Container>
); );
}; };

View File

@ -1,5 +1,10 @@
import { Card, Grid, Stepper } from '@/components'; import {
import { MintCardHeader } from '@/views/mint/mint-card'; Card,
CustomCardContainer,
CustomCardHeader,
Flex,
Stepper,
} from '@/components';
import { GithubButton } from './github-button'; import { GithubButton } from './github-button';
@ -7,10 +12,10 @@ export const GithubConnect: React.FC = () => {
const { prevStep } = Stepper.useContext(); const { prevStep } = Stepper.useContext();
return ( return (
<Card.Container css={{ maxWidth: '$107h' }}> <CustomCardContainer>
<MintCardHeader title="Connect GitHub" onClickBack={prevStep} /> <CustomCardHeader.Default title="Connect GitHub" onClickBack={prevStep} />
<Card.Body> <Card.Body>
<Grid css={{ rowGap: '$6' }}> <Flex css={{ gap: '$6', flexDirection: 'column' }}>
<GithubButton /> <GithubButton />
<Card.Text <Card.Text
css={{ css={{
@ -24,8 +29,8 @@ export const GithubConnect: React.FC = () => {
After connecting your GitHub, your repositories will show here. After connecting your GitHub, your repositories will show here.
</span> </span>
</Card.Text> </Card.Text>
</Grid> </Flex>
</Card.Body> </Card.Body>
</Card.Container> </CustomCardContainer>
); );
}; };

View File

@ -1,5 +1,5 @@
import { CustomCardHeader } from '@/components';
import { Mint } from '@/views/mint/mint.context'; import { Mint } from '@/views/mint/mint.context';
import { MintCardHeader } from '@/views/mint/mint-card';
import { useMintFormContext } from '@/views/mint/nfa-step/form-step'; import { useMintFormContext } from '@/views/mint/nfa-step/form-step';
export const RepoConfigurationHeader: React.FC = () => { export const RepoConfigurationHeader: React.FC = () => {
@ -22,7 +22,7 @@ export const RepoConfigurationHeader: React.FC = () => {
}; };
return ( return (
<MintCardHeader <CustomCardHeader.Default
title="Configure Repository" title="Configure Repository"
onClickBack={handlePrevStepClick} onClickBack={handlePrevStepClick}
/> />

View File

@ -1,13 +1,13 @@
import { Card } from '@/components'; import { CustomCardContainer } from '@/components';
import { RepoConfigurationBody } from './repo-configuration-body'; import { RepoConfigurationBody } from './repo-configuration-body';
import { RepoConfigurationHeader } from './repo-configuration-header'; import { RepoConfigurationHeader } from './repo-configuration-header';
export const GithubRepoConfiguration: React.FC = () => { export const GithubRepoConfiguration: React.FC = () => {
return ( return (
<Card.Container css={{ minWidth: '17rem', maxWidth: '$107h' }}> <CustomCardContainer css={{ minWidth: '17rem' }}>
<RepoConfigurationHeader /> <RepoConfigurationHeader />
<RepoConfigurationBody /> <RepoConfigurationBody />
</Card.Container> </CustomCardContainer>
); );
}; };

View File

@ -1,13 +1,21 @@
import { Card, Flex, Icon } from '@/components'; import {
Card,
CustomCardContainer,
CustomCardHeader,
Flex,
Icon,
} from '@/components';
import { styled } from '@/theme'; import { styled } from '@/theme';
export const GithubRepositorySelectionStyles = { export const GithubRepositorySelectionStyles = {
Card: { Card: {
Wrapper: styled(Card.Container, { Wrapper: styled(CustomCardContainer, {
maxWidth: '$107h',
maxHeight: '$95h', maxHeight: '$95h',
pr: '$3h', pr: '$3h',
}), }),
Header: styled(CustomCardHeader.Default, {
pr: '$3h',
}),
Body: styled(Card.Body, { Body: styled(Card.Body, {
pt: '$4', pt: '$4',
}), }),

View File

@ -1,14 +1,6 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { import { Flex, InputGroup, InputGroupText, Spinner } from '@/components';
Card,
Flex,
Icon,
IconButton,
InputGroup,
InputGroupText,
Spinner,
} from '@/components';
import { useDebounce } from '@/hooks/use-debounce'; import { useDebounce } from '@/hooks/use-debounce';
import { useGithubStore } from '@/store'; import { useGithubStore } from '@/store';
import { Mint } from '@/views/mint/mint.context'; import { Mint } from '@/views/mint/mint.context';
@ -55,27 +47,9 @@ export const GithubRepositoryConnection: React.FC = () => {
return ( return (
<S.Card.Wrapper> <S.Card.Wrapper>
<Card.Heading <S.Card.Header
title="Select Repository" title="Select Repository"
css={{ pr: '$3h' }} onClickBack={handlePrevStepClick}
leftIcon={
<IconButton
aria-label="back"
colorScheme="gray"
variant="link"
icon={<Icon name="back" />}
css={{ mr: '$2' }}
onClick={handlePrevStepClick}
/>
}
rightIcon={
<IconButton
aria-label="info"
colorScheme="gray"
variant="link"
icon={<Icon name="info" />}
/>
}
/> />
<S.Card.Body> <S.Card.Body>
<S.Container> <S.Container>

View File

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

View File

@ -1,35 +0,0 @@
import { Card, Icon, IconButton } from '@/components';
type MintCardHeaderProps = {
title: string;
onClickBack: () => void;
};
export const MintCardHeader: React.FC<MintCardHeaderProps> = ({
title,
onClickBack,
}: MintCardHeaderProps) => {
return (
<Card.Heading
title={title}
leftIcon={
<IconButton
aria-label="Add"
colorScheme="gray"
variant="link"
icon={<Icon name="back" />}
css={{ mr: '$2' }}
onClick={onClickBack}
/>
}
rightIcon={
<IconButton
aria-label="Add"
colorScheme="gray"
variant="link"
icon={<Icon name="info" />}
/>
}
/>
);
};

View File

@ -6,7 +6,14 @@ export const MintStyles = {
height: '100%', height: '100%',
justifyContent: 'center', justifyContent: 'center',
'@media (min-width: 1024px)': { '@md': {
//to align on center
position: 'absolute',
top: '50%',
transform: 'translateY(-50%)',
},
'@lg': {
flexDirection: 'row', flexDirection: 'row',
}, },
}), }),

View File

@ -1,11 +1,17 @@
import { useAccount } from 'wagmi'; import { useAccount } from 'wagmi';
import { Button, Card, Grid, Stepper } from '@/components'; import {
Button,
Card,
CustomCardContainer,
CustomCardHeader,
Flex,
Stepper,
} from '@/components';
import { AppLog } from '@/utils'; import { AppLog } from '@/utils';
import { parseColorToNumber } from '@/utils/color'; import { parseColorToNumber } from '@/utils/color';
import { Mint } from '../../mint.context'; import { Mint } from '../../mint.context';
import { MintCardHeader } from '../../mint-card';
import { import {
AppDescriptionField, AppDescriptionField,
AppNameField, AppNameField,
@ -78,20 +84,24 @@ export const MintFormStep: React.FC = () => {
}; };
return ( return (
<Card.Container css={{ maxWidth: '$107h' }}> <CustomCardContainer>
<MintCardHeader title="NFA Details" onClickBack={handlePrevStep} /> <CustomCardHeader.Default
title="NFA Details"
onClickBack={handlePrevStep}
/>
<Card.Body> <Card.Body>
<Grid <Flex
css={{ css={{
rowGap: '$6', gap: '$6',
flexDirection: 'column',
}} }}
> >
<Grid css={{ rowGap: '$4' }}> <Flex css={{ gap: '$4', flexDirection: 'column' }}>
<AppNameField /> <AppNameField />
<AppDescriptionField /> <AppDescriptionField />
<EnsDomainField /> <EnsDomainField />
<LogoField /> <LogoField />
</Grid> </Flex>
<Button <Button
disabled={!isValid} disabled={!isValid}
colorScheme="blue" colorScheme="blue"
@ -100,8 +110,8 @@ export const MintFormStep: React.FC = () => {
> >
Continue Continue
</Button> </Button>
</Grid> </Flex>
</Card.Body> </Card.Body>
</Card.Container> </CustomCardContainer>
); );
}; };

View File

@ -0,0 +1,74 @@
import { useQuery } from '@apollo/client';
import { useEffect, useMemo } from 'react';
import { getVerifiersDocument } from '@/../.graphclient';
import { Form, ResolvedAddress } from '@/components';
import { useENSStore } from '@/store';
import { useMintFormContext } from '../form-step';
// TODO: remove mocked items after graphql api is fixed
const mockedItems = [
'0xdBb04e00D5ec8C9e3aeF811D315Ee7C147c5DBFD',
'0x7ED735b7095C05d78dF169F991f2b7f1A1F1A049',
];
export const SelectVerifier: React.FC = () => {
const {
form: { verifier },
} = useMintFormContext();
const {
value: [selectedVerifier, setSelectedVerifier],
} = verifier;
const { addressMap } = useENSStore();
const { data } = useQuery(getVerifiersDocument);
const items = useMemo(() => {
if (!data) return [];
const verifiers = data.verifiers
.map<string>((verifier) => verifier.id.toString())
.concat(mockedItems);
return verifiers.map((verifier) => ({
address: verifier,
ens: addressMap[verifier]?.value,
}));
}, [data, addressMap]);
useEffect(() => {
if (!selectedVerifier && items.length > 0) {
setSelectedVerifier(items[0].address);
}
}, [selectedVerifier, setSelectedVerifier, items]);
return (
<Form.Field context={verifier}>
<Form.Combobox
items={items}
handleValue={(item) => item.address}
queryKey={['address', 'ens']}
>
{({ Field, Options }) => (
<>
<Field>
{(selected) =>
selected ? (
<ResolvedAddress>{selected.address}</ResolvedAddress>
) : (
'Select a Verifier'
)
}
</Field>
<Options>
{(item) => <ResolvedAddress>{item.address}</ResolvedAddress>}
</Options>
</>
)}
</Form.Combobox>
</Form.Field>
);
};

View File

@ -0,0 +1,22 @@
import { Card, Flex, Text } from '@/components';
import { styled } from '@/theme';
export const VerifyNfaStepStyles = {
Body: {
Container: styled(Flex, {
flexDirection: 'column',
gap: '$6',
}),
Text: styled(Text, {
color: '$slate11',
fontSize: '$sm',
}),
VerifyContainer: styled(Card.Text, {
p: '$4',
textAlign: 'left',
flexDirection: 'row',
justifyContent: 'space-between',
borderRadius: '$lg',
}),
},
};

View File

@ -1,88 +1,17 @@
import { useQuery } from '@apollo/client';
import { useEffect, useMemo } from 'react';
import { getVerifiersDocument } from '@/../.graphclient';
import { import {
Button, Button,
Card, Card,
Flex, CustomCardContainer,
Form, CustomCardHeader,
ResolvedAddress,
Stepper, Stepper,
Switch, Switch,
Text, Text,
} from '@/components'; } from '@/components';
import { useENSStore } from '@/store';
import { Mint } from '../../mint.context'; import { Mint } from '../../mint.context';
import { MintCardHeader } from '../../mint-card';
import { useMintFormContext } from '../form-step'; import { useMintFormContext } from '../form-step';
import { SelectVerifier } from './select-verifier';
// TODO: remove mocked items after graphql api is fixed import { VerifyNfaStepStyles as S } from './verify-nfa-step.styles';
const mockedItems = [
'0xdBb04e00D5ec8C9e3aeF811D315Ee7C147c5DBFD',
'0x7ED735b7095C05d78dF169F991f2b7f1A1F1A049',
];
const SelectVerifier: React.FC = () => {
const {
form: { verifier },
} = useMintFormContext();
const {
value: [selectedVerifier, setSelectedVerifier],
} = verifier;
const { addressMap } = useENSStore();
const { data } = useQuery(getVerifiersDocument);
const items = useMemo(() => {
if (!data) return [];
const verifiers = data.verifiers
.map<string>((verifier) => verifier.id.toString())
.concat(mockedItems);
return verifiers.map((verifier) => ({
address: verifier,
ens: addressMap[verifier]?.value,
}));
}, [data, addressMap]);
useEffect(() => {
if (!selectedVerifier && items.length > 0) {
setSelectedVerifier(items[0].address);
}
}, [selectedVerifier, setSelectedVerifier, items]);
return (
<Form.Field context={verifier}>
<Form.Combobox
items={items}
handleValue={(item) => item.address}
queryKey={['address', 'ens']}
>
{({ Field, Options }) => (
<>
<Field>
{(selected) =>
selected ? (
<ResolvedAddress>{selected.address}</ResolvedAddress>
) : (
'Select a Verifier'
)
}
</Field>
<Options>
{(item) => <ResolvedAddress>{item.address}</ResolvedAddress>}
</Options>
</>
)}
</Form.Combobox>
</Form.Field>
);
};
export const VerifyNFAStep: React.FC = () => { export const VerifyNFAStep: React.FC = () => {
const { prevStep } = Stepper.useContext(); const { prevStep } = Stepper.useContext();
@ -100,30 +29,22 @@ export const VerifyNFAStep: React.FC = () => {
}; };
return ( return (
<Card.Container css={{ maxWidth: '$107h' }}> <CustomCardContainer>
<MintCardHeader title="Verify NFA" onClickBack={prevStep} /> <CustomCardHeader.Default title="Verify NFA" onClickBack={prevStep} />
<Card.Body> <Card.Body>
<Flex css={{ flexDirection: 'column', gap: '$6' }}> <S.Body.Container>
<Text css={{ color: '$slate11', fontSize: '$sm' }}> <S.Body.Text>
Below you can allow Fleek to be added as a controller to your NFA. Below you can allow Fleek to be added as a controller to your NFA.
This will allow Fleek to automatically verify your NFA and update This will allow Fleek to automatically verify your NFA and update
builds and other metadata. It will not allow Fleek to transfer or builds and other metadata. It will not allow Fleek to transfer or
burn your NFT. You can change this setting later on your NFA but burn your NFT. You can change this setting later on your NFA but
adding it now will save you a transaction in the future. We adding it now will save you a transaction in the future. We
recommend it so that your users can get verified NFAs. recommend it so that your users can get verified NFAs.
</Text> </S.Body.Text>
<Card.Text <S.Body.VerifyContainer>
css={{
p: '$4',
textAlign: 'left',
flexDirection: 'row',
justifyContent: 'space-between',
borderRadius: '$lg',
}}
>
<Text css={{ color: '$slate12' }}>Verify NFA</Text> <Text css={{ color: '$slate12' }}>Verify NFA</Text>
<Switch checked={verifyNFA} onChange={setVerifyNFA} /> <Switch checked={verifyNFA} onChange={setVerifyNFA} />
</Card.Text> </S.Body.VerifyContainer>
<SelectVerifier /> <SelectVerifier />
<Button <Button
colorScheme="blue" colorScheme="blue"
@ -133,8 +54,8 @@ export const VerifyNFAStep: React.FC = () => {
> >
Continue Continue
</Button> </Button>
</Flex> </S.Body.Container>
</Card.Body> </Card.Body>
</Card.Container> </CustomCardContainer>
); );
}; };

View File

@ -1,4 +1,11 @@
import { Button, Card, Grid } from '@/components'; import {
Button,
Card,
CustomCardContainer,
CustomCardHeader,
Flex,
Text,
} from '@/components';
import { NFAPreview } from '@/components'; import { NFAPreview } from '@/components';
import { useMintFormContext } from '../nfa-step/form-step'; import { useMintFormContext } from '../nfa-step/form-step';
@ -16,8 +23,6 @@ type NftCardProps = {
export const NftCard: React.FC<NftCardProps> = ({ export const NftCard: React.FC<NftCardProps> = ({
title, title,
leftIcon,
rightIcon,
message, message,
buttonText, buttonText,
leftIconButton, leftIconButton,
@ -43,23 +48,21 @@ export const NftCard: React.FC<NftCardProps> = ({
} = useMintFormContext(); } = useMintFormContext();
return ( return (
<Card.Container css={{ maxWidth: '$107h', p: '$0' }}> <CustomCardContainer css={{ p: '$0' }}>
<NFAPreview <NFAPreview
color={logoColor} color={logoColor}
logo={appLogo} logo={appLogo}
name={appName} name={appName}
ens={ens} ens={ens}
size={size} size={size}
className="rounded-t-xhl" css={{
bt: '1.25rem',
}}
/> />
<Card.Body css={{ p: '$7' }}> <Card.Body css={{ p: '$7' }}>
<Grid css={{ rowGap: '$6' }}> <Flex css={{ gap: '$6', flexDirection: 'column' }}>
<Card.Heading <CustomCardHeader.Success title={title} />
title={title} <Text css={{ color: '$slate11', fontSize: '$sm' }}>{message}</Text>
leftIcon={leftIcon}
rightIcon={rightIcon}
/>
<span className="text-slate11 text-sm">{message}</span>
<Button <Button
colorScheme="blue" colorScheme="blue"
variant="solid" variant="solid"
@ -70,8 +73,8 @@ export const NftCard: React.FC<NftCardProps> = ({
> >
{buttonText} {buttonText}
</Button> </Button>
</Grid> </Flex>
</Card.Body> </Card.Body>
</Card.Container> </CustomCardContainer>
); );
}; };

View File

@ -1,23 +1,18 @@
import { Card, Grid, Icon, IconButton } from '@/components'; import {
Card,
CustomCardContainer,
CustomCardHeader,
Flex,
} from '@/components';
import { ConnectWalletButton } from './connect-wallet-button'; import { ConnectWalletButton } from './connect-wallet-button';
export const WalletStep: React.FC = () => { export const WalletStep: React.FC = () => {
return ( return (
<Card.Container css={{ maxWidth: '$107h' }}> <CustomCardContainer>
<Card.Heading <CustomCardHeader.Default title="Connect Wallet" />
title="Connect Wallet"
rightIcon={
<IconButton
aria-label="Add"
colorScheme="gray"
variant="link"
icon={<Icon name="info" />}
/>
}
/>
<Card.Body> <Card.Body>
<Grid css={{ rowGap: '$6' }}> <Flex css={{ gap: '$6', flexDirection: 'column' }}>
<ConnectWalletButton /> <ConnectWalletButton />
<Card.Text <Card.Text
css={{ css={{
@ -29,8 +24,8 @@ export const WalletStep: React.FC = () => {
> >
<span>Connect with the wallet you want to mint & own the NFA.</span> <span>Connect with the wallet you want to mint & own the NFA.</span>
</Card.Text> </Card.Text>
</Grid> </Flex>
</Card.Body> </Card.Body>
</Card.Container> </CustomCardContainer>
); );
}; };