Commit bb7d3592 authored by tom's avatar tom

private tags page refactoring

parent fcde6016
...@@ -4,12 +4,12 @@ import React from 'react'; ...@@ -4,12 +4,12 @@ import React from 'react';
import PageNextJs from 'nextjs/PageNextJs'; import PageNextJs from 'nextjs/PageNextJs';
// const PrivateTags = dynamic(() => import('ui/pages/PrivateTags'), { ssr: false }); const PrivateTags = dynamic(() => import('ui/pages/PrivateTags'), { ssr: false });
const Page: NextPage = () => { const Page: NextPage = () => {
return ( return (
<PageNextJs pathname="/account/tag-address"> <PageNextJs pathname="/account/tag-address">
{ /* <PrivateTags/> */ } <PrivateTags/>
</PageNextJs> </PageNextJs>
); );
}; };
......
...@@ -418,10 +418,10 @@ const semanticTokens: ThemingConfig['semanticTokens'] = { ...@@ -418,10 +418,10 @@ const semanticTokens: ThemingConfig['semanticTokens'] = {
DEFAULT: { value: '{colors.red.500}' }, DEFAULT: { value: '{colors.red.500}' },
_dark: { value: '{colors.red.500}' }, _dark: { value: '{colors.red.500}' },
}, },
dialog_bg: { // dialog_bg: {
DEFAULT: { value: '{colors.white}' }, // DEFAULT: { value: '{colors.white}' },
_dark: { value: '{colors.gray.900}' }, // _dark: { value: '{colors.gray.900}' },
}, // },
}, },
shadows: { shadows: {
popover: { popover: {
......
...@@ -4,7 +4,7 @@ export const recipe = defineSlotRecipe({ ...@@ -4,7 +4,7 @@ export const recipe = defineSlotRecipe({
slots: [ 'backdrop', 'positioner', 'content', 'header', 'body', 'footer', 'title', 'description' ], slots: [ 'backdrop', 'positioner', 'content', 'header', 'body', 'footer', 'title', 'description' ],
base: { base: {
backdrop: { backdrop: {
bg: 'blackAlpha.500', bg: 'blackAlpha.800',
pos: 'fixed', pos: 'fixed',
left: 0, left: 0,
top: 0, top: 0,
...@@ -70,9 +70,9 @@ export const recipe = defineSlotRecipe({ ...@@ -70,9 +70,9 @@ export const recipe = defineSlotRecipe({
alignItems: 'center', alignItems: 'center',
justifyContent: 'flex-end', justifyContent: 'flex-end',
gap: '3', gap: '3',
px: '6', px: '0',
pt: '2', pt: '2',
pb: '4', pb: '0',
}, },
title: { title: {
textStyle: 'heading.md', textStyle: 'heading.md',
......
...@@ -25,6 +25,7 @@ import AlertsShowcase from 'ui/showcases/Alerts'; ...@@ -25,6 +25,7 @@ import AlertsShowcase from 'ui/showcases/Alerts';
import BadgesShowcase from 'ui/showcases/Badges'; import BadgesShowcase from 'ui/showcases/Badges';
import ButtonShowcase from 'ui/showcases/Button'; import ButtonShowcase from 'ui/showcases/Button';
import CheckboxesShowcase from 'ui/showcases/Checkbox'; import CheckboxesShowcase from 'ui/showcases/Checkbox';
import DialogsShowcase from 'ui/showcases/Dialog';
import LinksShowcase from 'ui/showcases/Links'; import LinksShowcase from 'ui/showcases/Links';
import MenusShowcase from 'ui/showcases/Menu'; import MenusShowcase from 'ui/showcases/Menu';
import PaginationShowcase from 'ui/showcases/Pagination'; import PaginationShowcase from 'ui/showcases/Pagination';
...@@ -53,6 +54,7 @@ const ChakraShowcases = () => { ...@@ -53,6 +54,7 @@ const ChakraShowcases = () => {
<TabsTrigger value="badges">Badges</TabsTrigger> <TabsTrigger value="badges">Badges</TabsTrigger>
<TabsTrigger value="buttons">Buttons</TabsTrigger> <TabsTrigger value="buttons">Buttons</TabsTrigger>
<TabsTrigger value="checkboxes">Checkboxes</TabsTrigger> <TabsTrigger value="checkboxes">Checkboxes</TabsTrigger>
<TabsTrigger value="dialogs">Dialogs</TabsTrigger>
<TabsTrigger value="links">Links</TabsTrigger> <TabsTrigger value="links">Links</TabsTrigger>
<TabsTrigger value="menus">Menus</TabsTrigger> <TabsTrigger value="menus">Menus</TabsTrigger>
<TabsTrigger value="pagination">Pagination</TabsTrigger> <TabsTrigger value="pagination">Pagination</TabsTrigger>
...@@ -67,6 +69,7 @@ const ChakraShowcases = () => { ...@@ -67,6 +69,7 @@ const ChakraShowcases = () => {
<AlertsShowcase/> <AlertsShowcase/>
<BadgesShowcase/> <BadgesShowcase/>
<ButtonShowcase/> <ButtonShowcase/>
<DialogsShowcase/>
<CheckboxesShowcase/> <CheckboxesShowcase/>
<LinksShowcase/> <LinksShowcase/>
<MenusShowcase/> <MenusShowcase/>
......
...@@ -2,10 +2,10 @@ import React from 'react'; ...@@ -2,10 +2,10 @@ import React from 'react';
import type { RoutedTab } from 'ui/shared/Tabs/types'; import type { RoutedTab } from 'ui/shared/Tabs/types';
import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs';
import PrivateAddressTags from 'ui/privateTags/PrivateAddressTags'; import PrivateAddressTags from 'ui/privateTags/PrivateAddressTags';
import PrivateTransactionTags from 'ui/privateTags/PrivateTransactionTags'; import PrivateTransactionTags from 'ui/privateTags/PrivateTransactionTags';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
import useRedirectForInvalidAuthToken from 'ui/snippets/auth/useRedirectForInvalidAuthToken'; import useRedirectForInvalidAuthToken from 'ui/snippets/auth/useRedirectForInvalidAuthToken';
const TABS: Array<RoutedTab> = [ const TABS: Array<RoutedTab> = [
......
import {
Box,
Button,
} from '@chakra-ui/react';
import { useMutation } from '@tanstack/react-query'; import { useMutation } from '@tanstack/react-query';
import React, { useState } from 'react'; import React, { useState } from 'react';
import type { SubmitHandler } from 'react-hook-form'; import type { SubmitHandler } from 'react-hook-form';
...@@ -12,6 +8,7 @@ import type { AddressTag, AddressTagErrors } from 'types/api/account'; ...@@ -12,6 +8,7 @@ import type { AddressTag, AddressTagErrors } from 'types/api/account';
import type { ResourceErrorAccount } from 'lib/api/resources'; import type { ResourceErrorAccount } from 'lib/api/resources';
import useApiFetch from 'lib/api/useApiFetch'; import useApiFetch from 'lib/api/useApiFetch';
import getErrorMessage from 'lib/getErrorMessage'; import getErrorMessage from 'lib/getErrorMessage';
import { Button } from 'toolkit/chakra/button';
import FormFieldAddress from 'ui/shared/forms/fields/FormFieldAddress'; import FormFieldAddress from 'ui/shared/forms/fields/FormFieldAddress';
import FormFieldText from 'ui/shared/forms/fields/FormFieldText'; import FormFieldText from 'ui/shared/forms/fields/FormFieldText';
...@@ -19,7 +16,7 @@ const TAG_MAX_LENGTH = 35; ...@@ -19,7 +16,7 @@ const TAG_MAX_LENGTH = 35;
type Props = { type Props = {
data?: Partial<AddressTag>; data?: Partial<AddressTag>;
onClose: () => void; onOpenChange: ({ open }: { open: boolean }) => void;
onSuccess: () => Promise<void>; onSuccess: () => Promise<void>;
setAlertVisible: (isAlertVisible: boolean) => void; setAlertVisible: (isAlertVisible: boolean) => void;
}; };
...@@ -29,7 +26,7 @@ type Inputs = { ...@@ -29,7 +26,7 @@ type Inputs = {
tag: string; tag: string;
}; };
const AddressForm: React.FC<Props> = ({ data, onClose, onSuccess, setAlertVisible }) => { const AddressForm: React.FC<Props> = ({ data, onOpenChange, onSuccess, setAlertVisible }) => {
const apiFetch = useApiFetch(); const apiFetch = useApiFetch();
const [ pending, setPending ] = useState(false); const [ pending, setPending ] = useState(false);
const formApi = useForm<Inputs>({ const formApi = useForm<Inputs>({
...@@ -71,7 +68,7 @@ const AddressForm: React.FC<Props> = ({ data, onClose, onSuccess, setAlertVisibl ...@@ -71,7 +68,7 @@ const AddressForm: React.FC<Props> = ({ data, onClose, onSuccess, setAlertVisibl
}, },
onSuccess: async() => { onSuccess: async() => {
await onSuccess(); await onSuccess();
onClose(); onOpenChange({ open: false });
setPending(false); setPending(false);
}, },
}); });
...@@ -87,30 +84,28 @@ const AddressForm: React.FC<Props> = ({ data, onClose, onSuccess, setAlertVisibl ...@@ -87,30 +84,28 @@ const AddressForm: React.FC<Props> = ({ data, onClose, onSuccess, setAlertVisibl
<form noValidate onSubmit={ formApi.handleSubmit(onSubmit) }> <form noValidate onSubmit={ formApi.handleSubmit(onSubmit) }>
<FormFieldAddress<Inputs> <FormFieldAddress<Inputs>
name="address" name="address"
isRequired required
bgColor="dialog_bg" bgColor="dialog.bg"
mb={ 5 } mb={ 5 }
/> />
<FormFieldText<Inputs> <FormFieldText<Inputs>
name="tag" name="tag"
placeholder="Private tag (max 35 characters)" placeholder="Private tag (max 35 characters)"
isRequired required
rules={{ rules={{
maxLength: TAG_MAX_LENGTH, maxLength: TAG_MAX_LENGTH,
}} }}
bgColor="dialog_bg" bgColor="dialog.bg"
mb={ 8 } mb={ 8 }
/> />
<Box marginTop={ 8 }>
<Button <Button
size="lg" size="lg"
type="submit" type="submit"
isDisabled={ !formApi.formState.isDirty } disabled={ !formApi.formState.isDirty }
isLoading={ pending } loading={ pending }
> >
{ data ? 'Save changes' : 'Add tag' } { data ? 'Save changes' : 'Add tag' }
</Button> </Button>
</Box>
</form> </form>
</FormProvider> </FormProvider>
); );
......
...@@ -8,25 +8,25 @@ import FormModal from 'ui/shared/FormModal'; ...@@ -8,25 +8,25 @@ import FormModal from 'ui/shared/FormModal';
import AddressForm from './AddressForm'; import AddressForm from './AddressForm';
type Props = { type Props = {
isOpen: boolean; open: boolean;
onClose: () => void; onOpenChange: ({ open }: { open: boolean }) => void;
onSuccess: () => Promise<void>; onSuccess: () => Promise<void>;
data?: Partial<AddressTag>; data?: Partial<AddressTag>;
pageType: string; pageType: string;
}; };
const AddressModal: React.FC<Props> = ({ isOpen, onClose, onSuccess, data, pageType }) => { const AddressModal: React.FC<Props> = ({ open, onOpenChange, onSuccess, data, pageType }) => {
const title = data?.id ? 'Edit address tag' : 'New address tag'; const title = data?.id ? 'Edit address tag' : 'New address tag';
const text = !data?.id ? 'Label any address with a private address tag (up to 35 chars) to customize your explorer experience.' : ''; const text = !data?.id ? 'Label any address with a private address tag (up to 35 chars) to customize your explorer experience.' : '';
const [ isAlertVisible, setAlertVisible ] = useState(false); const [ isAlertVisible, setAlertVisible ] = useState(false);
React.useEffect(() => { React.useEffect(() => {
isOpen && !data?.id && mixpanel.logEvent( open && !data?.id && mixpanel.logEvent(
mixpanel.EventTypes.PRIVATE_TAG, mixpanel.EventTypes.PRIVATE_TAG,
{ Action: 'Form opened', 'Page type': pageType, 'Tag type': 'Address' }, { Action: 'Form opened', 'Page type': pageType, 'Tag type': 'Address' },
); );
}, [ data?.id, isOpen, pageType ]); }, [ data?.id, open, pageType ]);
const handleSuccess = React.useCallback(() => { const handleSuccess = React.useCallback(() => {
if (!data?.id) { if (!data?.id) {
...@@ -39,12 +39,12 @@ const AddressModal: React.FC<Props> = ({ isOpen, onClose, onSuccess, data, pageT ...@@ -39,12 +39,12 @@ const AddressModal: React.FC<Props> = ({ isOpen, onClose, onSuccess, data, pageT
}, [ data?.id, onSuccess, pageType ]); }, [ data?.id, onSuccess, pageType ]);
const renderForm = useCallback(() => { const renderForm = useCallback(() => {
return <AddressForm data={ data } onClose={ onClose } onSuccess={ handleSuccess } setAlertVisible={ setAlertVisible }/>; return <AddressForm data={ data } onOpenChange={ onOpenChange } onSuccess={ handleSuccess } setAlertVisible={ setAlertVisible }/>;
}, [ data, onClose, handleSuccess ]); }, [ data, onOpenChange, handleSuccess ]);
return ( return (
<FormModal<AddressTag> <FormModal<AddressTag>
isOpen={ isOpen } open={ open }
onClose={ onClose } onOpenChange={ onOpenChange }
title={ title } title={ title }
text={ text } text={ text }
renderForm={ renderForm } renderForm={ renderForm }
......
import { Tag, Flex, HStack, Text } from '@chakra-ui/react'; import { Flex, HStack, Text } from '@chakra-ui/react';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import type { AddressTag } from 'types/api/account'; import type { AddressTag } from 'types/api/account';
import Skeleton from 'ui/shared/chakra/Skeleton'; import { Skeleton } from 'toolkit/chakra/skeleton';
import { Tag } from 'toolkit/chakra/tag';
import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import TableItemActionButtons from 'ui/shared/TableItemActionButtons'; import TableItemActionButtons from 'ui/shared/TableItemActionButtons';
...@@ -33,9 +34,9 @@ const AddressTagListItem = ({ item, onEditClick, onDeleteClick, isLoading }: Pro ...@@ -33,9 +34,9 @@ const AddressTagListItem = ({ item, onEditClick, onDeleteClick, isLoading }: Pro
fontWeight="600" fontWeight="600"
w="100%" w="100%"
/> />
<HStack spacing={ 3 } mt={ 4 }> <HStack gap={ 3 } mt={ 4 }>
<Text fontSize="sm" fontWeight={ 500 }>Private tag</Text> <Text textStyle="sm" fontWeight="medium">Private tag</Text>
<Skeleton isLoaded={ !isLoading } display="inline-block" borderRadius="sm"> <Skeleton loading={ isLoading } display="inline-block" borderRadius="sm">
<Tag> <Tag>
{ item.name } { item.name }
</Tag> </Tag>
......
import {
Table,
Tbody,
Tr,
Th,
} from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { AddressTags, AddressTag } from 'types/api/account'; import type { AddressTags, AddressTag } from 'types/api/account';
import TheadSticky from 'ui/shared/TheadSticky'; import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';
import AddressTagTableItem from './AddressTagTableItem'; import AddressTagTableItem from './AddressTagTableItem';
...@@ -22,15 +16,15 @@ interface Props { ...@@ -22,15 +16,15 @@ interface Props {
const AddressTagTable = ({ data, onDeleteClick, onEditClick, isLoading, top }: Props) => { const AddressTagTable = ({ data, onDeleteClick, onEditClick, isLoading, top }: Props) => {
return ( return (
<Table minWidth="600px"> <TableRoot minWidth="600px">
<TheadSticky top={ top }> <TableHeaderSticky top={ top }>
<Tr> <TableRow>
<Th width="60%">Address</Th> <TableColumnHeader width="60%">Address</TableColumnHeader>
<Th width="40%">Private tag</Th> <TableColumnHeader width="40%">Private tag</TableColumnHeader>
<Th width="116px"></Th> <TableColumnHeader width="116px"></TableColumnHeader>
</Tr> </TableRow>
</TheadSticky> </TableHeaderSticky>
<Tbody> <TableBody>
{ data?.map((item: AddressTag, index: number) => ( { data?.map((item: AddressTag, index: number) => (
<AddressTagTableItem <AddressTagTableItem
item={ item } item={ item }
...@@ -40,8 +34,8 @@ const AddressTagTable = ({ data, onDeleteClick, onEditClick, isLoading, top }: P ...@@ -40,8 +34,8 @@ const AddressTagTable = ({ data, onDeleteClick, onEditClick, isLoading, top }: P
isLoading={ isLoading } isLoading={ isLoading }
/> />
)) } )) }
</Tbody> </TableBody>
</Table> </TableRoot>
); );
}; };
......
import {
Tr,
Td,
} from '@chakra-ui/react';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import type { AddressTag } from 'types/api/account'; import type { AddressTag } from 'types/api/account';
import Tag from 'ui/shared/chakra/Tag'; import { TableCell, TableRow } from 'toolkit/chakra/table';
import { Tag } from 'toolkit/chakra/tag';
import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TableItemActionButtons from 'ui/shared/TableItemActionButtons'; import TableItemActionButtons from 'ui/shared/TableItemActionButtons';
...@@ -27,22 +24,22 @@ const AddressTagTableItem = ({ item, onEditClick, onDeleteClick, isLoading }: Pr ...@@ -27,22 +24,22 @@ const AddressTagTableItem = ({ item, onEditClick, onDeleteClick, isLoading }: Pr
}, [ item, onDeleteClick ]); }, [ item, onDeleteClick ]);
return ( return (
<Tr alignItems="top" key={ item.id }> <TableRow alignItems="top" key={ item.id }>
<Td> <TableCell>
<AddressEntity <AddressEntity
address={ item.address } address={ item.address }
isLoading={ isLoading } isLoading={ isLoading }
fontWeight="600" fontWeight="600"
py="2px" py="2px"
/> />
</Td> </TableCell>
<Td whiteSpace="nowrap"> <TableCell whiteSpace="nowrap">
<Tag isLoading={ isLoading } isTruncated>{ item.name }</Tag> <Tag loading={ isLoading } truncated>{ item.name }</Tag>
</Td> </TableCell>
<Td> <TableCell>
<TableItemActionButtons onDeleteClick={ onItemDeleteClick } onEditClick={ onItemEditClick } isLoading={ isLoading }/> <TableItemActionButtons onDeleteClick={ onItemDeleteClick } onEditClick={ onItemEditClick } isLoading={ isLoading }/>
</Td> </TableCell>
</Tr> </TableRow>
); );
}; };
......
...@@ -9,13 +9,13 @@ import { getResourceKey } from 'lib/api/useApiQuery'; ...@@ -9,13 +9,13 @@ import { getResourceKey } from 'lib/api/useApiQuery';
import DeleteModal from 'ui/shared/DeleteModal'; import DeleteModal from 'ui/shared/DeleteModal';
type Props = { type Props = {
isOpen: boolean; open: boolean;
onClose: () => void; onOpenChange: ({ open }: { open: boolean }) => void;
data: AddressTag | TransactionTag; data: AddressTag | TransactionTag;
type: 'address' | 'transaction'; type: 'address' | 'transaction';
}; };
const DeletePrivateTagModal: React.FC<Props> = ({ isOpen, onClose, data, type }) => { const DeletePrivateTagModal: React.FC<Props> = ({ open, onOpenChange, data, type }) => {
const tag = data.name; const tag = data.name;
const id = data.id; const id = data.id;
...@@ -53,8 +53,8 @@ const DeletePrivateTagModal: React.FC<Props> = ({ isOpen, onClose, data, type }) ...@@ -53,8 +53,8 @@ const DeletePrivateTagModal: React.FC<Props> = ({ isOpen, onClose, data, type })
return ( return (
<DeleteModal <DeleteModal
isOpen={ isOpen } open={ open }
onClose={ onClose } onOpenChange={ onOpenChange }
title="Removal of private tag" title="Removal of private tag"
renderContent={ renderText } renderContent={ renderText }
mutationFn={ mutationFn } mutationFn={ mutationFn }
......
import { Box, Button, useDisclosure } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import React, { useCallback, useState } from 'react'; import React, { useCallback, useState } from 'react';
import type { AddressTag } from 'types/api/account'; import type { AddressTag } from 'types/api/account';
import { PAGE_TYPE_DICT } from 'lib/mixpanel/getPageType'; import { PAGE_TYPE_DICT } from 'lib/mixpanel/getPageType';
import { PRIVATE_TAG_ADDRESS } from 'stubs/account'; import { PRIVATE_TAG_ADDRESS } from 'stubs/account';
import { Button } from 'toolkit/chakra/button';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { useDisclosure } from 'toolkit/hooks/useDisclosure';
import AccountPageDescription from 'ui/shared/AccountPageDescription'; import AccountPageDescription from 'ui/shared/AccountPageDescription';
import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar';
import Skeleton from 'ui/shared/chakra/Skeleton';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import Pagination from 'ui/shared/pagination/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
...@@ -41,9 +43,9 @@ const PrivateAddressTags = () => { ...@@ -41,9 +43,9 @@ const PrivateAddressTags = () => {
await refetch(); await refetch();
}, [ refetch ]); }, [ refetch ]);
const onAddressModalClose = useCallback(() => { const onAddressModalOpenChange = useCallback(({ open }: { open: boolean }) => {
setAddressModalData(undefined); !open && setAddressModalData(undefined);
addressModalProps.onClose(); addressModalProps.onOpenChange({ open });
}, [ addressModalProps ]); }, [ addressModalProps ]);
const onDeleteClick = useCallback((data: AddressTag) => { const onDeleteClick = useCallback((data: AddressTag) => {
...@@ -51,13 +53,29 @@ const PrivateAddressTags = () => { ...@@ -51,13 +53,29 @@ const PrivateAddressTags = () => {
deleteModalProps.onOpen(); deleteModalProps.onOpen();
}, [ deleteModalProps ]); }, [ deleteModalProps ]);
const onDeleteModalClose = useCallback(() => { const onDeleteModalOpenChange = useCallback(({ open }: { open: boolean }) => {
setDeleteModalData(undefined); !open && setDeleteModalData(undefined);
deleteModalProps.onClose(); deleteModalProps.onOpenChange({ open });
}, [ deleteModalProps ]); }, [ deleteModalProps ]);
const list = ( const actionBar = pagination.isVisible ? (
<ActionBar mt={ -6 }>
<Pagination ml="auto" { ...pagination }/>
</ActionBar>
) : null;
return (
<> <>
<AccountPageDescription>
Use private address tags to track any addresses of interest.
Private tags are saved in your account and are only visible when you are logged in.
</AccountPageDescription>
<DataListDisplay
isError={ isError }
itemsNum={ addressTagsData?.items.length }
emptyText=""
actionBar={ actionBar }
>
<Box display={{ base: 'block', lg: 'none' }}> <Box display={{ base: 'block', lg: 'none' }}>
{ addressTagsData?.items.map((item: AddressTag, index: number) => ( { addressTagsData?.items.map((item: AddressTag, index: number) => (
<AddressTagListItem <AddressTagListItem
...@@ -78,29 +96,8 @@ const PrivateAddressTags = () => { ...@@ -78,29 +96,8 @@ const PrivateAddressTags = () => {
top={ pagination.isVisible ? ACTION_BAR_HEIGHT_DESKTOP : 0 } top={ pagination.isVisible ? ACTION_BAR_HEIGHT_DESKTOP : 0 }
/> />
</Box> </Box>
</> </DataListDisplay>
); <Skeleton mt={ 8 } loading={ isPlaceholderData } display="inline-block">
const actionBar = pagination.isVisible ? (
<ActionBar mt={ -6 }>
<Pagination ml="auto" { ...pagination }/>
</ActionBar>
) : null;
return (
<>
<AccountPageDescription>
Use private address tags to track any addresses of interest.
Private tags are saved in your account and are only visible when you are logged in.
</AccountPageDescription>
<DataListDisplay
isError={ isError }
items={ addressTagsData?.items }
emptyText=""
content={ list }
actionBar={ actionBar }
/>
<Skeleton mt={ 8 } isLoaded={ !isPlaceholderData } display="inline-block">
<Button <Button
size="lg" size="lg"
onClick={ addressModalProps.onOpen } onClick={ addressModalProps.onOpen }
...@@ -112,13 +109,13 @@ const PrivateAddressTags = () => { ...@@ -112,13 +109,13 @@ const PrivateAddressTags = () => {
{ ...addressModalProps } { ...addressModalProps }
data={ addressModalData } data={ addressModalData }
pageType={ PAGE_TYPE_DICT['/account/tag-address'] } pageType={ PAGE_TYPE_DICT['/account/tag-address'] }
onClose={ onAddressModalClose } onOpenChange={ onAddressModalOpenChange }
onSuccess={ onAddOrEditSuccess } onSuccess={ onAddOrEditSuccess }
/> />
{ deleteModalData && ( { deleteModalData && (
<DeletePrivateTagModal <DeletePrivateTagModal
{ ...deleteModalProps } { ...deleteModalProps }
onClose={ onDeleteModalClose } onOpenChange={ onDeleteModalOpenChange }
data={ deleteModalData } data={ deleteModalData }
type="address" type="address"
/> />
......
import { Box, Button, useDisclosure } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import React, { useCallback, useState } from 'react'; import React, { useCallback, useState } from 'react';
import type { TransactionTag } from 'types/api/account'; import type { TransactionTag } from 'types/api/account';
import { PRIVATE_TAG_TX } from 'stubs/account'; import { PRIVATE_TAG_TX } from 'stubs/account';
import { Button } from 'toolkit/chakra/button';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { useDisclosure } from 'toolkit/hooks/useDisclosure';
import AccountPageDescription from 'ui/shared/AccountPageDescription'; import AccountPageDescription from 'ui/shared/AccountPageDescription';
import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar';
import Skeleton from 'ui/shared/chakra/Skeleton';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import Pagination from 'ui/shared/pagination/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
...@@ -36,9 +38,9 @@ const PrivateTransactionTags = () => { ...@@ -36,9 +38,9 @@ const PrivateTransactionTags = () => {
transactionModalProps.onOpen(); transactionModalProps.onOpen();
}, [ transactionModalProps ]); }, [ transactionModalProps ]);
const onAddressModalClose = useCallback(() => { const onAddressModalOpenChange = useCallback(({ open }: { open: boolean }) => {
setTransactionModalData(undefined); !open && setTransactionModalData(undefined);
transactionModalProps.onClose(); transactionModalProps.onOpenChange({ open });
}, [ transactionModalProps ]); }, [ transactionModalProps ]);
const onDeleteClick = useCallback((data: TransactionTag) => { const onDeleteClick = useCallback((data: TransactionTag) => {
...@@ -46,9 +48,9 @@ const PrivateTransactionTags = () => { ...@@ -46,9 +48,9 @@ const PrivateTransactionTags = () => {
deleteModalProps.onOpen(); deleteModalProps.onOpen();
}, [ deleteModalProps ]); }, [ deleteModalProps ]);
const onDeleteModalClose = useCallback(() => { const onDeleteModalOpenChange = useCallback(({ open }: { open: boolean }) => {
setDeleteModalData(undefined); !open && setDeleteModalData(undefined);
deleteModalProps.onClose(); deleteModalProps.onOpenChange({ open });
}, [ deleteModalProps ]); }, [ deleteModalProps ]);
const description = ( const description = (
...@@ -58,8 +60,21 @@ const PrivateTransactionTags = () => { ...@@ -58,8 +60,21 @@ const PrivateTransactionTags = () => {
</AccountPageDescription> </AccountPageDescription>
); );
const list = ( const actionBar = pagination.isVisible ? (
<ActionBar mt={ -6 }>
<Pagination ml="auto" { ...pagination }/>
</ActionBar>
) : null;
return (
<> <>
{ description }
<DataListDisplay
isError={ isError }
itemsNum={ transactionTagsData?.items.length }
emptyText=""
actionBar={ actionBar }
>
<Box display={{ base: 'block', lg: 'none' }}> <Box display={{ base: 'block', lg: 'none' }}>
{ transactionTagsData?.items.map((item, index) => ( { transactionTagsData?.items.map((item, index) => (
<TransactionTagListItem <TransactionTagListItem
...@@ -80,25 +95,8 @@ const PrivateTransactionTags = () => { ...@@ -80,25 +95,8 @@ const PrivateTransactionTags = () => {
top={ pagination.isVisible ? ACTION_BAR_HEIGHT_DESKTOP : 0 } top={ pagination.isVisible ? ACTION_BAR_HEIGHT_DESKTOP : 0 }
/> />
</Box> </Box>
</> </DataListDisplay>
); <Skeleton mt={ 8 } loading={ isPlaceholderData } display="inline-block">
const actionBar = pagination.isVisible ? (
<ActionBar mt={ -6 }>
<Pagination ml="auto" { ...pagination }/>
</ActionBar>
) : null;
return (
<>
{ description }
<DataListDisplay
isError={ isError }
items={ transactionTagsData?.items }
emptyText=""
content={ list }
actionBar={ actionBar }
/>
<Skeleton mt={ 8 } isLoaded={ !isPlaceholderData } display="inline-block">
<Button <Button
size="lg" size="lg"
onClick={ transactionModalProps.onOpen } onClick={ transactionModalProps.onOpen }
...@@ -106,11 +104,15 @@ const PrivateTransactionTags = () => { ...@@ -106,11 +104,15 @@ const PrivateTransactionTags = () => {
Add transaction tag Add transaction tag
</Button> </Button>
</Skeleton> </Skeleton>
<TransactionModal { ...transactionModalProps } onClose={ onAddressModalClose } data={ transactionModalData }/> <TransactionModal
{ ...transactionModalProps }
onOpenChange={ onAddressModalOpenChange }
data={ transactionModalData }
/>
{ deleteModalData && ( { deleteModalData && (
<DeletePrivateTagModal <DeletePrivateTagModal
{ ...deleteModalProps } { ...deleteModalProps }
onClose={ onDeleteModalClose } onOpenChange={ onDeleteModalOpenChange }
data={ deleteModalData } data={ deleteModalData }
type="transaction" type="transaction"
/> />
......
import { import { Box } from '@chakra-ui/react';
Box,
Button,
} from '@chakra-ui/react';
import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query';
import React, { useState } from 'react'; import React, { useState } from 'react';
import type { SubmitHandler } from 'react-hook-form'; import type { SubmitHandler } from 'react-hook-form';
...@@ -13,6 +10,7 @@ import type { ResourceErrorAccount } from 'lib/api/resources'; ...@@ -13,6 +10,7 @@ import type { ResourceErrorAccount } from 'lib/api/resources';
import { resourceKey } from 'lib/api/resources'; import { resourceKey } from 'lib/api/resources';
import useApiFetch from 'lib/api/useApiFetch'; import useApiFetch from 'lib/api/useApiFetch';
import getErrorMessage from 'lib/getErrorMessage'; import getErrorMessage from 'lib/getErrorMessage';
import { Button } from 'toolkit/chakra/button';
import FormFieldText from 'ui/shared/forms/fields/FormFieldText'; import FormFieldText from 'ui/shared/forms/fields/FormFieldText';
import { TRANSACTION_HASH_LENGTH, TRANSACTION_HASH_REGEXP } from 'ui/shared/forms/validators/transaction'; import { TRANSACTION_HASH_LENGTH, TRANSACTION_HASH_REGEXP } from 'ui/shared/forms/validators/transaction';
...@@ -20,7 +18,7 @@ const TAG_MAX_LENGTH = 35; ...@@ -20,7 +18,7 @@ const TAG_MAX_LENGTH = 35;
type Props = { type Props = {
data?: Partial<TransactionTag>; data?: Partial<TransactionTag>;
onClose: () => void; onOpenChange: ({ open }: { open: boolean }) => void;
onSuccess: () => Promise<void>; onSuccess: () => Promise<void>;
setAlertVisible: (isAlertVisible: boolean) => void; setAlertVisible: (isAlertVisible: boolean) => void;
}; };
...@@ -30,7 +28,7 @@ type Inputs = { ...@@ -30,7 +28,7 @@ type Inputs = {
tag: string; tag: string;
}; };
const TransactionForm: React.FC<Props> = ({ data, onClose, onSuccess, setAlertVisible }) => { const TransactionForm: React.FC<Props> = ({ data, onOpenChange, onSuccess, setAlertVisible }) => {
const [ pending, setPending ] = useState(false); const [ pending, setPending ] = useState(false);
const formApi = useForm<Inputs>({ const formApi = useForm<Inputs>({
...@@ -76,7 +74,7 @@ const TransactionForm: React.FC<Props> = ({ data, onClose, onSuccess, setAlertVi ...@@ -76,7 +74,7 @@ const TransactionForm: React.FC<Props> = ({ data, onClose, onSuccess, setAlertVi
onSuccess: async() => { onSuccess: async() => {
await queryClient.refetchQueries({ queryKey: [ resourceKey('private_tags_tx') ] }); await queryClient.refetchQueries({ queryKey: [ resourceKey('private_tags_tx') ] });
await onSuccess(); await onSuccess();
onClose(); onOpenChange({ open: false });
setPending(false); setPending(false);
}, },
}); });
...@@ -92,30 +90,30 @@ const TransactionForm: React.FC<Props> = ({ data, onClose, onSuccess, setAlertVi ...@@ -92,30 +90,30 @@ const TransactionForm: React.FC<Props> = ({ data, onClose, onSuccess, setAlertVi
<FormFieldText<Inputs> <FormFieldText<Inputs>
name="transaction" name="transaction"
placeholder="Transaction hash (0x...)" placeholder="Transaction hash (0x...)"
isRequired required
rules={{ rules={{
maxLength: TRANSACTION_HASH_LENGTH, maxLength: TRANSACTION_HASH_LENGTH,
pattern: TRANSACTION_HASH_REGEXP, pattern: TRANSACTION_HASH_REGEXP,
}} }}
bgColor="dialog_bg" bgColor="dialog.bg"
mb={ 5 } mb={ 5 }
/> />
<FormFieldText<Inputs> <FormFieldText<Inputs>
name="tag" name="tag"
placeholder="Private tag (max 35 characters)" placeholder="Private tag (max 35 characters)"
isRequired required
rules={{ rules={{
maxLength: TAG_MAX_LENGTH, maxLength: TAG_MAX_LENGTH,
}} }}
bgColor="dialog_bg" bgColor="dialog.bg"
mb={ 8 } mb={ 8 }
/> />
<Box marginTop={ 8 }> <Box marginTop={ 8 }>
<Button <Button
size="lg" size="lg"
type="submit" type="submit"
isDisabled={ !formApi.formState.isDirty } disabled={ !formApi.formState.isDirty }
isLoading={ pending } loading={ pending }
> >
{ data ? 'Save changes' : 'Add tag' } { data ? 'Save changes' : 'Add tag' }
</Button> </Button>
......
...@@ -9,24 +9,24 @@ import FormModal from 'ui/shared/FormModal'; ...@@ -9,24 +9,24 @@ import FormModal from 'ui/shared/FormModal';
import TransactionForm from './TransactionForm'; import TransactionForm from './TransactionForm';
type Props = { type Props = {
isOpen: boolean; open: boolean;
onClose: () => void; onOpenChange: ({ open }: { open: boolean }) => void;
onSuccess?: () => Promise<void>; onSuccess?: () => Promise<void>;
data?: Partial<TransactionTag>; data?: Partial<TransactionTag>;
}; };
const AddressModal: React.FC<Props> = ({ isOpen, onClose, onSuccess, data }) => { const TransactionModal: React.FC<Props> = ({ open, onOpenChange, onSuccess, data }) => {
const title = data ? 'Edit transaction tag' : 'New transaction tag'; const title = data ? 'Edit transaction tag' : 'New transaction tag';
const text = !data ? 'Label any transaction with a private transaction tag (up to 35 chars) to customize your explorer experience.' : ''; const text = !data ? 'Label any transaction with a private transaction tag (up to 35 chars) to customize your explorer experience.' : '';
const [ isAlertVisible, setAlertVisible ] = useState(false); const [ isAlertVisible, setAlertVisible ] = useState(false);
React.useEffect(() => { React.useEffect(() => {
isOpen && !data?.id && mixpanel.logEvent( open && !data?.id && mixpanel.logEvent(
mixpanel.EventTypes.PRIVATE_TAG, mixpanel.EventTypes.PRIVATE_TAG,
{ Action: 'Form opened', 'Page type': PAGE_TYPE_DICT['/account/tag-address'], 'Tag type': 'Tx' }, { Action: 'Form opened', 'Page type': PAGE_TYPE_DICT['/account/tag-address'], 'Tag type': 'Tx' },
); );
}, [ data?.id, isOpen ]); }, [ data?.id, open ]);
const handleSuccess = React.useCallback(async() => { const handleSuccess = React.useCallback(async() => {
onSuccess?.(); onSuccess?.();
...@@ -39,12 +39,12 @@ const AddressModal: React.FC<Props> = ({ isOpen, onClose, onSuccess, data }) => ...@@ -39,12 +39,12 @@ const AddressModal: React.FC<Props> = ({ isOpen, onClose, onSuccess, data }) =>
}, [ data?.id, onSuccess ]); }, [ data?.id, onSuccess ]);
const renderForm = useCallback(() => { const renderForm = useCallback(() => {
return <TransactionForm data={ data } onClose={ onClose } onSuccess={ handleSuccess } setAlertVisible={ setAlertVisible }/>; return <TransactionForm data={ data } onOpenChange={ onOpenChange } onSuccess={ handleSuccess } setAlertVisible={ setAlertVisible }/>;
}, [ data, handleSuccess, onClose ]); }, [ data, handleSuccess, onOpenChange ]);
return ( return (
<FormModal<TransactionTag> <FormModal<TransactionTag>
isOpen={ isOpen } open={ open }
onClose={ onClose } onOpenChange={ onOpenChange }
title={ title } title={ title }
text={ text } text={ text }
renderForm={ renderForm } renderForm={ renderForm }
...@@ -54,4 +54,4 @@ const AddressModal: React.FC<Props> = ({ isOpen, onClose, onSuccess, data }) => ...@@ -54,4 +54,4 @@ const AddressModal: React.FC<Props> = ({ isOpen, onClose, onSuccess, data }) =>
); );
}; };
export default AddressModal; export default TransactionModal;
...@@ -3,7 +3,7 @@ import React, { useCallback } from 'react'; ...@@ -3,7 +3,7 @@ import React, { useCallback } from 'react';
import type { TransactionTag } from 'types/api/account'; import type { TransactionTag } from 'types/api/account';
import Tag from 'ui/shared/chakra/Tag'; import { Tag } from 'toolkit/chakra/tag';
import TxEntity from 'ui/shared/entities/tx/TxEntity'; import TxEntity from 'ui/shared/entities/tx/TxEntity';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import TableItemActionButtons from 'ui/shared/TableItemActionButtons'; import TableItemActionButtons from 'ui/shared/TableItemActionButtons';
...@@ -34,9 +34,9 @@ const TransactionTagListItem = ({ item, isLoading, onEditClick, onDeleteClick }: ...@@ -34,9 +34,9 @@ const TransactionTagListItem = ({ item, isLoading, onEditClick, onDeleteClick }:
fontWeight={ 600 } fontWeight={ 600 }
maxW="100%" maxW="100%"
/> />
<HStack spacing={ 3 } mt={ 4 }> <HStack gap={ 3 } mt={ 4 }>
<Text fontSize="sm" fontWeight={ 500 }>Private tag</Text> <Text textStyle="sm" fontWeight={ 500 }>Private tag</Text>
<Tag isLoading={ isLoading } isTruncated>{ item.name }</Tag> <Tag loading={ isLoading } truncated>{ item.name }</Tag>
</HStack> </HStack>
</Flex> </Flex>
<TableItemActionButtons onDeleteClick={ onItemDeleteClick } onEditClick={ onItemEditClick } isLoading={ isLoading }/> <TableItemActionButtons onDeleteClick={ onItemDeleteClick } onEditClick={ onItemEditClick } isLoading={ isLoading }/>
......
import {
Table,
Tbody,
Tr,
Th,
} from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { TransactionTags, TransactionTag } from 'types/api/account'; import type { TransactionTags, TransactionTag } from 'types/api/account';
import TheadSticky from 'ui/shared/TheadSticky'; import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';
import TransactionTagTableItem from './TransactionTagTableItem'; import TransactionTagTableItem from './TransactionTagTableItem';
...@@ -20,17 +14,17 @@ interface Props { ...@@ -20,17 +14,17 @@ interface Props {
top: number; top: number;
} }
const AddressTagTable = ({ data, isLoading, onDeleteClick, onEditClick, top }: Props) => { const TransactionTagTable = ({ data, isLoading, onDeleteClick, onEditClick, top }: Props) => {
return ( return (
<Table minWidth="600px"> <TableRoot minWidth="600px">
<TheadSticky top={ top }> <TableHeaderSticky top={ top }>
<Tr> <TableRow>
<Th width="75%">Transaction</Th> <TableColumnHeader width="75%">Transaction</TableColumnHeader>
<Th width="25%">Private tag</Th> <TableColumnHeader width="25%">Private tag</TableColumnHeader>
<Th width="108px"></Th> <TableColumnHeader width="108px"></TableColumnHeader>
</Tr> </TableRow>
</TheadSticky> </TableHeaderSticky>
<Tbody> <TableBody>
{ data?.map((item, index) => ( { data?.map((item, index) => (
<TransactionTagTableItem <TransactionTagTableItem
key={ item.id + (isLoading ? String(index) : '') } key={ item.id + (isLoading ? String(index) : '') }
...@@ -40,9 +34,9 @@ const AddressTagTable = ({ data, isLoading, onDeleteClick, onEditClick, top }: P ...@@ -40,9 +34,9 @@ const AddressTagTable = ({ data, isLoading, onDeleteClick, onEditClick, top }: P
onEditClick={ onEditClick } onEditClick={ onEditClick }
/> />
)) } )) }
</Tbody> </TableBody>
</Table> </TableRoot>
); );
}; };
export default AddressTagTable; export default TransactionTagTable;
import {
Tr,
Td,
} from '@chakra-ui/react';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import type { TransactionTag } from 'types/api/account'; import type { TransactionTag } from 'types/api/account';
import Tag from 'ui/shared/chakra/Tag'; import { TableCell, TableRow } from 'toolkit/chakra/table';
import { Tag } from 'toolkit/chakra/tag';
import TxEntity from 'ui/shared/entities/tx/TxEntity'; import TxEntity from 'ui/shared/entities/tx/TxEntity';
import TableItemActionButtons from 'ui/shared/TableItemActionButtons'; import TableItemActionButtons from 'ui/shared/TableItemActionButtons';
...@@ -27,22 +24,22 @@ const TransactionTagTableItem = ({ item, isLoading, onEditClick, onDeleteClick } ...@@ -27,22 +24,22 @@ const TransactionTagTableItem = ({ item, isLoading, onEditClick, onDeleteClick }
}, [ item, onDeleteClick ]); }, [ item, onDeleteClick ]);
return ( return (
<Tr alignItems="top" key={ item.id }> <TableRow alignItems="top" key={ item.id }>
<Td> <TableCell>
<TxEntity <TxEntity
hash={ item.transaction_hash } hash={ item.transaction_hash }
isLoading={ isLoading } isLoading={ isLoading }
noCopy={ false } noCopy={ false }
fontWeight={ 600 } fontWeight={ 600 }
/> />
</Td> </TableCell>
<Td> <TableCell>
<Tag isLoading={ isLoading } isTruncated>{ item.name }</Tag> <Tag loading={ isLoading } truncated>{ item.name }</Tag>
</Td> </TableCell>
<Td> <TableCell>
<TableItemActionButtons onDeleteClick={ onItemDeleteClick } onEditClick={ onItemEditClick } isLoading={ isLoading }/> <TableItemActionButtons onDeleteClick={ onItemDeleteClick } onEditClick={ onItemEditClick } isLoading={ isLoading }/>
</Td> </TableCell>
</Tr> </TableRow>
); );
}; };
......
...@@ -50,10 +50,10 @@ const AccountActionsMenu = ({ isLoading, className, showUpdateMetadataItem }: Pr ...@@ -50,10 +50,10 @@ const AccountActionsMenu = ({ isLoading, className, showUpdateMetadataItem }: Pr
// render: (props: ItemProps) => <TokenInfoMenuItem { ...props }/>, // render: (props: ItemProps) => <TokenInfoMenuItem { ...props }/>,
// enabled: config.features.account.isEnabled && isTokenPage && config.features.addressVerification.isEnabled && !userWithoutEmail, // enabled: config.features.account.isEnabled && isTokenPage && config.features.addressVerification.isEnabled && !userWithoutEmail,
// }, // },
// { {
// render: (props: ItemProps) => <PrivateTagMenuItem { ...props } entityType={ isTxPage ? 'tx' : 'address' }/>, render: (props: ItemProps) => <PrivateTagMenuItem { ...props } entityType={ isTxPage ? 'tx' : 'address' }/>,
// enabled: config.features.account.isEnabled, enabled: config.features.account.isEnabled,
// }, },
{ {
render: (props: ItemProps) => <PublicTagMenuItem { ...props }/>, render: (props: ItemProps) => <PublicTagMenuItem { ...props }/>,
enabled: config.features.account.isEnabled && !isTxPage && config.features.publicTagsSubmission.isEnabled, enabled: config.features.account.isEnabled && !isTxPage && config.features.publicTagsSubmission.isEnabled,
......
...@@ -46,8 +46,8 @@ const PrivateTagMenuItem = ({ hash, entityType = 'address', type }: Props) => { ...@@ -46,8 +46,8 @@ const PrivateTagMenuItem = ({ hash, entityType = 'address', type }: Props) => {
const pageType = getPageType(router.pathname); const pageType = getPageType(router.pathname);
const modalProps = { const modalProps = {
isOpen: modal.open, open: modal.open,
onClose: modal.onClose, onOpenChange: modal.onOpenChange,
onSuccess: handleAddPrivateTag, onSuccess: handleAddPrivateTag,
pageType, pageType,
}; };
......
import { Box, useColorModeValue } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import { debounce } from 'es-toolkit'; import { debounce } from 'es-toolkit';
import React, { useRef, useEffect, useState, useCallback } from 'react'; import React, { useRef, useEffect, useState, useCallback } from 'react';
...@@ -37,11 +37,6 @@ const AccountPageDescription = ({ children, allowCut = true }: { children: React ...@@ -37,11 +37,6 @@ const AccountPageDescription = ({ children, allowCut = true }: { children: React
setExpanded(true); setExpanded(true);
}, []); }, []);
const gradient = useColorModeValue(
'linear-gradient(360deg, rgba(255, 255, 255, 0.8) 38.1%, rgba(255, 255, 255, 0) 166.67%)',
'linear-gradient(360deg, rgba(16, 17, 18, 0.8) 38.1%, rgba(16, 17, 18, 0) 166.67%)',
);
return ( return (
<Box position="relative" marginBottom={{ base: 6, lg: 8 }}> <Box position="relative" marginBottom={{ base: 6, lg: 8 }}>
<Box <Box
...@@ -59,7 +54,10 @@ const AccountPageDescription = ({ children, allowCut = true }: { children: React ...@@ -59,7 +54,10 @@ const AccountPageDescription = ({ children, allowCut = true }: { children: React
left={ 0 } left={ 0 }
width="100%" width="100%"
height="63px" height="63px"
style={{ background: gradient }} bgGradient={{
_light: 'linear-gradient(360deg, rgba(255, 255, 255, 0.8) 38.1%, rgba(255, 255, 255, 0) 166.67%)',
_dark: 'linear-gradient(360deg, rgba(16, 17, 18, 0.8) 38.1%, rgba(16, 17, 18, 0) 166.67%)',
}}
onClick={ expand } onClick={ expand }
> >
</Box> </Box>
......
import { import { Box } from '@chakra-ui/react';
Box,
Button,
Modal,
ModalOverlay,
ModalContent,
ModalFooter,
ModalHeader,
ModalBody,
ModalCloseButton,
} from '@chakra-ui/react';
import { useMutation } from '@tanstack/react-query'; import { useMutation } from '@tanstack/react-query';
import React, { useCallback, useState } from 'react'; import React, { useCallback, useState } from 'react';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import { Button } from 'toolkit/chakra/button';
import { DialogBody, DialogContent, DialogFooter, DialogHeader, DialogRoot } from 'toolkit/chakra/dialog';
import FormSubmitAlert from 'ui/shared/FormSubmitAlert'; import FormSubmitAlert from 'ui/shared/FormSubmitAlert';
type Props = { type Props = {
isOpen: boolean; open: boolean;
onClose: () => void; onOpenChange: ({ open }: { open: boolean }) => void;
title: string; title: string;
renderContent: () => React.JSX.Element; renderContent: () => React.JSX.Element;
mutationFn: () => Promise<unknown>; mutationFn: () => Promise<unknown>;
...@@ -25,8 +17,8 @@ type Props = { ...@@ -25,8 +17,8 @@ type Props = {
}; };
const DeleteModal: React.FC<Props> = ({ const DeleteModal: React.FC<Props> = ({
isOpen, open,
onClose, onOpenChange,
title, title,
renderContent, renderContent,
mutationFn, mutationFn,
...@@ -34,16 +26,16 @@ const DeleteModal: React.FC<Props> = ({ ...@@ -34,16 +26,16 @@ const DeleteModal: React.FC<Props> = ({
}) => { }) => {
const [ isAlertVisible, setAlertVisible ] = useState(false); const [ isAlertVisible, setAlertVisible ] = useState(false);
const onModalClose = useCallback(() => { const onModalOpenChange = useCallback(({ open }: { open: boolean }) => {
setAlertVisible(false); !open && setAlertVisible(false);
onClose(); onOpenChange({ open });
}, [ onClose, setAlertVisible ]); }, [ onOpenChange, setAlertVisible ]);
const { mutate, isPending } = useMutation({ const { mutate, isPending } = useMutation({
mutationFn, mutationFn,
onSuccess: async() => { onSuccess: async() => {
onSuccess(); onSuccess();
onClose(); onOpenChange({ open: false });
}, },
onError: () => { onError: () => {
setAlertVisible(true); setAlertVisible(true);
...@@ -58,28 +50,24 @@ const DeleteModal: React.FC<Props> = ({ ...@@ -58,28 +50,24 @@ const DeleteModal: React.FC<Props> = ({
const isMobile = useIsMobile(); const isMobile = useIsMobile();
return ( return (
<Modal isOpen={ isOpen } onClose={ onModalClose } size={ isMobile ? 'full' : 'md' }> <DialogRoot open={ open } onOpenChange={ onModalOpenChange } size={ isMobile ? 'full' : 'md' }>
<ModalOverlay/> <DialogContent>
<ModalContent> <DialogHeader fontWeight="500" textStyle="h3">{ title }</DialogHeader>
<ModalHeader fontWeight="500" textStyle="h3">{ title }</ModalHeader> <DialogBody>
<ModalCloseButton/>
<ModalBody>
{ isAlertVisible && <Box mb={ 4 }><FormSubmitAlert/></Box> } { isAlertVisible && <Box mb={ 4 }><FormSubmitAlert/></Box> }
{ renderContent() } { renderContent() }
</ModalBody> </DialogBody>
<ModalFooter> <DialogFooter>
<Button <Button
size="lg" size="lg"
onClick={ onDeleteClick } onClick={ onDeleteClick }
isLoading={ isPending } loading={ isPending }
// FIXME: chackra's button is disabled when isLoading
isDisabled={ false }
> >
Delete Delete
</Button> </Button>
</ModalFooter> </DialogFooter>
</ModalContent> </DialogContent>
</Modal> </DialogRoot>
); );
}; };
......
import { import { Box, Text } from '@chakra-ui/react';
Box,
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalBody,
ModalCloseButton,
Text,
} from '@chakra-ui/react';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import useIsMobile from 'lib/hooks/useIsMobile'; import { DialogBody, DialogContent, DialogHeader, DialogRoot } from 'toolkit/chakra/dialog';
import FormSubmitAlert from 'ui/shared/FormSubmitAlert'; import FormSubmitAlert from 'ui/shared/FormSubmitAlert';
interface Props<TData> { interface Props<TData> {
isOpen: boolean; open: boolean;
onClose: () => void; onOpenChange: ({ open }: { open: boolean }) => void;
data?: TData; data?: TData;
title: string; title: string;
text?: string; text?: string;
...@@ -25,8 +16,8 @@ interface Props<TData> { ...@@ -25,8 +16,8 @@ interface Props<TData> {
} }
export default function FormModal<TData>({ export default function FormModal<TData>({
isOpen, open,
onClose, onOpenChange,
title, title,
text, text,
renderForm, renderForm,
...@@ -34,20 +25,16 @@ export default function FormModal<TData>({ ...@@ -34,20 +25,16 @@ export default function FormModal<TData>({
setAlertVisible, setAlertVisible,
}: Props<TData>) { }: Props<TData>) {
const onModalClose = useCallback(() => { const handleOpenChange = useCallback(({ open }: { open: boolean }) => {
setAlertVisible && setAlertVisible(false); !open && setAlertVisible?.(false);
onClose(); onOpenChange({ open });
}, [ onClose, setAlertVisible ]); }, [ onOpenChange, setAlertVisible ]);
const isMobile = useIsMobile();
return ( return (
<Modal isOpen={ isOpen } onClose={ onModalClose } size={ isMobile ? 'full' : 'md' }> <DialogRoot open={ open } onOpenChange={ handleOpenChange } size={{ lgDown: 'full', lg: 'md' }}>
<ModalOverlay/> <DialogContent>
<ModalContent> <DialogHeader>{ title }</DialogHeader>
<ModalHeader fontWeight="500" textStyle="h3">{ title }</ModalHeader> <DialogBody>
<ModalCloseButton/>
<ModalBody>
{ (isAlertVisible || text) && ( { (isAlertVisible || text) && (
<Box marginBottom={{ base: 6, lg: 8 }}> <Box marginBottom={{ base: 6, lg: 8 }}>
{ text && ( { text && (
...@@ -59,8 +46,8 @@ export default function FormModal<TData>({ ...@@ -59,8 +46,8 @@ export default function FormModal<TData>({
</Box> </Box>
) } ) }
{ renderForm() } { renderForm() }
</ModalBody> </DialogBody>
</ModalContent> </DialogContent>
</Modal> </DialogRoot>
); );
} }
import { Alert, AlertDescription } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { Alert } from 'toolkit/chakra/alert';
const FormSubmitAlert = () => { const FormSubmitAlert = () => {
return ( return (
<Alert status="error"> <Alert status="error">
<AlertDescription>
There has been an error processing your request There has been an error processing your request
</AlertDescription>
</Alert> </Alert>
); );
}; };
......
import { Tooltip, IconButton, HStack } from '@chakra-ui/react'; import { HStack } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import usePreventFocusAfterModalClosing from 'lib/hooks/usePreventFocusAfterModalClosing'; import usePreventFocusAfterModalClosing from 'lib/hooks/usePreventFocusAfterModalClosing';
import Skeleton from 'ui/shared/chakra/Skeleton'; import { IconButton } from 'toolkit/chakra/icon-button';
import { Tooltip } from 'toolkit/chakra/tooltip';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
type Props = { type Props = {
...@@ -14,42 +15,37 @@ type Props = { ...@@ -14,42 +15,37 @@ type Props = {
const TableItemActionButtons = ({ onEditClick, onDeleteClick, isLoading }: Props) => { const TableItemActionButtons = ({ onEditClick, onDeleteClick, isLoading }: Props) => {
const onFocusCapture = usePreventFocusAfterModalClosing(); const onFocusCapture = usePreventFocusAfterModalClosing();
if (isLoading) {
return ( return (
<HStack spacing={ 6 } alignSelf="flex-end"> <HStack gap={ 6 } alignSelf="flex-end">
<Skeleton boxSize={ 5 } flexShrink={ 0 } borderRadius="sm"/> <Tooltip content="Edit">
<Skeleton boxSize={ 5 } flexShrink={ 0 } borderRadius="sm"/>
</HStack>
);
}
return (
<HStack spacing={ 6 } alignSelf="flex-end">
<Tooltip label="Edit">
<IconButton <IconButton
aria-label="edit" aria-label="edit"
variant="simple" variant="link"
boxSize={ 5 } boxSize={ 5 }
onClick={ onEditClick } onClick={ onEditClick }
icon={ <IconSvg name="edit" boxSize={ 5 }/> }
onFocusCapture={ onFocusCapture } onFocusCapture={ onFocusCapture }
loading={ isLoading }
display="inline-block" display="inline-block"
flexShrink={ 0 } flexShrink={ 0 }
borderRadius="none" borderRadius="none"
/> >
<IconSvg name="edit" boxSize={ 5 }/>
</IconButton>
</Tooltip> </Tooltip>
<Tooltip label="Delete"> <Tooltip content="Delete">
<IconButton <IconButton
aria-label="delete" aria-label="delete"
variant="simple" variant="link"
boxSize={ 5 } boxSize={ 5 }
onClick={ onDeleteClick } onClick={ onDeleteClick }
icon={ <IconSvg name="delete" boxSize={ 5 }/> }
onFocusCapture={ onFocusCapture } onFocusCapture={ onFocusCapture }
loading={ isLoading }
display="inline-block" display="inline-block"
flexShrink={ 0 } flexShrink={ 0 }
borderRadius="none" borderRadius="none"
/> >
<IconSvg name="delete" boxSize={ 5 }/>
</IconButton>
</Tooltip> </Tooltip>
</HStack> </HStack>
); );
......
import type { ChakraProps } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { FieldValues, Path } from 'react-hook-form'; import type { FieldValues } from 'react-hook-form';
import type { FormFieldPropsBase } from './types'; import type { FormFieldPropsBase } from './types';
import type { PartialBy } from 'types/utils'; import type { PartialBy } from 'types/utils';
...@@ -31,9 +30,4 @@ const FormFieldAddress = <FormFields extends FieldValues>( ...@@ -31,9 +30,4 @@ const FormFieldAddress = <FormFields extends FieldValues>(
); );
}; };
export type WrappedComponent = < export default React.memo(FormFieldAddress) as typeof FormFieldAddress;
FormFields extends FieldValues,
Name extends Path<FormFields> = Path<FormFields>,
>(props: PartialBy<FormFieldPropsBase<FormFields, Name>, 'placeholder'> & ChakraProps) => React.JSX.Element;
export default React.memo(FormFieldAddress) as WrappedComponent;
...@@ -4,7 +4,7 @@ import type { FieldValues } from 'react-hook-form'; ...@@ -4,7 +4,7 @@ import type { FieldValues } from 'react-hook-form';
import type { FormFieldPropsBase } from './types'; import type { FormFieldPropsBase } from './types';
import { urlValidator } from '../validators/url'; import { urlValidator } from '../validators/url';
import FormFieldText, { type WrappedComponent } from './FormFieldText'; import FormFieldText from './FormFieldText';
const FormFieldUrl = <FormFields extends FieldValues>( const FormFieldUrl = <FormFields extends FieldValues>(
props: FormFieldPropsBase<FormFields>, props: FormFieldPropsBase<FormFields>,
...@@ -23,4 +23,4 @@ const FormFieldUrl = <FormFields extends FieldValues>( ...@@ -23,4 +23,4 @@ const FormFieldUrl = <FormFields extends FieldValues>(
return <FormFieldText { ...props } rules={ rules }/>; return <FormFieldText { ...props } rules={ rules }/>;
}; };
export default React.memo(FormFieldUrl) as WrappedComponent; export default React.memo(FormFieldUrl) as typeof FormFieldUrl;
/* eslint-disable max-len */
import React from 'react';
import { Button } from 'toolkit/chakra/button';
import { DialogActionTrigger, DialogBody, DialogContent, DialogFooter, DialogHeader, DialogRoot, DialogTitle, DialogTrigger } from 'toolkit/chakra/dialog';
import { Section, Container, SectionHeader, SamplesStack, Sample } from './parts';
const CONTENT = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.';
const DialogsShowcase = () => {
return (
<Container value="dialogs">
<Section>
<SectionHeader>Size</SectionHeader>
<SamplesStack>
<Sample label="size: sm">
<DialogRoot size="sm">
<DialogTrigger asChild>
<Button size="sm">
Open Dialog
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Dialog Title</DialogTitle>
</DialogHeader>
<DialogBody>
<p>{ CONTENT }</p>
</DialogBody>
<DialogFooter>
<DialogActionTrigger asChild>
<Button variant="outline">Cancel</Button>
</DialogActionTrigger>
<Button>Save</Button>
</DialogFooter>
</DialogContent>
</DialogRoot>
</Sample>
<Sample label="size: md">
<DialogRoot size="md">
<DialogTrigger asChild>
<Button size="sm">
Open Dialog
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Dialog Title</DialogTitle>
</DialogHeader>
<DialogBody>
<p>{ CONTENT }</p>
</DialogBody>
<DialogFooter>
<DialogActionTrigger asChild>
<Button variant="outline">Cancel</Button>
</DialogActionTrigger>
<Button>Save</Button>
</DialogFooter>
</DialogContent>
</DialogRoot>
</Sample>
<Sample label="size: full">
<DialogRoot size="full">
<DialogTrigger asChild>
<Button size="sm">
Open Dialog
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Dialog Title</DialogTitle>
</DialogHeader>
<DialogBody>
<p>{ CONTENT }</p>
</DialogBody>
<DialogFooter>
<DialogActionTrigger asChild>
<Button variant="outline">Cancel</Button>
</DialogActionTrigger>
<Button>Save</Button>
</DialogFooter>
</DialogContent>
</DialogRoot>
</Sample>
</SamplesStack>
</Section>
</Container>
);
};
export default React.memo(DialogsShowcase);
...@@ -33,6 +33,7 @@ const HeaderMobile = ({ hideSearchBar, renderSearchBar }: Props) => { ...@@ -33,6 +33,7 @@ const HeaderMobile = ({ hideSearchBar, renderSearchBar }: Props) => {
left={ 0 } left={ 0 }
zIndex="sticky2" zIndex="sticky2"
pt="1px" pt="1px"
height="56px"
> >
<Flex <Flex
as="header" as="header"
......
...@@ -18,8 +18,7 @@ const NetworkMenu = () => { ...@@ -18,8 +18,7 @@ const NetworkMenu = () => {
> >
<PopoverTrigger> <PopoverTrigger>
<IconButton <IconButton
color="link.primary" variant="link"
_hover={{ color: 'link.primary.hover' }}
aria-label="Network menu" aria-label="Network menu"
borderRadius="sm" borderRadius="sm"
onClick={ menu.onToggle } onClick={ menu.onToggle }
......
...@@ -21,8 +21,7 @@ const Settings = () => { ...@@ -21,8 +21,7 @@ const Settings = () => {
> >
<PopoverTrigger> <PopoverTrigger>
<IconButton <IconButton
color="link.primary" variant="link"
_hover={{ color: 'link.primary.hover' }}
borderRadius="sm" borderRadius="sm"
aria-label="User settings" aria-label="User settings"
> >
......
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