From f622f947ecfafbb23dc34bd20ff5463263f1e6c7 Mon Sep 17 00:00:00 2001 From: Pierre Gauthier Date: Mon, 25 Mar 2024 11:12:55 +0100 Subject: [PATCH] feat(#1275390): Autocomplete attribute Example App --- Makefile | 4 + docker-compose.connectors.yml | 18 ++++ .../src/components/Facets/FacetSlider.tsx | 9 +- .../src/components/SearchBar/SearchBar.tsx | 88 ++++++++++++++++--- front/example-app/src/constants/facet.ts | 1 + front/example-app/src/constants/index.ts | 1 + front/example-app/src/contexts/search.ts | 2 +- front/example-app/src/types/facet.ts | 8 ++ 8 files changed, 115 insertions(+), 16 deletions(-) create mode 100644 docker-compose.connectors.yml create mode 100644 front/example-app/src/constants/facet.ts diff --git a/Makefile b/Makefile index c95e452f6..7f018da2e 100644 --- a/Makefile +++ b/Makefile @@ -33,6 +33,10 @@ up: ## Start the docker hub in detached mode (no logs) $(MAKE) .env @$(DOCKER_COMP) up --detach +up-connectors: ## Start the docker hub in detached mode with connectors conf (no logs) + $(MAKE) .env + @$(DOCKER_COMP) -f docker-compose.yml -f docker-compose.override.yml -f docker-compose.connectors.yml up --detach + start: build up ## Build and start the containers down: ## Stop the docker hub diff --git a/docker-compose.connectors.yml b/docker-compose.connectors.yml new file mode 100644 index 000000000..34fdd0a24 --- /dev/null +++ b/docker-compose.connectors.yml @@ -0,0 +1,18 @@ +version: "3.4" + +services: + php: + environment: + TRUSTED_HOSTS: ^${SERVER_NAME:-example\.com|localhost}|caddy$|gally.localhost$$ + + caddy: + networks: + default: + aliases: + - 'gally.localhost' + environment: + SERVER_NAME: gally.localhost + +networks: + default: + name: 'connectors-proxy' diff --git a/front/example-app/src/components/Facets/FacetSlider.tsx b/front/example-app/src/components/Facets/FacetSlider.tsx index da7d5f2d9..12135c4ee 100644 --- a/front/example-app/src/components/Facets/FacetSlider.tsx +++ b/front/example-app/src/components/Facets/FacetSlider.tsx @@ -21,12 +21,13 @@ function FacetSlider(props: IProps): JSX.Element { value: min, label: min, }, - { + ] + if (min !== max) { + marks.push({ value: max, label: max, - }, - ] - + }) + } function handleChange(_: Event, value: number | number[]): void { onChange(filter, String(value))() } diff --git a/front/example-app/src/components/SearchBar/SearchBar.tsx b/front/example-app/src/components/SearchBar/SearchBar.tsx index 39f946d28..6ac9d3c05 100644 --- a/front/example-app/src/components/SearchBar/SearchBar.tsx +++ b/front/example-app/src/components/SearchBar/SearchBar.tsx @@ -31,6 +31,7 @@ import { searchContext, } from '../../contexts' import { + IGraphqlAggregationOption, IGraphqlSearchProducts, IGraphqlSearchProductsVariables, ProductRequestType, @@ -41,11 +42,16 @@ import { joinUrlPath, } from '@elastic-suite/gally-admin-shared' import { useGraphqlApi } from '../../hooks' -import { ICategoryAutoComplete, IProductAutoComplete } from '../../types' +import { + ICategoryAutoComplete, + IFacetAutocomplete, + IProductAutoComplete, +} from '../../types' import { IGraphqlSearchDocumentsVariables } from '@elastic-suite/gally-admin-shared/src/types/documents' import { IGraphqlSearchCategories } from '@elastic-suite/gally-admin-shared/src' import { AUTOCOMPLETE_CATEGORY_TYPE, + AUTOCOMPLETE_FACET_TYPE, AUTOCOMPLETE_PRODUCT_TYPE, } from '../../constants' import { useNavigate } from 'react-router-dom' @@ -58,7 +64,7 @@ interface IProps { const StyledAutocomplete = styled( Autocomplete< - IProductAutoComplete | ICategoryAutoComplete, + IProductAutoComplete | ICategoryAutoComplete | IFacetAutocomplete, boolean, boolean, boolean @@ -87,13 +93,16 @@ const StyledPopper = styled(Popper)(() => ({ width: 540, }, })) - function SearchBar(props: IProps): JSX.Element { const { shrink } = props const searchId = useId() const { localizedCatalogId } = useContext(catalogContext) - const { onSearch, search: searchedText } = useContext(searchContext) + const { + onSearch, + search: searchedText, + productSearch: { setActiveFilters }, + } = useContext(searchContext) const baseUrl = useContext(configurationsContext)?.['base_url/media'] const categories = useContext(categoryContext) @@ -105,7 +114,11 @@ function SearchBar(props: IProps): JSX.Element { IGraphqlSearchProducts & IGraphqlSearchCategories >() - let options: (IProductAutoComplete | ICategoryAutoComplete)[] = + let options: ( + | IProductAutoComplete + | ICategoryAutoComplete + | IFacetAutocomplete + )[] = documents.data?.products.collection.map((product) => { return { ...product, @@ -120,7 +133,24 @@ function SearchBar(props: IProps): JSX.Element { type: AUTOCOMPLETE_CATEGORY_TYPE, })) ?? [] ) - + documents.data?.products?.aggregations?.forEach((aggregation) => { + let aggregationOptions: IGraphqlAggregationOption[] = [] + if (aggregation.type === 'checkbox') { + aggregationOptions = aggregation.options + } else if (aggregation.type === 'boolean') { + aggregationOptions = aggregation.options.filter( + (option) => option.value === '1' + ) + } + options = options.concat( + aggregationOptions.map((option) => ({ + ...aggregation, + type: AUTOCOMPLETE_FACET_TYPE, + fieldType: aggregation.type, + option, + })) + ) + }) const loadDocuments = useCallback( (querySearch: string) => { if ( @@ -130,7 +160,7 @@ function SearchBar(props: IProps): JSX.Element { ) { const productVariables: IGraphqlSearchProductsVariables = { localizedCatalog: String(localizedCatalogId), - requestType: ProductRequestType.SEARCH, + requestType: ProductRequestType.AUTOCOMPLETE, currentPage: 1, pageSize: 5, search: querySearch, @@ -154,7 +184,7 @@ function SearchBar(props: IProps): JSX.Element { getAutoCompleteSearchQuery( null, { equalFilter: { field: 'is_active', eq: 'true' } }, - false + true ), variables as unknown as Record, { signal: controller.current.signal } @@ -175,7 +205,7 @@ function SearchBar(props: IProps): JSX.Element { function handleSearchChange( _event: ChangeEvent, - value: IProductAutoComplete | ICategoryAutoComplete + value: IProductAutoComplete | ICategoryAutoComplete | IFacetAutocomplete ): void { let querySearch = '' @@ -185,6 +215,20 @@ function SearchBar(props: IProps): JSX.Element { if (value?.type === AUTOCOMPLETE_PRODUCT_TYPE) { querySearch = value.name + setActiveFilters([]) + } + + if (value?.type === AUTOCOMPLETE_FACET_TYPE) { + querySearch = search + setActiveFilters([ + { + filter: { + ...value, + type: value.fieldType, + }, + value: value.option.value, + }, + ]) } setSearch(querySearch) if (querySearch.trim()) { @@ -198,9 +242,9 @@ function SearchBar(props: IProps): JSX.Element { function handleSubmit(event: FormEvent): void { event.preventDefault() + setActiveFilters([]) onSearch(search) } - return (
)} renderOption={(props, option): ReactNode => { + let key + // To avoid Facets to have the same keys + if (option.type === AUTOCOMPLETE_FACET_TYPE) { + key = `${option.label} ${option.option.label} ${option.option.value}` + } return ( -
  • +
  • {option.type === AUTOCOMPLETE_PRODUCT_TYPE && ( <> @@ -314,6 +368,18 @@ function SearchBar(props: IProps): JSX.Element { )} + {option.type === AUTOCOMPLETE_FACET_TYPE && ( + + {option.label} + + + {option.fieldType === 'boolean' + ? 'Oui' + : option.option.label} + + + + )}
  • ) diff --git a/front/example-app/src/constants/facet.ts b/front/example-app/src/constants/facet.ts new file mode 100644 index 000000000..988a3ec9b --- /dev/null +++ b/front/example-app/src/constants/facet.ts @@ -0,0 +1 @@ +export const AUTOCOMPLETE_FACET_TYPE = 'aggregation' diff --git a/front/example-app/src/constants/index.ts b/front/example-app/src/constants/index.ts index abdc79feb..44e9bbe88 100644 --- a/front/example-app/src/constants/index.ts +++ b/front/example-app/src/constants/index.ts @@ -2,3 +2,4 @@ export * from './category' export * from './graphql' export * from './product' export * from './routes' +export * from './facet' diff --git a/front/example-app/src/contexts/search.ts b/front/example-app/src/contexts/search.ts index a131af813..34a0e51ae 100644 --- a/front/example-app/src/contexts/search.ts +++ b/front/example-app/src/contexts/search.ts @@ -3,7 +3,7 @@ import { createContext } from 'react' import { IDocumentsHook, IProductsHook } from '../types' export interface ISearchContext { - search: string + search: string, onSearch: (search: string) => void productSearch: IProductsHook cmsPageSearch: IDocumentsHook diff --git a/front/example-app/src/types/facet.ts b/front/example-app/src/types/facet.ts index 4d7aa3ffc..7e9a87769 100644 --- a/front/example-app/src/types/facet.ts +++ b/front/example-app/src/types/facet.ts @@ -1,6 +1,8 @@ import { + AggregationType, IFetch, IGraphqlAggregation, + IGraphqlAggregationOption, IGraphqlViewMoreFacetOption, } from '@elastic-suite/gally-admin-shared' @@ -20,3 +22,9 @@ export type IFilterMoreOptions = Map< IGraphqlAggregation, IFetch > + +export interface IFacetAutocomplete extends Omit { + fieldType: AggregationType + type: 'aggregation' + option: IGraphqlAggregationOption +}