Commit 5827803b authored by tom's avatar tom

sorting and filter

parent d808658f
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.145 8.091c.198.423.689 1.258.689 1.258L9.508 0 3.97 3.854c-.33.22-.6.517-.788.865a3.989 3.989 0 0 0-.037 3.372ZM.897 11.17a6.277 6.277 0 0 0 2.478 4.573L9.501 20S5.668 14.495 2.436 9.018a5.52 5.52 0 0 1-.65-1.868 2.98 2.98 0 0 1 0-.897c-.084.156-.247.475-.247.475A7.234 7.234 0 0 0 .877 8.84c-.064.776-.057 1.556.02 2.33ZM16.523 11.909c-.198-.423-.689-1.258-.689-1.258L10.16 20l5.538-3.852c.33-.22.6-.516.788-.864a3.99 3.99 0 0 0 .037-3.375ZM18.772 8.83a6.279 6.279 0 0 0-2.48-4.573L10.168 0s3.83 5.505 7.065 10.982a5.52 5.52 0 0 1 .647 1.868c.045.297.045.6 0 .897.084-.156.248-.475.248-.475.328-.666.551-1.378.662-2.112.065-.776.059-1.555-.017-2.33Z" fill="currentColor"/>
<path d="M3.182 4.719c.188-.348.458-.645.788-.865L9.508 0 3.834 9.351s-.496-.835-.69-1.257a3.989 3.989 0 0 1 .038-3.375Zm-2.285 6.45a6.278 6.278 0 0 0 2.478 4.574L9.501 20S5.668 14.495 2.436 9.018a5.52 5.52 0 0 1-.65-1.868 2.98 2.98 0 0 1 0-.897 34.31 34.31 0 0 0-.247.475A7.234 7.234 0 0 0 .877 8.84c-.064.776-.057 1.555.02 2.33Zm15.616.742c-.198-.422-.689-1.258-.689-1.258L10.16 20l5.538-3.852c.33-.22.6-.516.788-.864a3.99 3.99 0 0 0 .037-3.375l-.01.002Zm2.249-3.078a6.277 6.277 0 0 0-2.48-4.574L10.169 0s3.83 5.505 7.064 10.982a5.52 5.52 0 0 1 .647 1.868c.045.297.045.6 0 .897.084-.156.248-.475.248-.475a7.233 7.233 0 0 0 .662-2.112c.064-.776.059-1.555-.018-2.33l-.01.003Z" fill="currentColor" style="mix-blend-mode:color"/>
<svg viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.41 3 7.764 7.625c-.396.263-.72.62-.946 1.038a4.74 4.74 0 0 0-.046.1l-.009.019a4.786 4.786 0 0 0 .01 3.93c.233.508.828 1.51.828 1.51L14.41 3ZM4.076 16.404A15.45 15.45 0 0 1 4 14.886l1.168-3.159a6.617 6.617 0 0 0 .755 2.095C9.802 20.393 14.401 27 14.401 27l-7.35-5.108A7.562 7.562 0 0 1 4.25 17.57a7.53 7.53 0 0 1-.174-1.166ZM4 14.886l1.144-3.306a3.575 3.575 0 0 1 0-1.076c-.101.187-.298.57-.298.57a8.68 8.68 0 0 0-.794 2.534A15.45 15.45 0 0 0 4 14.886ZM15.192 27l.063-.103 6.734-11.113.005.009.007-.012s.589 1.002.827 1.51a4.787 4.787 0 0 1-.045 4.05c-.225.417-.55.774-.946 1.037L15.192 27Zm9.263-7.504c.054-.357.054-.72 0-1.076a6.622 6.622 0 0 0-.776-2.242C19.796 9.607 15.2 3 15.2 3l7.35 5.108a7.533 7.533 0 0 1 2.975 5.488c.091.93.098 1.865.02 2.796a8.68 8.68 0 0 1-.794 2.535s-.196.382-.297.57v-.001Z" fill="currentColor"/>
</svg>
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.145 8.091c.198.423.689 1.258.689 1.258L9.508 0 3.97 3.854c-.33.22-.6.517-.788.865a3.989 3.989 0 0 0-.037 3.372ZM.897 11.17a6.277 6.277 0 0 0 2.478 4.573L9.501 20S5.668 14.495 2.436 9.018a5.52 5.52 0 0 1-.65-1.868 2.98 2.98 0 0 1 0-.897 35.09 35.09 0 0 0-.247.475A7.234 7.234 0 0 0 .877 8.84a12.86 12.86 0 0 0 .02 2.33Zm15.626.739c-.198-.423-.689-1.258-.689-1.258L10.16 20l5.538-3.852c.33-.22.6-.516.788-.864a3.99 3.99 0 0 0 .037-3.375Zm2.249-3.079a6.279 6.279 0 0 0-2.48-4.573L10.168 0s3.83 5.505 7.065 10.982a5.52 5.52 0 0 1 .647 1.868c.045.297.045.6 0 .897.084-.156.248-.475.248-.475a7.223 7.223 0 0 0 .662-2.112c.065-.776.059-1.555-.017-2.33Z" fill="currentColor"/>
<path d="M3.182 4.719c.188-.348.458-.645.788-.865L9.508 0 3.834 9.351s-.496-.835-.69-1.257a3.989 3.989 0 0 1 .038-3.375Zm-2.285 6.45a6.278 6.278 0 0 0 2.478 4.574L9.501 20S5.668 14.495 2.436 9.018a5.52 5.52 0 0 1-.65-1.868 2.98 2.98 0 0 1 0-.897 34.31 34.31 0 0 0-.247.475A7.234 7.234 0 0 0 .877 8.84c-.064.776-.057 1.555.02 2.33Zm15.616.742c-.198-.422-.689-1.258-.689-1.258L10.16 20l5.538-3.852c.33-.22.6-.516.788-.864a3.99 3.99 0 0 0 .037-3.375l-.01.002Zm2.249-3.078a6.277 6.277 0 0 0-2.48-4.574L10.169 0s3.83 5.505 7.064 10.982a5.52 5.52 0 0 1 .647 1.868c.045.297.045.6 0 .897.084-.156.248-.475.248-.475a7.233 7.233 0 0 0 .662-2.112c.064-.776.059-1.555-.018-2.33l-.01.003Z" fill="currentColor" style="mix-blend-mode:color"/>
</svg>
......@@ -636,7 +636,7 @@ export type PaginatedResources = 'blocks' | 'block_txs' |
'zkevm_l2_txn_batches' | 'zkevm_l2_txn_batch_txs' |
'withdrawals' | 'address_withdrawals' | 'block_withdrawals' |
'watchlist' | 'private_tags_address' | 'private_tags_tx' |
'domain_events' | 'domains_lookup';
'domain_events' | 'domains_lookup' | 'addresses_lookup';
export type PaginatedResponse<Q extends PaginatedResources> = ResourcePayload<Q>;
......
......@@ -42,3 +42,12 @@ export interface EnsDomainLookupResponse {
items: Array<EnsDomain>;
totalRecords: number;
}
export interface EnsDomainLookupFilters {
name: string | null;
resolvedTo: boolean;
ownedBy: boolean;
onlyActive: boolean;
}
export type EnsDomainLookupFiltersOptions = Array<'resolvedTo' | 'ownedBy' | 'withInactive'>;
......@@ -3,7 +3,7 @@ import React from 'react';
import config from 'configs/app';
import arrowIcon from 'icons/arrows/east-mini.svg';
import ensIcon from 'icons/ENS.svg';
import ensIcon from 'icons/ENS_slim.svg';
import useApiQuery from 'lib/api/useApiQuery';
import dayjs from 'lib/date/dayjs';
import EnsEntity from 'ui/shared/entities/ens/EnsEntity';
......
import { HStack } from '@chakra-ui/react';
import { Checkbox, CheckboxGroup, HStack, Text } from '@chakra-ui/react';
import React from 'react';
import type { EnsDomainLookupFiltersOptions } from 'types/api/ens';
import type { PaginationParams } from 'ui/shared/pagination/types';
import useIsInitialLoading from 'lib/hooks/useIsInitialLoading';
import ActionBar from 'ui/shared/ActionBar';
import FilterInput from 'ui/shared/filters/FilterInput';
import PopoverFilter from 'ui/shared/filters/PopoverFilter';
import Pagination from 'ui/shared/pagination/Pagination';
import Sort from 'ui/shared/sort/Sort';
import type { Sort as TSort } from './utils';
import { SORT_OPTIONS } from './utils';
const pagination: PaginationParams = {
isVisible: true,
......@@ -23,10 +30,15 @@ interface Props {
pagination?: PaginationParams;
searchTerm: string | undefined;
onSearchChange: (value: string) => void;
inTabsSlot?: boolean;
filterValue: EnsDomainLookupFiltersOptions;
onFilterValueChange: (nextValue: EnsDomainLookupFiltersOptions) => void;
sort: TSort | undefined;
onSortChange: (nextValue: TSort | undefined) => void;
isLoading: boolean;
}
const NameDomainsActionBar = ({ searchTerm, onSearchChange }: Props) => {
const NameDomainsActionBar = ({ searchTerm, onSearchChange, filterValue, onFilterValueChange, sort, onSortChange, isLoading }: Props) => {
const isInitialLoading = useIsInitialLoading(isLoading);
const searchInput = (
<FilterInput
......@@ -35,12 +47,52 @@ const NameDomainsActionBar = ({ searchTerm, onSearchChange }: Props) => {
onChange={ onSearchChange }
placeholder="Search by name"
initialValue={ searchTerm }
isLoading={ isInitialLoading }
/>
);
const filter = (
<PopoverFilter appliedFiltersNum={ filterValue.length } contentProps={{ w: '220px' }} isLoading={ isInitialLoading }>
<div>
<CheckboxGroup size="lg" onChange={ onFilterValueChange } value={ filterValue } defaultValue={ filterValue }>
<Text variant="secondary" fontWeight={ 600 } mb={ 3 } fontSize="sm">Address</Text>
<Checkbox value="ownedBy" display="block">
Owned by
</Checkbox>
<Checkbox
value="resolvedTo"
display="block"
mt={ 5 }
mb={ 4 }
pb={ 4 }
borderBottom="1px solid"
borderColor="divider"
>
Resolved to address
</Checkbox>
<Text variant="secondary" fontWeight={ 600 } mb={ 3 } fontSize="sm">Status</Text>
<Checkbox value="withInactive" display="block">
Include expired
</Checkbox>
</CheckboxGroup>
</div>
</PopoverFilter>
);
const sortButton = (
<Sort
options={ SORT_OPTIONS }
sort={ sort }
setSort={ onSortChange }
isLoading={ isInitialLoading }
/>
);
return (
<>
<HStack spacing={ 3 } mb={ 6 } display={{ base: 'flex', lg: 'none' }}>
{ filter }
{ sortButton }
{ searchInput }
</HStack>
<ActionBar
......@@ -48,6 +100,7 @@ const NameDomainsActionBar = ({ searchTerm, onSearchChange }: Props) => {
display={{ base: pagination.isVisible ? 'flex' : 'none', lg: 'flex' }}
>
<HStack spacing={ 3 } display={{ base: 'none', lg: 'flex' }}>
{ filter }
{ searchInput }
</HStack>
<Pagination { ...pagination } ml="auto"/>
......
......@@ -35,7 +35,7 @@ const NameDomainsListItem = ({ name, isLoading, resolvedAddress, registrationDat
<ListItemMobileGrid.Label isLoading={ isLoading }>Registered on</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<Skeleton isLoaded={ !isLoading }>
{ dayjs(registrationDate).format('llll') }
{ dayjs(registrationDate).format('MMM DD YYYY HH:mm:ss A') }
<chakra.span color="text_secondary"> { dayjs(registrationDate).fromNow() }</chakra.span>
</Skeleton>
</ListItemMobileGrid.Value>
......@@ -47,7 +47,7 @@ const NameDomainsListItem = ({ name, isLoading, resolvedAddress, registrationDat
<ListItemMobileGrid.Label isLoading={ isLoading }>Registered on</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<Skeleton isLoaded={ !isLoading } whiteSpace="pre-wrap">
<span>{ dayjs(expiryDate).format('llll') } </span>
<span>{ dayjs(expiryDate).format('MMM DD YYYY HH:mm:ss A') } </span>
<NameDomainExpiryStatus date={ expiryDate }/>
</Skeleton>
</ListItemMobileGrid.Value>
......
......@@ -25,7 +25,7 @@ const NameDomainsTableItem = ({ isLoading, name, resolvedAddress, registrationDa
<Td verticalAlign="middle" pl={ 9 }>
{ registrationDate && (
<Skeleton isLoaded={ !isLoading }>
{ dayjs(registrationDate).format('llll') }
{ dayjs(registrationDate).format('MMM DD YYYY HH:mm:ss A') }
<chakra.span color="text_secondary"> { dayjs(registrationDate).fromNow() }</chakra.span>
</Skeleton>
) }
......@@ -33,7 +33,7 @@ const NameDomainsTableItem = ({ isLoading, name, resolvedAddress, registrationDa
<Td verticalAlign="middle">
{ expiryDate && (
<Skeleton isLoaded={ !isLoading } whiteSpace="pre-wrap">
<span>{ dayjs(expiryDate).format('llll') } </span>
<span>{ dayjs(expiryDate).format('MMM DD YYYY HH:mm:ss A') } </span>
<NameDomainExpiryStatus date={ expiryDate }/>
</Skeleton>
) }
......
import type { EnsDomain } from 'types/api/ens';
import getNextSortValueShared from 'ui/shared/sort/getNextSortValue';
import type { Option } from 'ui/shared/sort/Sort';
export type SortField = 'registration_date';
export type Sort = `${ SortField }-asc` | `${ SortField }-desc`;
export const SORT_OPTIONS: Array<Option<Sort>> = [
{ title: 'Default', id: undefined },
{ title: 'Registered on descending', id: 'registration_date-desc' },
{ title: 'Registered on ascending', id: 'registration_date-asc' },
];
const SORT_SEQUENCE: Record<SortField, Array<Sort | undefined>> = {
registration_date: [ 'registration_date-desc', 'registration_date-asc', undefined ],
};
......
......@@ -2,8 +2,11 @@ import { Box, Hide, Show } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
import type { EnsDomainLookupFiltersOptions } from 'types/api/ens';
import config from 'configs/app';
import useApiQuery from 'lib/api/useApiQuery';
import useDebounce from 'lib/hooks/useDebounce';
import { apos } from 'lib/html-entities';
import getQueryParamString from 'lib/router/getQueryParamString';
import { ENS_DOMAIN } from 'stubs/ENS';
......@@ -19,24 +22,38 @@ import PageTitle from 'ui/shared/Page/PageTitle';
const NameDomains = () => {
const router = useRouter();
const q = getQueryParamString(router.query.q);
const address = getQueryParamString(router.query.address);
const ownedBy = getQueryParamString(router.query.ownedBy);
const resolvedTo = getQueryParamString(router.query.resolvedTo);
const withInactive = getQueryParamString(router.query.withInactive);
const initialFilters: EnsDomainLookupFiltersOptions = [
ownedBy === 'true' ? 'ownedBy' as const : undefined,
resolvedTo === 'true' ? 'resolvedTo' as const : undefined,
withInactive === 'true' ? 'withInactive' as const : undefined,
].filter(Boolean);
const [ searchTerm, setSearchTerm ] = React.useState<string>(q ?? '');
const [ searchTerm, setSearchTerm ] = React.useState<string>(address ?? '');
const [ filterValue, setFilterValue ] = React.useState<EnsDomainLookupFiltersOptions>(initialFilters);
const [ sort, setSort ] = React.useState<Sort>();
const { isError, isPlaceholderData, data } = useApiQuery('domains_lookup', {
const debouncedSearchTerm = useDebounce(searchTerm || '', 300);
const { isError, isPlaceholderData, data } = useApiQuery('addresses_lookup', {
pathParams: { chainId: config.chain.id },
fetchParams: {
method: 'POST',
body: {
name: 'pepecat🐾.eth',
address: debouncedSearchTerm,
resolvedTo: true,
ownedBy: true,
onlyActive: true,
sort: 'registration_date',
order: 'ASC',
},
},
queryOptions: {
placeholderData: generateListStub<'domains_lookup'>(ENS_DOMAIN, 50, { totalRecords: 50 }),
placeholderData: generateListStub<'addresses_lookup'>(ENS_DOMAIN, 50, { totalRecords: 50 }),
},
});
......@@ -55,6 +72,10 @@ const NameDomains = () => {
setSearchTerm(value);
}, []);
const handleFilterValueChange = React.useCallback((value: EnsDomainLookupFiltersOptions) => {
setFilterValue(value);
}, []);
const hasActiveFilters = Boolean(searchTerm);
const content = (
......@@ -83,8 +104,13 @@ const NameDomains = () => {
const actionBar = (
<NameDomainsActionBar
isLoading={ isPlaceholderData }
searchTerm={ searchTerm }
onSearchChange={ handleSearchTermChange }
filterValue={ filterValue }
onFilterValueChange={ handleFilterValueChange }
sort={ sort }
onSortChange={ setSort }
/>
);
......
......@@ -5,7 +5,7 @@ import React from 'react';
import { route } from 'nextjs-routes';
import ensIcon from 'icons/ENS.svg';
import ensIcon from 'icons/ENS_slim.svg';
import * as EntityBase from 'ui/shared/entities/base/components';
import TruncatedValue from 'ui/shared/TruncatedValue';
......
......@@ -19,7 +19,7 @@ const FilterButton = ({ isActive, isLoading, appliedFiltersNum, onClick, as }: P
const badgeBgColor = useColorModeValue('blue.700', 'gray.50');
if (isLoading) {
return <Skeleton w={{ base: 9, lg: '78px' }} h={ 8 } borderRadius="base"/>;
return <Skeleton w={{ base: 9, lg: '78px' }} h={ 8 } borderRadius="base" flexShrink={ 0 }/>;
}
return (
......
......@@ -37,6 +37,7 @@ const FilterInput = ({ onChange, className, size = 'sm', placeholder, initialVal
isLoaded={ !isLoading }
className={ className }
minW="250px"
borderRadius="base"
>
<InputGroup
size={ size }
......
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