feat: UI search on nfas list (#211)
* wip: created context for explore view * feat: add search to context to filter on query * feat: add search value to query * feat: add search by name, external URL and ens * feat: search by name * chore: remove comments * chore: rename files explore view (#217) * chore: rename files explore view * Update ui/src/views/explore/explore-list/results-search.tsx Co-authored-by: Felipe Mendes <zo.fmendes@gmail.com> * chore: rename nfa search fragment --------- Co-authored-by: Felipe Mendes <zo.fmendes@gmail.com> * chore: update setPageNumber --------- Co-authored-by: Felipe Mendes <zo.fmendes@gmail.com>
This commit is contained in:
parent
e9275ed04f
commit
09e50adefc
|
|
@ -1,9 +1,10 @@
|
||||||
query lastNFAsPaginated($pageSize: Int, $skip: Int) {
|
query lastNFAsPaginated($pageSize: Int, $skip: Int, $searchValue: String) {
|
||||||
tokens(
|
tokens(
|
||||||
first: $pageSize
|
first: $pageSize
|
||||||
skip: $skip
|
skip: $skip
|
||||||
orderDirection: desc
|
orderDirection: desc
|
||||||
orderBy: tokenId
|
orderBy: tokenId
|
||||||
|
where: { name_contains_nocase: $searchValue }
|
||||||
) {
|
) {
|
||||||
id
|
id
|
||||||
tokenId
|
tokenId
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { Flex } from '@/components';
|
import { Flex } from '@/components';
|
||||||
import { styled } from '@/theme';
|
import { styled } from '@/theme';
|
||||||
|
|
||||||
export abstract class Header {
|
export abstract class ExploreHeaderStyles {
|
||||||
static readonly Container = styled(Flex, {
|
static readonly Container = styled(Flex, {
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
gap: '$6',
|
gap: '$6',
|
||||||
|
|
@ -2,17 +2,17 @@ import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import { Button } from '@/components';
|
import { Button } from '@/components';
|
||||||
|
|
||||||
import { Header as HS } from './header.styles';
|
import { ExploreHeaderStyles as S } from './explore-header.styles';
|
||||||
|
|
||||||
export const Header: React.FC = () => (
|
export const ExploreHeader: React.FC = () => (
|
||||||
<HS.Container>
|
<S.Container>
|
||||||
<HS.Text>
|
<S.Text>
|
||||||
<HS.GrayText>
|
<S.GrayText>
|
||||||
Created with a focus on decentralizing your applications,
|
Created with a focus on decentralizing your applications,
|
||||||
</HS.GrayText>
|
</S.GrayText>
|
||||||
<HS.WhiteText> NFAs are the only thing you need.</HS.WhiteText>
|
<S.WhiteText> NFAs are the only thing you need.</S.WhiteText>
|
||||||
</HS.Text>
|
</S.Text>
|
||||||
<HS.ButtonContainer>
|
<S.ButtonContainer>
|
||||||
<Button as={Link} to="/mint" colorScheme="blue" variant="outline">
|
<Button as={Link} to="/mint" colorScheme="blue" variant="outline">
|
||||||
Create an NFA
|
Create an NFA
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -20,6 +20,6 @@ export const Header: React.FC = () => (
|
||||||
<Button as={Link} to="/create-ap" colorScheme="gray" variant="outline">
|
<Button as={Link} to="/create-ap" colorScheme="gray" variant="outline">
|
||||||
Host an Access Point
|
Host an Access Point
|
||||||
</Button>
|
</Button>
|
||||||
</HS.ButtonContainer>
|
</S.ButtonContainer>
|
||||||
</HS.Container>
|
</S.Container>
|
||||||
);
|
);
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { Flex } from '@/components';
|
||||||
|
|
||||||
|
import { Explore } from '../explore.context';
|
||||||
|
import { NFAListFragment } from './nfa-list';
|
||||||
|
import { NFASearchFragment } from './nfa-search';
|
||||||
|
|
||||||
|
export const ExploreListContainer: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<Flex css={{ flexDirection: 'column' }}>
|
||||||
|
<Explore.Provider>
|
||||||
|
<NFASearchFragment />
|
||||||
|
<NFAListFragment />
|
||||||
|
</Explore.Provider>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './explore-list-container';
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
import { useQuery } from '@apollo/client';
|
import { useQuery } from '@apollo/client';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
import { Flex, NFACard, NFACardSkeleton, NoResults } from '@/components';
|
import { Flex, NFACard, NFACardSkeleton, NoResults } from '@/components';
|
||||||
import { lastNFAsPaginatedDocument } from '@/graphclient';
|
import { lastNFAsPaginatedDocument } from '@/graphclient';
|
||||||
import { useWindowScrollEnd } from '@/hooks';
|
import { useWindowScrollEnd } from '@/hooks';
|
||||||
|
|
||||||
|
import { Explore } from '../../explore.context';
|
||||||
|
|
||||||
const pageSize = 10; //Set this size to test pagination
|
const pageSize = 10; //Set this size to test pagination
|
||||||
|
|
||||||
const LoadingSkeletons: React.FC = () => (
|
const LoadingSkeletons: React.FC = () => (
|
||||||
|
|
@ -16,9 +18,9 @@ const LoadingSkeletons: React.FC = () => (
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const NFAList: React.FC = () => {
|
export const NFAListFragment: React.FC = () => {
|
||||||
const [pageNumber, setPageNumber] = useState(0);
|
const { endReached, pageNumber, search, setEndReached, setPageNumber } =
|
||||||
const [endReached, setEndReached] = useState(false);
|
Explore.useContext();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: { tokens } = { tokens: [] },
|
data: { tokens } = { tokens: [] },
|
||||||
|
|
@ -28,7 +30,8 @@ export const NFAList: React.FC = () => {
|
||||||
fetchPolicy: 'cache-and-network',
|
fetchPolicy: 'cache-and-network',
|
||||||
variables: {
|
variables: {
|
||||||
pageSize,
|
pageSize,
|
||||||
skip: pageNumber * pageSize,
|
searchValue: search,
|
||||||
|
skip: pageNumber * pageSize, //skip is for the pagination
|
||||||
},
|
},
|
||||||
onCompleted: (data) => {
|
onCompleted: (data) => {
|
||||||
if (data.tokens.length - tokens.length < pageSize) setEndReached(true);
|
if (data.tokens.length - tokens.length < pageSize) setEndReached(true);
|
||||||
|
|
@ -43,7 +46,7 @@ export const NFAList: React.FC = () => {
|
||||||
|
|
||||||
useWindowScrollEnd(() => {
|
useWindowScrollEnd(() => {
|
||||||
if (isLoading || endReached || queryError) return;
|
if (isLoading || endReached || queryError) return;
|
||||||
setPageNumber((prevState) => prevState + 1);
|
setPageNumber(pageNumber + 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (queryError) return <div>Error</div>; //TODO handle error
|
if (queryError) return <div>Error</div>; //TODO handle error
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
import { Dropdown, DropdownItem, Flex, Input } from '@/components';
|
import { Dropdown, DropdownItem, Flex, Input } from '@/components';
|
||||||
|
import { useDebounce } from '@/hooks';
|
||||||
|
|
||||||
|
import { Explore } from '../explore.context';
|
||||||
import { ResultsContainer, ResultsNumber, ResultsText } from './results.styles';
|
import { ResultsContainer, ResultsNumber, ResultsText } from './results.styles';
|
||||||
|
|
||||||
const orderResults: DropdownItem[] = [
|
const orderResults: DropdownItem[] = [
|
||||||
|
|
@ -9,11 +11,21 @@ const orderResults: DropdownItem[] = [
|
||||||
{ value: 'z-a', label: 'Sort Z-A' },
|
{ value: 'z-a', label: 'Sort Z-A' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const ResultsSearch: React.FC = () => {
|
export const NFASearchFragment: React.FC = () => {
|
||||||
|
const { setSearch } = Explore.useContext();
|
||||||
const [selectedValue, setSelectedValue] = useState<DropdownItem>(
|
const [selectedValue, setSelectedValue] = useState<DropdownItem>(
|
||||||
orderResults[0]
|
orderResults[0]
|
||||||
); //TODO replace for context
|
); //TODO replace for context
|
||||||
|
|
||||||
|
const handleSearch = useDebounce(
|
||||||
|
(searchValue: string) => setSearch(searchValue),
|
||||||
|
200
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
|
handleSearch(e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex css={{ justifyContent: 'space-between' }}>
|
<Flex css={{ justifyContent: 'space-between' }}>
|
||||||
<ResultsContainer>
|
<ResultsContainer>
|
||||||
|
|
@ -25,6 +37,7 @@ export const ResultsSearch: React.FC = () => {
|
||||||
placeholder="Search"
|
placeholder="Search"
|
||||||
leftIcon="search"
|
leftIcon="search"
|
||||||
css={{ width: '23rem' }}
|
css={{ width: '23rem' }}
|
||||||
|
onChange={handleSearchChange}
|
||||||
/>
|
/>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
items={orderResults}
|
items={orderResults}
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import { OrderDirection } from '@/../.graphclient';
|
||||||
|
import { createContext } from '@/utils';
|
||||||
|
|
||||||
|
export type ExploreContext = {
|
||||||
|
search: string;
|
||||||
|
orderBy: string;
|
||||||
|
orderDirection: OrderDirection;
|
||||||
|
pageNumber: number;
|
||||||
|
endReached: boolean;
|
||||||
|
setSearch: (search: string) => void;
|
||||||
|
setOrderBy: (orderBy: string) => void;
|
||||||
|
setOrderDirection: (orderDirection: OrderDirection) => void;
|
||||||
|
setPageNumber: (pageNumber: number) => void;
|
||||||
|
setEndReached: (isEndReaced: boolean) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const [ExploreProvider, useContext] = createContext<ExploreContext>({
|
||||||
|
name: 'Explore.Context',
|
||||||
|
hookName: 'Explore.useContext',
|
||||||
|
providerName: 'Explore.Provider',
|
||||||
|
});
|
||||||
|
|
||||||
|
export abstract class Explore {
|
||||||
|
static readonly useContext = useContext;
|
||||||
|
|
||||||
|
static readonly Provider: React.FC<Explore.ProviderProps> = ({
|
||||||
|
children,
|
||||||
|
}: Explore.ProviderProps) => {
|
||||||
|
const [search, setSearch] = useState('');
|
||||||
|
const [orderBy, setOrderBy] = useState('');
|
||||||
|
const [orderDirection, setOrderDirection] =
|
||||||
|
useState<OrderDirection>('desc');
|
||||||
|
const [pageNumber, setPageNumber] = useState(0);
|
||||||
|
const [endReached, setEndReached] = useState(false);
|
||||||
|
|
||||||
|
const context = {
|
||||||
|
search,
|
||||||
|
orderBy,
|
||||||
|
orderDirection,
|
||||||
|
pageNumber,
|
||||||
|
endReached,
|
||||||
|
setSearch,
|
||||||
|
setOrderBy,
|
||||||
|
setOrderDirection,
|
||||||
|
setPageNumber,
|
||||||
|
setEndReached,
|
||||||
|
};
|
||||||
|
|
||||||
|
return <ExploreProvider value={context}>{children}</ExploreProvider>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace Explore {
|
||||||
|
export type ProviderProps = {
|
||||||
|
children: React.ReactNode;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import { Explore as ES } from './explore.styles';
|
import { Explore as ES } from './explore.styles';
|
||||||
import { Header } from './header';
|
import { ExploreHeader } from './explore-header';
|
||||||
import { ListNfas } from './list-nfas';
|
import { ExploreListContainer } from './explore-list';
|
||||||
|
|
||||||
export const Explore: React.FC = () => {
|
export const Explore: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<ES.Container>
|
<ES.Container>
|
||||||
<Header />
|
<ExploreHeader />
|
||||||
<ListNfas />
|
<ExploreListContainer />
|
||||||
</ES.Container>
|
</ES.Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
export * from './list-nfas';
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
import { Flex } from '@/components';
|
|
||||||
|
|
||||||
import { NFAList } from './nfa-list';
|
|
||||||
import { ResultsSearch } from './results-search';
|
|
||||||
|
|
||||||
export const ListNfas: React.FC = () => {
|
|
||||||
return (
|
|
||||||
<Flex css={{ flexDirection: 'column' }}>
|
|
||||||
<ResultsSearch />
|
|
||||||
<NFAList />
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
Loading…
Reference in New Issue