From 2d4d359ec30ae049e675fd1dbf8628da2f6b7073 Mon Sep 17 00:00:00 2001 From: Alexei Mochalov Date: Fri, 26 Apr 2024 10:52:10 +0200 Subject: [PATCH] Catalog: More search UI tweaks (#3967) --- catalog/app/containers/Search/ResultType.tsx | 47 ++++++++++++++++++++ catalog/app/containers/Search/Results.tsx | 40 ++++++++++++----- docs/CHANGELOG.md | 1 + 3 files changed, 78 insertions(+), 10 deletions(-) diff --git a/catalog/app/containers/Search/ResultType.tsx b/catalog/app/containers/Search/ResultType.tsx index fb681f4af3c..e158fbff18a 100644 --- a/catalog/app/containers/Search/ResultType.tsx +++ b/catalog/app/containers/Search/ResultType.tsx @@ -1,6 +1,7 @@ import * as React from 'react' import * as M from '@material-ui/core' +import * as GQL from 'utils/GraphQL' import * as SearchUIModel from './model' const VALUES = [SearchUIModel.ResultType.QuiltPackage, SearchUIModel.ResultType.S3Object] @@ -45,6 +46,10 @@ const useResultTypeStyles = M.makeStyles((t) => ({ icon: { minWidth: t.spacing(3.5), }, + chip: { + backgroundColor: t.palette.text.hint, + color: t.palette.getContrastText(t.palette.text.hint), + }, item: { paddingLeft: t.spacing(1.5), paddingRight: t.spacing(1), @@ -60,10 +65,47 @@ const useResultTypeStyles = M.makeStyles((t) => ({ export default function ResultType() { const classes = useResultTypeStyles() const model = SearchUIModel.use() + + const getTotalSelectedResults = () => { + if (model.firstPageQuery._tag !== 'data') return null + const d = model.firstPageQuery.data + switch (d.__typename) { + case 'ObjectsSearchResultSet': + case 'PackagesSearchResultSet': + return d.stats.total + case 'EmptySearchResultSet': + return 0 + default: + return null + } + } + + const getTotalOtherResults = (resultType: SearchUIModel.ResultType) => + GQL.fold(model.baseSearchQuery, { + data: (data) => { + const r = + resultType === SearchUIModel.ResultType.QuiltPackage + ? data.searchPackages + : data.searchObjects + switch (r.__typename) { + case 'EmptySearchResultSet': + return 0 + case 'ObjectsSearchResultSet': + case 'PackagesSearchResultSet': + return r.stats.total + default: + return null + } + }, + fetching: () => null, + error: () => null, + }) + return ( {VALUES.map((v) => { const selected = model.state.resultType === v + const total = selected ? getTotalSelectedResults() : getTotalOtherResults(v) return ( + {total != null && ( + + + + )} ) })} diff --git a/catalog/app/containers/Search/Results.tsx b/catalog/app/containers/Search/Results.tsx index 30ea5204aa8..43904bdc175 100644 --- a/catalog/app/containers/Search/Results.tsx +++ b/catalog/app/containers/Search/Results.tsx @@ -5,6 +5,7 @@ import * as M from '@material-ui/core' import { ES_REF_SYNTAX } from 'components/SearchResults' import Skeleton from 'components/Skeleton' import { useNavBar } from 'containers/NavBar' +import * as GQL from 'utils/GraphQL' import StyledLink from 'utils/StyledLink' import * as SearchUIModel from './model' @@ -103,6 +104,7 @@ export function EmptyResults({ className }: EmptyResultsProps) { const classes = useEmptyResultsStyles() const { actions: { clearFilters, reset, setBuckets, setResultType }, + baseSearchQuery, state, } = SearchUIModel.use() const focus = useNavBar()?.focus @@ -121,6 +123,29 @@ export function EmptyResults({ className }: EmptyResultsProps) { ? SearchUIModel.ResultType.S3Object : SearchUIModel.ResultType.QuiltPackage + const getTotalResults = (resultType: SearchUIModel.ResultType) => + GQL.fold(baseSearchQuery, { + data: (data) => { + const r = + resultType === SearchUIModel.ResultType.QuiltPackage + ? data.searchPackages + : data.searchObjects + switch (r.__typename) { + case 'EmptySearchResultSet': + return 0 + case 'ObjectsSearchResultSet': + case 'PackagesSearchResultSet': + return r.stats.total + default: + return null + } + }, + fetching: () => null, + error: () => null, + }) + + const totalOtherResults = getTotalResults(otherResultType) + const switchResultType = React.useCallback(() => { setResultType(otherResultType) }, [setResultType, otherResultType]) @@ -136,10 +161,10 @@ export function EmptyResults({ className }: EmptyResultsProps) { - Could not find any {LABELS[state.resultType]} matching your search - criteria. -
- Some suggestions to help you find what you're looking for: + Search for{' '} + {LABELS[otherResultType]}{' '} + instead{totalOtherResults != null && ` (${totalOtherResults} found)`} or adjust + your search:
    @@ -154,12 +179,7 @@ export function EmptyResults({ className }: EmptyResultsProps) { )}
  • - Search for{' '} - {LABELS[otherResultType]}{' '} - instead -
  • -
  • - Adjust your search query + Edit your search query
  • Start from scratch diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index e54b6d01f30..bcfcfb06d49 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -18,6 +18,7 @@ Entries inside each section should be ordered by type: ## Python API * [Added] `quilt3.search()` and `quilt3.Bucket.search()` now accept custom Elasticsearch queries ([#3448](https://github.com/quiltdata/quilt/pull/3448)) * [Fixed] `quilt3.search()` and `quilt3.Bucket.search()` now work with 2022+ Quilt stacks ([#3448](https://github.com/quiltdata/quilt/pull/3448)) +* [Changed] Search UI QoL improvements ([#3967](https://github.com/quiltdata/quilt/pull/3967)) ## Catalog, Lambdas * [Added] Added "text" as a file type for quilt_summarize.json ([#3946](https://github.com/quiltdata/quilt/pull/3946))