From 8ba3ec9dc9c99ea156e9e82e5c0f2342df66db0e Mon Sep 17 00:00:00 2001 From: Steven Date: Fri, 14 Jun 2024 15:27:20 +0200 Subject: [PATCH] feat(explorer): search page (#2258) --- .../compact-table-desktop.tsx | 2 +- .../compact-table-desktop/field-cell.tsx | 2 +- .../search-component/search-component.tsx | 111 ++++++----- .../search/search-results/search-results.tsx | 178 ++++++++++++++++++ .../statistics-stack/statistics-stack.tsx | 1 + .../apps/explorer/src/constants/search.ts | 17 -- .../src/graphql/fragments/account.graph.ts | 1 + .../src/graphql/queries/block-height.graph.ts | 15 ++ .../queries/blocks-from-height.graph.ts | 33 ++++ .../apps/explorer/src/hooks/search/index.ts | 153 +++++++++++++++ .../src/hooks/search/utils/account.ts | 71 +++++++ .../src/hooks/search/utils/block-hash.ts | 32 ++++ .../src/hooks/search/utils/block-height.ts | 43 +++++ .../explorer/src/hooks/search/utils/event.ts | 31 +++ .../explorer/src/hooks/search/utils/utils.ts | 27 +++ packages/apps/explorer/src/pages/index.tsx | 80 ++++++-- packages/apps/explorer/src/services/format.ts | 3 +- 17 files changed, 718 insertions(+), 82 deletions(-) create mode 100644 packages/apps/explorer/src/components/search/search-results/search-results.tsx delete mode 100644 packages/apps/explorer/src/constants/search.ts create mode 100644 packages/apps/explorer/src/graphql/queries/block-height.graph.ts create mode 100644 packages/apps/explorer/src/graphql/queries/blocks-from-height.graph.ts create mode 100644 packages/apps/explorer/src/hooks/search/index.ts create mode 100644 packages/apps/explorer/src/hooks/search/utils/account.ts create mode 100644 packages/apps/explorer/src/hooks/search/utils/block-hash.ts create mode 100644 packages/apps/explorer/src/hooks/search/utils/block-height.ts create mode 100644 packages/apps/explorer/src/hooks/search/utils/event.ts create mode 100644 packages/apps/explorer/src/hooks/search/utils/utils.ts diff --git a/packages/apps/explorer/src/components/compact-table/compact-table-desktop/compact-table-desktop.tsx b/packages/apps/explorer/src/components/compact-table/compact-table-desktop/compact-table-desktop.tsx index 566e17fffd..558bc10ba7 100644 --- a/packages/apps/explorer/src/components/compact-table/compact-table-desktop/compact-table-desktop.tsx +++ b/packages/apps/explorer/src/components/compact-table/compact-table-desktop/compact-table-desktop.tsx @@ -16,7 +16,7 @@ import { tableClass } from './styles.css'; const CompactTableDesktop: FC = ({ data, fields, - label, + label = 'Table', }) => { return ( diff --git a/packages/apps/explorer/src/components/compact-table/compact-table-desktop/field-cell.tsx b/packages/apps/explorer/src/components/compact-table/compact-table-desktop/field-cell.tsx index 7efb767a9e..b28033ff36 100644 --- a/packages/apps/explorer/src/components/compact-table/compact-table-desktop/field-cell.tsx +++ b/packages/apps/explorer/src/components/compact-table/compact-table-desktop/field-cell.tsx @@ -15,7 +15,7 @@ const getItem = (item: IProps['item'], key: ITableField['key']) => { const value = keyArr.reduce((acc, val) => { if (!acc) return; const newItem = acc[val]; - if (!newItem) return; + if (newItem === undefined || newItem === null) return; return newItem; }, item); diff --git a/packages/apps/explorer/src/components/search/search-component/search-component.tsx b/packages/apps/explorer/src/components/search/search-component/search-component.tsx index 3276672186..c5cb3b0e04 100644 --- a/packages/apps/explorer/src/components/search/search-component/search-component.tsx +++ b/packages/apps/explorer/src/components/search/search-component/search-component.tsx @@ -1,8 +1,11 @@ +import { SearchOptionEnum } from '@/hooks/search/utils/utils'; import { truncateValues } from '@/services/format'; +import type { ApolloError } from '@apollo/client'; import { MonoSearch } from '@kadena/react-icons/system'; import { Badge, Box } from '@kadena/react-ui'; import { atoms } from '@kadena/react-ui/styles'; -import React, { useState } from 'react'; +import type { Dispatch, SetStateAction } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { searchBadgeBoxClass, searchBoxClass, @@ -18,29 +21,48 @@ export type SearchItemTitle = export interface ISearchItem { title: SearchItemTitle; - disabled?: boolean; + data?: any; } -interface ISearchComponentProps { - placeholder: string; - searchItems: ISearchItem[]; +export interface ISearchComponentProps { + searchData: ISearchItem[]; + setSearchQuery?: Dispatch>; + searchQuery?: string; + searchOption: SearchOptionEnum | null; + setSearchOption: Dispatch>; + loading: boolean; + errors: ApolloError[]; } const SearchComponent: React.FC = ({ - placeholder, - searchItems, + searchData, + setSearchQuery, + searchQuery, + searchOption, + setSearchOption, }) => { const [isEditing, setIsEditing] = useState(false); - const [searchOption, setSearchOption] = useState(null); const [searchValue, setSearchValue] = useState(''); const [optionClicked, setOptionClicked] = useState(false); const [escapePressed, setEscapePressed] = useState(false); + const ref = useRef(null); - const setOptionsDisabledExcept = (exceptIndex: number): void => { - searchItems.forEach((item, index) => { - if (index !== exceptIndex) { - item.disabled = true; - } - }); + const handleSearchOption = ( + inferedOption: SearchItemTitle | undefined, + ): void => { + if (inferedOption === 'Account') { + setSearchOption(SearchOptionEnum.ACCOUNT); + } + if (inferedOption === 'Request Key') { + setSearchOption(SearchOptionEnum.REQUESTKEY); + } + + if (inferedOption === 'Block Height') { + setSearchOption(SearchOptionEnum.BLOCKHEIGHT); + } + + if (!inferedOption || inferedOption === undefined) { + setSearchOption(null); + } }; const inferOption = (value: string): SearchItemTitle | undefined => { @@ -60,14 +82,11 @@ const SearchComponent: React.FC = ({ return undefined; }; - const enableAllOptions = (): void => { - searchItems.forEach((item) => { - item.disabled = false; - }); + const handleSearch = (): void => { + const value = ref.current?.value ?? ''; + if (setSearchQuery) setSearchQuery(value); }; - const handleSearch = (value: string, option: number | null): void => {}; - const handleSearchValueChange = ( e: React.ChangeEvent, ): void => { @@ -76,24 +95,7 @@ const SearchComponent: React.FC = ({ if (escapePressed || optionClicked) return; const inferedOption = inferOption(e.target.value); - if (inferedOption === 'Account') { - setSearchOption(0); - setOptionsDisabledExcept(0); - } - if (inferedOption === 'Request Key') { - setSearchOption(1); - setOptionsDisabledExcept(1); - } - - if (inferedOption === 'Block Height') { - setSearchOption(2); - setOptionsDisabledExcept(2); - } - - if (!inferedOption || inferedOption === undefined) { - setSearchOption(null); - enableAllOptions(); - } + handleSearchOption(inferedOption); }; const handleSearchValueKeyDown = ( @@ -102,7 +104,7 @@ const SearchComponent: React.FC = ({ if (e.key === 'ArrowDown') { e.preventDefault(); setSearchOption((prev) => - prev === null ? 0 : Math.min(prev + 1, searchItems.length - 1), + prev === null ? 0 : Math.min(prev + 1, searchData.length - 1), ); } else if (e.key === 'ArrowUp') { e.preventDefault(); @@ -112,15 +114,22 @@ const SearchComponent: React.FC = ({ setIsEditing(false); setEscapePressed(false); setOptionClicked(false); - handleSearch(searchValue, searchOption); + handleSearch(); } else if (e.key === 'Escape') { setOptionClicked(false); setSearchOption(null); setEscapePressed(true); - enableAllOptions(); + setIsEditing(false); + } else { + setEscapePressed(false); + setOptionClicked(false); } }; + useEffect(() => { + setSearchValue(searchQuery ?? ''); + }, [searchQuery]); + return ( <> = ({ handleSearchValueChange(e)} - onFocus={() => setIsEditing(true)} + onClick={() => setIsEditing((v) => !v)} className={searchInputClass} /> @@ -163,7 +173,9 @@ const SearchComponent: React.FC = ({ justifyContent={'flex-end'} className={searchBadgeBoxClass} > - {searchItems[searchOption].title} + {searchData[searchOption] && ( + {searchData[searchOption].title} + )} )} @@ -179,15 +191,14 @@ const SearchComponent: React.FC = ({ fontFamily: 'primaryFont', })} > - {searchItems.map((item, index) => ( + {searchData?.map((item, index) => ( setOptionClicked(true)} onClick={() => { - if (!item.disabled) { - setSearchOption(index); - setIsEditing(false); - } + handleSearch(); + setSearchOption(index); + setIsEditing(false); }} style={{ gridTemplateColumns: '1fr 3fr', @@ -197,7 +208,7 @@ const SearchComponent: React.FC = ({ display: 'grid', alignItems: 'flex-start', paddingInlineStart: 'md', - cursor: item.disabled ? 'not-allowed' : 'pointer', + cursor: 'pointer', backgroundColor: index === searchOption ? 'base.@active' : 'base.default', width: '100%', diff --git a/packages/apps/explorer/src/components/search/search-results/search-results.tsx b/packages/apps/explorer/src/components/search/search-results/search-results.tsx new file mode 100644 index 0000000000..7dacefc111 --- /dev/null +++ b/packages/apps/explorer/src/components/search/search-results/search-results.tsx @@ -0,0 +1,178 @@ +import CompactTable from '@/components/compact-table/compact-table'; +import { FormatAmount } from '@/components/compact-table/utils/format-amount'; +import { FormatLink } from '@/components/compact-table/utils/format-link'; +import { FormatStatus } from '@/components/compact-table/utils/format-status'; +import { SearchOptionEnum } from '@/hooks/search/utils/utils'; +import type { ApolloError } from '@apollo/client'; +import type { FC } from 'react'; +import React from 'react'; +import type { ISearchItem } from '../search-component/search-component'; + +export interface ISearchResultsProps { + searchData: ISearchItem[]; + loading: boolean; + errors: ApolloError[]; +} + +const SearchResults: FC = ({ + searchData, + loading, + errors, +}) => { + console.log(searchData[SearchOptionEnum.REQUESTKEY]); + return ( + <> + {loading &&
Loading...
} + {errors?.length > 0 && ( +
Error: {JSON.stringify(errors, null, 2)}
+ )} + +
+ {searchData[SearchOptionEnum.ACCOUNT].data.length > 0 && ( + + )} + + {searchData[SearchOptionEnum.BLOCKHEIGHT].data?.blocksFromHeight && ( + + )} + {searchData[SearchOptionEnum.BLOCKHASH].data?.block && ( + + )} + + {searchData[SearchOptionEnum.REQUESTKEY].data?.transaction && ( + + )} + + {searchData[SearchOptionEnum.EVENT].data?.events.edges?.length > 0 && ( + + )} +
+ + ); +}; + +export default SearchResults; diff --git a/packages/apps/explorer/src/components/statistics-component/statistics-stack/statistics-stack.tsx b/packages/apps/explorer/src/components/statistics-component/statistics-stack/statistics-stack.tsx index 77e0772347..d3cbf641e8 100644 --- a/packages/apps/explorer/src/components/statistics-component/statistics-stack/statistics-stack.tsx +++ b/packages/apps/explorer/src/components/statistics-component/statistics-stack/statistics-stack.tsx @@ -46,6 +46,7 @@ const StatisticsStack: React.FC = ({ data }) => {