From 9e9c82679fcbd8d679c64460787136fb70799500 Mon Sep 17 00:00:00 2001 From: baerlach-git <83013270+baerlach-git@users.noreply.github.com> Date: Wed, 21 Jun 2023 13:39:21 +0200 Subject: [PATCH 01/34] integrated meilisearch into the searchview --- .../components/views/search/SearchView.tsx | 45 +++++++++++++++---- .../helpers/getSearchResultPictureIds.ts | 3 +- projects/bp-gallery/src/helpers/growthbook.ts | 1 + projects/bp-strapi/config/plugins.js | 1 + 4 files changed, 40 insertions(+), 10 deletions(-) diff --git a/projects/bp-gallery/src/components/views/search/SearchView.tsx b/projects/bp-gallery/src/components/views/search/SearchView.tsx index 66ec9fbcb..af0e192af 100644 --- a/projects/bp-gallery/src/components/views/search/SearchView.tsx +++ b/projects/bp-gallery/src/components/views/search/SearchView.tsx @@ -2,6 +2,9 @@ import { Location } from 'history'; import { useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useLocation } from 'react-router-dom'; +import usePromise from 'react-use-promise'; +import { PictureFiltersInput } from '../../../graphql/APIConnector'; +import { useFlag } from '../../../helpers/growthbook'; import useBulkOperations from '../../../hooks/bulk-operations.hook'; import { HelpTooltip } from '../../common/HelpTooltip'; import PictureScrollGrid from '../../common/picture-gallery/PictureScrollGrid'; @@ -44,7 +47,6 @@ const SearchView = () => { // Builds query from search params in the path const queryParams = useMemo(() => { - // if (isAllSearchActive) { const allSearchTerms = searchParams .getAll(toURLSearchParam(SearchType.ALL)) .map(decodeURIComponent); @@ -59,11 +61,32 @@ const SearchView = () => { searchTerms: allSearchTerms.filter(searchTerm => !isValidTimeSpecification(searchTerm)), searchTimes, }; - // } - // return convertSearchParamsToPictureFilters(searchParams); - }, [/*isAllSearchActive,*/ searchParams]); - if (import.meta.env.MODE === 'development') - getSearchResultPictureIds(queryParams, '').then(res => console.log('search results:', res)); + }, [searchParams]); + + const [searchResultIds, error, state] = usePromise( + async () => + (await getSearchResultPictureIds(queryParams, '')).map(hit => (hit.id as number).toString()), + [queryParams] + ); + + const pictureFilter: PictureFiltersInput = useMemo(() => { + if (error) { + console.log(error); + return {}; + } + return state === 'resolved' ? { id: { in: searchResultIds } } : {}; + }, [searchResultIds, state, error]); + console.log(pictureFilter); + const isOldSearchActive = useFlag('old_search'); + + if (import.meta.env.MODE === 'development') { + // getSearchResultPictureIds(queryParams, '').then(res => console.log('search results:', res)); + console.log('resultids', searchResultIds); + console.log( + '31149 key:', + searchResultIds?.findIndex(element => element === '31149') + ); + } const { linkToCollection, bulkEdit } = useBulkOperations(); return ( @@ -84,8 +107,14 @@ const SearchView = () => { ) : ( { diff --git a/projects/bp-gallery/src/components/views/search/helpers/getSearchResultPictureIds.ts b/projects/bp-gallery/src/components/views/search/helpers/getSearchResultPictureIds.ts index 9492f44bf..2d4aa41fe 100644 --- a/projects/bp-gallery/src/components/views/search/helpers/getSearchResultPictureIds.ts +++ b/projects/bp-gallery/src/components/views/search/helpers/getSearchResultPictureIds.ts @@ -30,13 +30,12 @@ const getSearchResultPictureIds = async ( filter = filter === '' ? timeFilter : filter.concat(' AND ', timeFilter); } + // for reference: https://www.meilisearch.com/docs/reference/api/search const RESULT_LIMIT = 1000; - // this makes it so only documents that match all of the query terms are returned const MATCHING_STRATEGY = 'all'; const settings = { limit: RESULT_LIMIT, - showMatchesPosition: true, matchingStrategy: MATCHING_STRATEGY, filter: filter, }; diff --git a/projects/bp-gallery/src/helpers/growthbook.ts b/projects/bp-gallery/src/helpers/growthbook.ts index 893cbcb22..9955286ef 100644 --- a/projects/bp-gallery/src/helpers/growthbook.ts +++ b/projects/bp-gallery/src/helpers/growthbook.ts @@ -19,6 +19,7 @@ export type AppFeatures = { }; geopictures_collection_id: string; start_view_default_tab_index: number; + old_search: boolean; }; export type FeatureId = keyof AppFeatures; diff --git a/projects/bp-strapi/config/plugins.js b/projects/bp-strapi/config/plugins.js index 1346ce148..003d881ff 100755 --- a/projects/bp-strapi/config/plugins.js +++ b/projects/bp-strapi/config/plugins.js @@ -107,6 +107,7 @@ module.exports = ({ env }) => ({ return transformedEntry; }, settings: { + //for reference: https://www.meilisearch.com/docs/reference/api/settings displayedAttributes: ["id"], // the order of the attributes in searchableAttributes determines the priorization // of search results i.e. a match in the first searchable attribute will always outrank a match in any other searchable attribute From d3404cc5c836392920776ce23b31f3ac901fa277 Mon Sep 17 00:00:00 2001 From: baerlach-git <83013270+baerlach-git@users.noreply.github.com> Date: Wed, 21 Jun 2023 13:54:59 +0200 Subject: [PATCH 02/34] added the additional dependencies and exteneded gitignore --- .gitignore | 2 ++ projects/bp-gallery/package.json | 1 + projects/bp-gallery/yarn.lock | 5 +++++ 3 files changed, 8 insertions(+) diff --git a/.gitignore b/.gitignore index 3e3810abb..47c8505ab 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ .vscode/**/* !.vscode/launch.json remote-debug-profile/** +projects/bp-gallery/vite.config.ts +projects/bp-gallery/.eslintrc diff --git a/projects/bp-gallery/package.json b/projects/bp-gallery/package.json index a3efe5692..cb5b72601 100755 --- a/projects/bp-gallery/package.json +++ b/projects/bp-gallery/package.json @@ -46,6 +46,7 @@ "react-router-config": "^5.1.1", "react-router-dom": "^5.3.0", "react-scroll-parallax": "^3.3.2", + "react-use-promise": "^0.5.0", "react-use-storage-state": "^1.0.5", "tailwindcss": "^3.2.4", "tui-image-editor": "^3.15.3", diff --git a/projects/bp-gallery/yarn.lock b/projects/bp-gallery/yarn.lock index 1cdbf9f4e..9b73e0edc 100755 --- a/projects/bp-gallery/yarn.lock +++ b/projects/bp-gallery/yarn.lock @@ -7126,6 +7126,11 @@ react-transition-group@^4.4.5: loose-envify "^1.4.0" prop-types "^15.6.2" +react-use-promise@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/react-use-promise/-/react-use-promise-0.5.0.tgz#1abe26f73a9d55415c081ef5be74087840bac0a8" + integrity sha512-kpmnwDsXILLsEe4lxMT2iaJKQq1Wt4KuW2t6YPXwulAWSvnqwPAlj8wrRMQ5d/0+SutKR2qk41yJpeaV49bvUQ== + react-use-storage-state@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/react-use-storage-state/-/react-use-storage-state-1.0.5.tgz#9b63690d59181ddb6b84d2dfa5fec86087b57eec" From 71baf38a4fcd442458fa13d66276500cb6805314 Mon Sep 17 00:00:00 2001 From: baerlach-git <83013270+baerlach-git@users.noreply.github.com> Date: Wed, 21 Jun 2023 14:01:39 +0200 Subject: [PATCH 03/34] reset gitignore to previous state --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 47c8505ab..e9d2a8dab 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,4 @@ .vscode/**/* !.vscode/launch.json remote-debug-profile/** -projects/bp-gallery/vite.config.ts -projects/bp-gallery/.eslintrc + From ebb239085710b4e4da20bfec17910697663944e1 Mon Sep 17 00:00:00 2001 From: baerlach-git <83013270+baerlach-git@users.noreply.github.com> Date: Thu, 22 Jun 2023 16:39:11 +0200 Subject: [PATCH 04/34] started working on the advanced search --- .../views/search/AdvancedSearch.tsx | 70 +++++++++++++++++++ .../components/views/search/SearchView.tsx | 2 + .../bp-gallery/src/shared/locales/de.json | 13 ++++ 3 files changed, 85 insertions(+) create mode 100644 projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx diff --git a/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx b/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx new file mode 100644 index 000000000..1e7c5e9e4 --- /dev/null +++ b/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx @@ -0,0 +1,70 @@ +import { MenuItem, Select, TextField } from '@mui/material'; +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; + +const AdvancedSearch = ({ setFilters }: { setFilters: (filters: string) => void }) => { + const { t } = useTranslation(); + const BASIC_FILTER_OPTIONS = ['default', 'equal', 'unequal', 'is-empty', 'is-not-empty']; + const ADDITIONAL_TIME_FILTER_OPTIONS = ['lower', 'lower-equal', 'greater', 'greater-equal', 'to']; + + const ATTRIBUTES = [ + 'keyword', + 'description', + 'comment', + 'person', + 'face-tag', + 'location', + 'collection', + 'archive', + 'time-range', + ]; + + const FILTER_KEYWORDS = { + keyword: 'keyword_tags', + description: 'descriptions', + comment: 'comments', + person: 'person_tags', + 'face-tag': 'face_tags', + location: 'location_tags', + collection: 'collections', + archive: 'archive_tag', + }; + const TIME_START = 'time_range_tag_start'; + const TIME_END = 'time_range_tag_end'; + + const IS_TEXT = 'is_text'; + + const [keywordFilter, SetKeywordFilter] = useState(''); + const [descriptionFilter, SetDescriptionFilter] = useState(''); + const [commentFilter, SetCommentFilter] = useState(''); + const [personFilter, SetPersonFilter] = useState(''); + const [faceTagFilter, SetFaceTagFilter] = useState(''); + const [locationFilter, SetLocationFilter] = useState(''); + const [collectionFilter, SetCollecionFilter] = useState(''); + const [archiveFilter, SetArchiveFilter] = useState(''); + const [timeRangeFilter, SetTimeFilter] = useState(''); + + return ( +
+
+
+ {ATTRIBUTES.map(attr => ( +
+ {t(`search.${attr}`)} + + +
+ ))} +
+
+
+ ); +}; + +export default AdvancedSearch; diff --git a/projects/bp-gallery/src/components/views/search/SearchView.tsx b/projects/bp-gallery/src/components/views/search/SearchView.tsx index af0e192af..541de4c8d 100644 --- a/projects/bp-gallery/src/components/views/search/SearchView.tsx +++ b/projects/bp-gallery/src/components/views/search/SearchView.tsx @@ -9,6 +9,7 @@ import useBulkOperations from '../../../hooks/bulk-operations.hook'; import { HelpTooltip } from '../../common/HelpTooltip'; import PictureScrollGrid from '../../common/picture-gallery/PictureScrollGrid'; import { ShowStats } from '../../provider/ShowStatsProvider'; +import AdvancedSearch from './AdvancedSearch'; import NoSearchResultsText from './NoSearchResultsText'; import SearchBar from './SearchBar'; import SearchBreadcrumbs from './SearchBreadcrumbs'; @@ -91,6 +92,7 @@ const SearchView = () => { return (
+ {}}>
{' '} {(!areResultsEmpty || !search) && ( diff --git a/projects/bp-gallery/src/shared/locales/de.json b/projects/bp-gallery/src/shared/locales/de.json index 6e377aa41..7ef14c043 100755 --- a/projects/bp-gallery/src/shared/locales/de.json +++ b/projects/bp-gallery/src/shared/locales/de.json @@ -128,10 +128,23 @@ "decade": "Jahrzehnte", "keyword": "Schlagworte", "description": "Beschreibungen", + "comment": "Kommentare", "person": "Personen", + "face-tag": "Personenmarkierungen", "location": "Orte", "collection": "Sammlungen", "archive": "Archive", + "time-range": "Zeiträume", + "default": "Nichts ausgewählt", + "equal": "enthalten", + "unequal": "enthalten nicht", + "is-empty": "sind leer", + "is-not-empty": "sind nicht leer", + "lower": "kleiner als", + "lower-equal": "kleiner gleich als", + "greater": "größer als", + "greater-equal": "größer gleich als", + "to": "von ... bis ...", "all": "Alle Bildinformationen", "start-search": "Suche starten", "picture-found": "Bild wurde gefunden", From f9a0b6156a62b7a606bce89205fb731c5d052054 Mon Sep 17 00:00:00 2001 From: baerlach-git <83013270+baerlach-git@users.noreply.github.com> Date: Fri, 23 Jun 2023 15:23:21 +0200 Subject: [PATCH 05/34] startet building the filter inputs --- .../views/search/AdvancedSearch.tsx | 150 ++++++++++++++---- .../components/views/search/SearchView.tsx | 14 +- .../bp-gallery/src/shared/locales/de.json | 2 +- 3 files changed, 132 insertions(+), 34 deletions(-) diff --git a/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx b/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx index 1e7c5e9e4..95ac1ba9d 100644 --- a/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx +++ b/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx @@ -1,12 +1,116 @@ import { MenuItem, Select, TextField } from '@mui/material'; -import { useState } from 'react'; +import { useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; -const AdvancedSearch = ({ setFilters }: { setFilters: (filters: string) => void }) => { +type SearchFilters = { + keywordFilters: string[]; + descriptionFilters: string[]; + commentFilters: string[]; + personFilters: string[]; + faceTagFilters: string[]; + locationFilters: string[]; + collectionFilters: string[]; + archiveFilters: string[]; + timeRangeFilters: string[]; +}; + +type FilterProps = { + attribute: string; + operatorOption: string; + firstValue: string; + secondValue: string; +}; + +const SearchFilterInput = ({ key, attribute }: { key: string; attribute: string }) => { + const [rowKeys, SetRowKeys] = useState([0]); + + return rowKeys.map(key => ( + + )); +}; + +const SearchFilterInputItem = ({ key, attribute }: { key: string; attribute: string }) => { const { t } = useTranslation(); - const BASIC_FILTER_OPTIONS = ['default', 'equal', 'unequal', 'is-empty', 'is-not-empty']; - const ADDITIONAL_TIME_FILTER_OPTIONS = ['lower', 'lower-equal', 'greater', 'greater-equal', 'to']; + const Text_FILTER_OPTIONS = ['default', 'equal', 'unequal', 'is-empty', 'is-not-empty']; + const TIME_FILTER_OPTIONS = ['default', 'to', 'lower', 'lower-equal', 'greater', 'greater-equal']; + const Filter_COMBINATION_OPTIONS = ['default', 'and', 'or']; + const filterOPtions = attribute === 'timeRange' ? TIME_FILTER_OPTIONS : Text_FILTER_OPTIONS; + + const [displayTwoTextFields, SetDisplayTwoTextFields] = useState(false); + const [filterProps, SetFilterProps] = useState({ + attribute: attribute, + operatorOption: '', + firstValue: '', + secondValue: '', + }); + + const setFilterOperatorOption = useCallback( + (option: string) => { + SetFilterProps(filterProps => ({ ...filterProps, operatorOption: option })); + SetDisplayTwoTextFields(option === 'to'); + }, + [SetFilterProps] + ); + + const setFilterValue = useCallback( + (key: T, value: string) => { + SetFilterProps(filterProps => ({ ...filterProps, [key]: value })); + }, + [SetFilterProps] + ); + + const setFilterCombinationOption = useCallback(() => {}, []); + console.log(key); + + return ( +
+ {t(`search.${attribute}`)} + + {displayTwoTextFields ? ( + <> + setFilterValue('firstValue', event.target.value)} + > + setFilterValue('secondValue', event.target.value)} + > + + ) : ( + setFilterValue('firstValue', event.target.value)}> + )} + + +
+ ); +}; +const AdvancedSearch = ({ + setFilters, + searchFilters, +}: { + setFilters: (filters: string) => void; + searchFilters: SearchFilters; +}) => { const ATTRIBUTES = [ 'keyword', 'description', @@ -16,7 +120,7 @@ const AdvancedSearch = ({ setFilters }: { setFilters: (filters: string) => void 'location', 'collection', 'archive', - 'time-range', + 'timeRange', ]; const FILTER_KEYWORDS = { @@ -34,35 +138,19 @@ const AdvancedSearch = ({ setFilters }: { setFilters: (filters: string) => void const IS_TEXT = 'is_text'; - const [keywordFilter, SetKeywordFilter] = useState(''); - const [descriptionFilter, SetDescriptionFilter] = useState(''); - const [commentFilter, SetCommentFilter] = useState(''); - const [personFilter, SetPersonFilter] = useState(''); - const [faceTagFilter, SetFaceTagFilter] = useState(''); - const [locationFilter, SetLocationFilter] = useState(''); - const [collectionFilter, SetCollecionFilter] = useState(''); - const [archiveFilter, SetArchiveFilter] = useState(''); - const [timeRangeFilter, SetTimeFilter] = useState(''); + // const [keywordFilter, SetKeywordFilter] = useState(''); + // const [descriptionFilter, SetDescriptionFilter] = useState(''); + // const [commentFilter, SetCommentFilter] = useState(''); + // const [personFilter, SetPersonFilter] = useState(''); + // const [faceTagFilter, SetFaceTagFilter] = useState(''); + // const [locationFilter, SetLocationFilter] = useState(''); + // const [collectionFilter, SetCollecionFilter] = useState(''); + // const [archiveFilter, SetArchiveFilter] = useState(''); + // const [timeRangeFilter, SetTimeFilter] = useState(''); return (
-
-
- {ATTRIBUTES.map(attr => ( -
- {t(`search.${attr}`)} - - -
- ))} -
-
+
); }; diff --git a/projects/bp-gallery/src/components/views/search/SearchView.tsx b/projects/bp-gallery/src/components/views/search/SearchView.tsx index 541de4c8d..604a558e9 100644 --- a/projects/bp-gallery/src/components/views/search/SearchView.tsx +++ b/projects/bp-gallery/src/components/views/search/SearchView.tsx @@ -90,11 +90,21 @@ const SearchView = () => { } const { linkToCollection, bulkEdit } = useBulkOperations(); + const [searchFilters, setSearchFilters] = useState({ + keywordFilters: [], + descriptionFilters: [], + commentFilters: [], + personFilters: [], + faceTagFilters: [], + locationFilters: [], + collectionFilters: [], + archiveFilters: [], + timeRangeFilters: [], + }); return (
- {}}> + {}} searchFilters={searchFilters}>
- {' '} {(!areResultsEmpty || !search) && ( )} diff --git a/projects/bp-gallery/src/shared/locales/de.json b/projects/bp-gallery/src/shared/locales/de.json index 7ef14c043..4bb0d868d 100755 --- a/projects/bp-gallery/src/shared/locales/de.json +++ b/projects/bp-gallery/src/shared/locales/de.json @@ -134,7 +134,7 @@ "location": "Orte", "collection": "Sammlungen", "archive": "Archive", - "time-range": "Zeiträume", + "timeRange": "Zeitspannen", "default": "Nichts ausgewählt", "equal": "enthalten", "unequal": "enthalten nicht", From 0fec384788721b15c8e6fdbc13aee8a898929f27 Mon Sep 17 00:00:00 2001 From: baerlach-git <83013270+baerlach-git@users.noreply.github.com> Date: Sun, 25 Jun 2023 21:05:13 +0200 Subject: [PATCH 06/34] worked on the methods for building filters --- .../views/search/AdvancedSearch.tsx | 236 +++++++++++++++--- .../bp-gallery/src/shared/locales/de.json | 2 + 2 files changed, 202 insertions(+), 36 deletions(-) diff --git a/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx b/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx index 95ac1ba9d..23a2b30fc 100644 --- a/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx +++ b/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx @@ -1,5 +1,5 @@ import { MenuItem, Select, TextField } from '@mui/material'; -import { useCallback, useState } from 'react'; +import { useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; type SearchFilters = { @@ -19,39 +19,200 @@ type FilterProps = { operatorOption: string; firstValue: string; secondValue: string; + combinatorOption: string; }; const SearchFilterInput = ({ key, attribute }: { key: string; attribute: string }) => { const [rowKeys, SetRowKeys] = useState([0]); + const [filters, SetFilters] = useState(['']); + console.log(rowKeys); - return rowKeys.map(key => ( - - )); + const deleteRow = useCallback( + (key: string) => { + if (rowKeys.length > 1) { + SetRowKeys(current => current.splice(parseInt(key), 1)); + SetFilters(current => current.splice(parseInt(key), 0, '')); + } + }, + [rowKeys] + ); + + const incrementRows = useCallback(() => { + SetRowKeys(current => current.splice(rowKeys.length, 0, rowKeys[rowKeys.length - 1] + 1)); + }, [rowKeys]); + + const filter = useMemo(() => { + return filters.join(' '); + }, [filters]); + + return ( + <> + {rowKeys.map(key => ( + + ))} + + ); }; -const SearchFilterInputItem = ({ key, attribute }: { key: string; attribute: string }) => { +const SearchFilterInputItem = ({ + key, + attribute, + deleteRow, + incrementRows, +}: { + key: string; + attribute: string; + deleteRow: (key: string) => void; + incrementRows: () => void; +}) => { const { t } = useTranslation(); - const Text_FILTER_OPTIONS = ['default', 'equal', 'unequal', 'is-empty', 'is-not-empty']; - const TIME_FILTER_OPTIONS = ['default', 'to', 'lower', 'lower-equal', 'greater', 'greater-equal']; - const Filter_COMBINATION_OPTIONS = ['default', 'and', 'or']; - const filterOPtions = attribute === 'timeRange' ? TIME_FILTER_OPTIONS : Text_FILTER_OPTIONS; + const DEFAULT_OPTION = 'default'; + const Text_FILTER_OPERATOR_OPTIONS = [ + DEFAULT_OPTION, + 'equal', + 'unequal', + 'is-empty', + 'is-not-empty', + ]; + const TIME_FILTER_OPERATOR_OPTIONS = [ + DEFAULT_OPTION, + 'span', + 'lower', + 'lower-equal', + 'greater', + 'greater-equal', + 'is-empty', + 'is-not-empty', + ]; + const Filter_COMBINATOR_OPTIONS = [DEFAULT_OPTION, 'and', 'or']; + const filterOperatorOptions = + attribute === 'timeRange' ? TIME_FILTER_OPERATOR_OPTIONS : Text_FILTER_OPERATOR_OPTIONS; - const [displayTwoTextFields, SetDisplayTwoTextFields] = useState(false); + const [displayedTextFieldsAmount, SetDisplayedTextFieldsAmount] = useState(1); const [filterProps, SetFilterProps] = useState({ attribute: attribute, operatorOption: '', firstValue: '', secondValue: '', + combinatorOption: '', }); - const setFilterOperatorOption = useCallback( - (option: string) => { - SetFilterProps(filterProps => ({ ...filterProps, operatorOption: option })); - SetDisplayTwoTextFields(option === 'to'); + const optionTranslator = useCallback((option: string) => { + switch (option) { + case 'equal': + return '='; + case 'unequal': + return '!='; + case 'lower': + return '<'; + case 'lower-equal': + return '<='; + case 'greater': + return '>'; + case 'greater-equal': + return '>='; + case 'and': + return 'and'; + case 'or': + return 'or'; + default: + return ''; + } + }, []); + + const attributeTranslator = useCallback((attribute: string) => { + switch (attribute) { + case 'keyword': + return 'keyword_tags'; + case 'description': + return 'descriptions'; + case 'comment': + return 'comments'; + case 'person': + return 'person_tags'; + case 'face-tag': + return 'face_tags'; + case 'location': + return 'location_tags'; + case 'collection': + return 'collections'; + case 'archive': + return 'archive_tag'; + default: + return ''; + } + }, []); + + const buildFilter = useCallback( + ({ + attribute, + operatorOption, + firstValue, + secondValue, + combinatorOption, + }: { + attribute: string; + operatorOption: string; + firstValue: string; + secondValue: string; + combinatorOption: string; + }) => { + const TIME_START = 'time_range_tag_start'; + const TIME_END = 'time_range_tag_end'; + + if (operatorOption === 'is-empty') { + return attribute !== 'timeRange' + ? `(${attributeTranslator(attribute)} IS EMPTY OR ${attributeTranslator( + attribute + )} IS NULL) ${optionTranslator(operatorOption)}` + : `((${TIME_START} IS EMPTY OR ${TIME_START} IS NULL) AND (${TIME_END} IS EMPTY OR ${TIME_END} IS NULL)) ${optionTranslator( + combinatorOption + )}`; + } else if (operatorOption === 'is-not-empty') { + return attribute !== 'timeRange' + ? `(${attributeTranslator(attribute)} IS NOT EMPTY AND ${attributeTranslator( + attribute + )} IS NOT NULL) ${optionTranslator(operatorOption)}` + : `(${TIME_START} IS NOT EMPTY AND ${TIME_START} IS NOT NULL AND ${TIME_END} IS NOT EMPTY AND ${TIME_END} IS NOT NULL) ${optionTranslator( + combinatorOption + )}`; + } + + if (attribute === 'timeRange') { + return operatorOption === 'span' + ? `(${TIME_START} >= ${firstValue} AND ${TIME_END} <= ${secondValue}) ${optionTranslator( + combinatorOption + )}` + : `(${TIME_START} ${optionTranslator( + operatorOption + )} ${firstValue} AND ${TIME_END} ${optionTranslator( + operatorOption + )} ${firstValue}) ${optionTranslator(combinatorOption)}`; + } else { + return `${attributeTranslator(attribute)} ${optionTranslator( + operatorOption + )} ${firstValue} ${optionTranslator(combinatorOption)}`; + } }, - [SetFilterProps] + [optionTranslator, attributeTranslator] ); + const filter = useMemo(() => { + return buildFilter(filterProps); + }, [filterProps, buildFilter]); + + const setFilterOperatorOption = useCallback((option: string) => { + SetFilterProps(filterProps => ({ ...filterProps, operatorOption: option })); + SetDisplayedTextFieldsAmount( + option === 'span' ? 2 : option === 'is-empty' || option === 'is-not-empty' ? 0 : 1 + ); + }, []); + const setFilterValue = useCallback( (key: T, value: string) => { SetFilterProps(filterProps => ({ ...filterProps, [key]: value })); @@ -59,8 +220,18 @@ const SearchFilterInputItem = ({ key, attribute }: { key: string; attribute: str [SetFilterProps] ); - const setFilterCombinationOption = useCallback(() => {}, []); - console.log(key); + const setFilterCombinatorOption = useCallback( + (option: string) => { + if (option === DEFAULT_OPTION) { + SetFilterProps(filterProps => ({ ...filterProps, combinatorOption: '' })); + deleteRow(key); + } else { + SetFilterProps(filterProps => ({ ...filterProps, combinatorOption: option })); + incrementRows(); + } + }, + [deleteRow, incrementRows, key] + ); return (
@@ -70,13 +241,13 @@ const SearchFilterInputItem = ({ key, attribute }: { key: string; attribute: str defaultValue={'default'} renderValue={value => t(`search.${value}`)} > - {filterOPtions.map(option => ( + {filterOperatorOptions.map(option => ( {t(`search.${option}`)} ))} - {displayTwoTextFields ? ( + {displayedTextFieldsAmount === 2 ? ( <> setFilterValue('firstValue', event.target.value)} @@ -85,16 +256,18 @@ const SearchFilterInputItem = ({ key, attribute }: { key: string; attribute: str onChange={event => setFilterValue('secondValue', event.target.value)} > - ) : ( + ) : displayedTextFieldsAmount === 1 ? ( setFilterValue('firstValue', event.target.value)}> + ) : ( + <> )} setFilterOperatorOption(event.target.value)} - defaultValue={'default'} - renderValue={value => t(`search.${value}`)} - > - {filterOperatorOptions.map(option => ( - - {t(`search.${option}`)} - - ))} - - {displayedTextFieldsAmount === 2 ? ( - <> - setFilterValue('firstValue', event.target.value)} - > - setFilterValue('secondValue', event.target.value)} - > - - ) : displayedTextFieldsAmount === 1 ? ( - setFilterValue('firstValue', event.target.value)}> - ) : ( - <> - )} - - -
- ); -}; - -const AdvancedSearch = ({ setFilters }: { setFilters: (filters: string) => void }) => { +export const AdvancedSearch = () => { const ATTRIBUTES = [ 'keyword', 'description', @@ -321,19 +12,6 @@ const AdvancedSearch = ({ setFilters }: { setFilters: (filters: string) => void 'archive', 'timeRange', ]; - - const IS_TEXT = 'is_text'; - - // const [keywordFilter, SetKeywordFilter] = useState(''); - // const [descriptionFilter, SetDescriptionFilter] = useState(''); - // const [commentFilter, SetCommentFilter] = useState(''); - // const [personFilter, SetPersonFilter] = useState(''); - // const [faceTagFilter, SetFaceTagFilter] = useState(''); - // const [locationFilter, SetLocationFilter] = useState(''); - // const [collectionFilter, SetCollecionFilter] = useState(''); - // const [archiveFilter, SetArchiveFilter] = useState(''); - // const [timeRangeFilter, SetTimeFilter] = useState(''); - return (
diff --git a/projects/bp-gallery/src/components/views/search/SearchFilterInput.tsx b/projects/bp-gallery/src/components/views/search/SearchFilterInput.tsx new file mode 100644 index 000000000..21a29acf8 --- /dev/null +++ b/projects/bp-gallery/src/components/views/search/SearchFilterInput.tsx @@ -0,0 +1,88 @@ +import { Dispatch, SetStateAction, useCallback, useEffect, useState } from 'react'; +import { useAdvancedSearch } from '../../../hooks/context-hooks'; +import { SearchFilterInputItem } from './SearchFilterInputItem'; + +export const SearchFilterInput = ({ key, attribute }: { key: string; attribute: string }) => { + const [rowIds, SetRowIds] = useState([0]); + const [filters, SetFilters] = useState(['']); + + const setFilter = (id: string, filter: string) => { + SetFilters(current => current.splice(parseInt(id), 0, filter)); + }; + + const deleteRow = useCallback( + (id: string) => { + if (rowIds.length > 1) { + SetRowIds(current => current.splice(parseInt(id), 1)); + SetFilters(current => current.splice(parseInt(id), 1)); + } + }, + [rowIds] + ); + + const incrementRows = useCallback(() => { + SetRowIds(current => current.splice(rowIds.length, 0, rowIds[rowIds.length - 1] + 1)); + }, [rowIds]); + + const filter = filters.join(' '); + + const context = useAdvancedSearch(); + + const updateFilter = useCallback(() => { + switch (attribute) { + case 'keyword': + return context?.SetKeywordFilter; + case 'description': + return context?.SetDescriptionFilter; + case 'comment': + return context?.SetCommentFilter; + case 'person': + return context?.SetPersonFilter; + case 'face-tag': + return context?.SetFaceTagFilter; + case 'location': + return context?.SetLocationFilter; + case 'collection': + return context?.SetCollectionFilter; + case 'archive': + return context?.SetArchiveFilter; + case 'timeRange': + return context?.SetTimeRangeFilter; + default: + return () => { + console.log("attribute doens't exist", attribute); + }; + } + }, [ + attribute, + context?.SetArchiveFilter, + context?.SetCollectionFilter, + context?.SetCommentFilter, + context?.SetDescriptionFilter, + context?.SetFaceTagFilter, + context?.SetKeywordFilter, + context?.SetLocationFilter, + context?.SetPersonFilter, + context?.SetTimeRangeFilter, + ]); + useEffect(() => { + if (updateFilter()) { + (updateFilter() as Dispatch>)(filter); + } + }, [filter, updateFilter]); + + return ( + <> + {rowIds.map(id => ( + + ))} + + ); +}; diff --git a/projects/bp-gallery/src/components/views/search/SearchFilterInputItem.tsx b/projects/bp-gallery/src/components/views/search/SearchFilterInputItem.tsx new file mode 100644 index 000000000..214afc758 --- /dev/null +++ b/projects/bp-gallery/src/components/views/search/SearchFilterInputItem.tsx @@ -0,0 +1,244 @@ +import { MenuItem, Select, TextField } from '@mui/material'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; + +type FilterProps = { + attribute: string; + operatorOption: string; + firstValue: string; + secondValue: string; + combinatorOption: string; +}; + +export const SearchFilterInputItem = ({ + key, + id, + attribute, + deleteRow, + incrementRows, + setFilter, +}: { + key: string; + id: string; + attribute: string; + deleteRow: (id: string) => void; + incrementRows: () => void; + setFilter: (id: string, filter: string) => void; +}) => { + const { t } = useTranslation(); + const DEFAULT_OPTION = 'default'; + const Text_FILTER_OPERATOR_OPTIONS = [ + DEFAULT_OPTION, + 'equal', + 'unequal', + 'is-empty', + 'is-not-empty', + ]; + const TIME_FILTER_OPERATOR_OPTIONS = [ + DEFAULT_OPTION, + 'span', + 'lower', + 'lower-equal', + 'greater', + 'greater-equal', + 'is-empty', + 'is-not-empty', + ]; + const Filter_COMBINATOR_OPTIONS = [DEFAULT_OPTION, 'and', 'or']; + const filterOperatorOptions = + attribute === 'timeRange' ? TIME_FILTER_OPERATOR_OPTIONS : Text_FILTER_OPERATOR_OPTIONS; + + const [displayedTextFieldsAmount, SetDisplayedTextFieldsAmount] = useState(1); + const [filterProps, SetFilterProps] = useState({ + attribute: attribute, + operatorOption: '', + firstValue: '', + secondValue: '', + combinatorOption: '', + }); + + const setFilterOperatorOption = useCallback((option: string) => { + SetFilterProps(filterProps => ({ ...filterProps, operatorOption: option })); + SetDisplayedTextFieldsAmount( + option === 'span' ? 2 : option === 'is-empty' || option === 'is-not-empty' ? 0 : 1 + ); + }, []); + + const setFilterValue = useCallback( + (key: T, value: string) => { + SetFilterProps(filterProps => ({ ...filterProps, [key]: value })); + }, + [SetFilterProps] + ); + + const setFilterCombinatorOption = useCallback( + (option: string) => { + if (option === DEFAULT_OPTION) { + SetFilterProps(filterProps => ({ ...filterProps, combinatorOption: '' })); + deleteRow(id); + } else { + SetFilterProps(filterProps => ({ ...filterProps, combinatorOption: option })); + incrementRows(); + } + }, + [deleteRow, incrementRows, id] + ); + + const optionTranslator = useCallback((option: string) => { + switch (option) { + case 'equal': + return '='; + case 'unequal': + return '!='; + case 'lower': + return '<'; + case 'lower-equal': + return '<='; + case 'greater': + return '>'; + case 'greater-equal': + return '>='; + case 'and': + return 'and'; + case 'or': + return 'or'; + default: + return ''; + } + }, []); + + const attributeTranslator = useCallback((attribute: string) => { + switch (attribute) { + case 'keyword': + return 'keyword_tags'; + case 'description': + return 'descriptions'; + case 'comment': + return 'comments'; + case 'person': + return 'person_tags'; + case 'face-tag': + return 'face_tags'; + case 'location': + return 'location_tags'; + case 'collection': + return 'collections'; + case 'archive': + return 'archive_tag'; + default: + return ''; + } + }, []); + + const startTimeParser = (startYear: string) => { + return Date.parse(`${startYear}-01-01T00:00:00Z`) / 1000; + }; + const endTimeparser = (endYear: string) => { + return Date.parse(`${endYear}-12-31T23:59:59Z`) / 1000; + }; + + const buildFilter = useCallback( + ({ + attribute, + operatorOption, + firstValue, + secondValue, + combinatorOption, + }: { + attribute: string; + operatorOption: string; + firstValue: string; + secondValue: string; + combinatorOption: string; + }) => { + const TIME_START = 'time_range_tag_start'; + const TIME_END = 'time_range_tag_end'; + + if (operatorOption === 'is-empty') { + return attribute !== 'timeRange' + ? `(${attributeTranslator(attribute)} IS EMPTY OR ${attributeTranslator( + attribute + )} IS NULL) ${optionTranslator(operatorOption)}` + : `((${TIME_START} IS EMPTY OR ${TIME_START} IS NULL) AND (${TIME_END} IS EMPTY OR ${TIME_END} IS NULL)) ${optionTranslator( + combinatorOption + )}`; + } else if (operatorOption === 'is-not-empty') { + return attribute !== 'timeRange' + ? `(${attributeTranslator(attribute)} IS NOT EMPTY AND ${attributeTranslator( + attribute + )} IS NOT NULL) ${optionTranslator(operatorOption)}` + : `(${TIME_START} IS NOT EMPTY AND ${TIME_START} IS NOT NULL AND ${TIME_END} IS NOT EMPTY AND ${TIME_END} IS NOT NULL) ${optionTranslator( + combinatorOption + )}`; + } + + if (attribute === 'timeRange') { + return operatorOption === 'span' + ? `(${TIME_START} >= ${startTimeParser(firstValue)} AND ${TIME_END} <= ${endTimeparser( + secondValue + )}) ${optionTranslator(combinatorOption)}` + : `(${TIME_START} ${optionTranslator(operatorOption)} ${startTimeParser( + firstValue + )} AND ${TIME_END} ${optionTranslator(operatorOption)} ${startTimeParser( + firstValue + )}) ${optionTranslator(combinatorOption)}`; + } else { + return `${attributeTranslator(attribute)} ${optionTranslator( + operatorOption + )} ${firstValue} ${optionTranslator(combinatorOption)}`; + } + }, + [optionTranslator, attributeTranslator] + ); + + const filter = useMemo(() => { + return buildFilter(filterProps); + }, [buildFilter, filterProps]); + + useEffect(() => { + setFilter(id, filter); + }, [id, filter, setFilter]); + + return ( +
+ {t(`search.${attribute}`)} + + {displayedTextFieldsAmount === 2 ? ( + <> + setFilterValue('firstValue', event.target.value)} + > + setFilterValue('secondValue', event.target.value)} + > + + ) : displayedTextFieldsAmount === 1 ? ( + setFilterValue('firstValue', event.target.value)}> + ) : ( + <> + )} + + +
+ ); +}; diff --git a/projects/bp-gallery/src/components/views/search/SearchView.tsx b/projects/bp-gallery/src/components/views/search/SearchView.tsx index 210c64fe5..aa2b0d9b0 100644 --- a/projects/bp-gallery/src/components/views/search/SearchView.tsx +++ b/projects/bp-gallery/src/components/views/search/SearchView.tsx @@ -67,19 +67,40 @@ const SearchView = () => { }, [searchParams]); const context = useAdvancedSearch(); + console.log(context?.keywordFilter); const filter = useMemo(() => { - return [ - context?.keywordFilter, - context?.descriptionFilter, - context?.commentFilter, - context?.personFilter, - context?.faceTagFilter, - context?.locationFilter, - context?.collectionFilter, - context?.archiveFilter, - ].join(' AND '); - }, [context]); + console.log(context?.keywordFilter); + const filters = [ + context?.keywordFilter ? context.keywordFilter : '', + context?.descriptionFilter ? context.descriptionFilter : '', + context?.commentFilter ? context.commentFilter : '', + context?.personFilter ? context.personFilter : '', + context?.faceTagFilter ? context.faceTagFilter : '', + context?.locationFilter ? context.locationFilter : '', + context?.collectionFilter ? context.collectionFilter : '', + context?.archiveFilter ? context.archiveFilter : '', + ]; + + console.log(filters); + return ( + filters + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + .filter(entry => { + entry === '' ? false : true; + }) + .join('AND') + ); + }, [ + context?.keywordFilter, + context?.descriptionFilter, + context?.commentFilter, + context?.personFilter, + context?.faceTagFilter, + context?.locationFilter, + context?.collectionFilter, + context?.archiveFilter, + ]); const [searchResultIds, error, state] = usePromise( async () => @@ -100,19 +121,14 @@ const SearchView = () => { const isOldSearchActive = useFlag('old_search'); if (import.meta.env.MODE === 'development') { - // getSearchResultPictureIds(queryParams, '').then(res => console.log('search results:', res)); console.log('resultids', searchResultIds); - console.log( - '31149 key:', - searchResultIds?.findIndex(element => element === '31149') - ); } const { linkToCollection, bulkEdit } = useBulkOperations(); return (
- {}}> +
{(!areResultsEmpty || !search) && ( diff --git a/projects/bp-gallery/src/shared/locales/de.json b/projects/bp-gallery/src/shared/locales/de.json index 89087e0f5..ad248364a 100755 --- a/projects/bp-gallery/src/shared/locales/de.json +++ b/projects/bp-gallery/src/shared/locales/de.json @@ -144,7 +144,7 @@ "lower-equal": "kleiner gleich als", "greater": "größer als", "greater-equal": "größer gleich als", - "to": "von ... bis ...", + "span": "von ... bis ...", "and": "und", "or": "oder", "all": "Alle Bildinformationen", From 6757b95807637d9d1235e57db5b5aa2653619c65 Mon Sep 17 00:00:00 2001 From: baerlach-git <83013270+baerlach-git@users.noreply.github.com> Date: Thu, 29 Jun 2023 11:33:26 +0200 Subject: [PATCH 10/34] still trash, lol --- .../views/search/AdvancedSearch.tsx | 35 +-- .../views/search/SearchFilterInput.tsx | 133 ++++++------ .../views/search/SearchFilterInputItem.tsx | 202 +++--------------- .../components/views/search/SearchView.tsx | 145 +++++++------ .../views/search/helpers/buildFilter.ts | 114 ++++++++++ 5 files changed, 296 insertions(+), 333 deletions(-) create mode 100644 projects/bp-gallery/src/components/views/search/helpers/buildFilter.ts diff --git a/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx b/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx index 87dc2df23..c39a7395c 100644 --- a/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx +++ b/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx @@ -1,24 +1,31 @@ +import { Button } from '@mui/material'; import { SearchFilterInput } from './SearchFilterInput'; -export const AdvancedSearch = () => { - const ATTRIBUTES = [ - 'keyword', - 'description', - 'comment', - 'person', - 'face-tag', - 'location', - 'collection', - 'archive', - 'timeRange', - ]; +export type SingleFilterProps = { + filterOperator: string; + combinationOperator: string; + values: string[]; +}; + +export type AttributeFilterProps = { attribute: string; filterProps: SingleFilterProps[] }; + +export const AdvancedSearch = ({ + advancedSearchProps, +}: { + advancedSearchProps: AttributeFilterProps[]; +}) => { return (
- {ATTRIBUTES.map(attr => ( - + {advancedSearchProps.map(props => ( + ))}
+
); }; diff --git a/projects/bp-gallery/src/components/views/search/SearchFilterInput.tsx b/projects/bp-gallery/src/components/views/search/SearchFilterInput.tsx index 21a29acf8..da309edcd 100644 --- a/projects/bp-gallery/src/components/views/search/SearchFilterInput.tsx +++ b/projects/bp-gallery/src/components/views/search/SearchFilterInput.tsx @@ -1,86 +1,73 @@ -import { Dispatch, SetStateAction, useCallback, useEffect, useState } from 'react'; -import { useAdvancedSearch } from '../../../hooks/context-hooks'; +import { AttributeFilterProps, SingleFilterProps } from './AdvancedSearch'; import { SearchFilterInputItem } from './SearchFilterInputItem'; -export const SearchFilterInput = ({ key, attribute }: { key: string; attribute: string }) => { - const [rowIds, SetRowIds] = useState([0]); - const [filters, SetFilters] = useState(['']); +export const SearchFilterInput = ({ + key, + attribute, + advancedSearchProps, +}: { + key: string; + attribute: string; + advancedSearchProps: AttributeFilterProps[]; +}) => { + let filterProps = advancedSearchProps.filter(attrProps => attrProps.attribute === attribute)[0] + .filterProps; - const setFilter = (id: string, filter: string) => { - SetFilters(current => current.splice(parseInt(id), 0, filter)); - }; - - const deleteRow = useCallback( - (id: string) => { - if (rowIds.length > 1) { - SetRowIds(current => current.splice(parseInt(id), 1)); - SetFilters(current => current.splice(parseInt(id), 1)); + const updateFilterProps = (index: number, action: string, property: string, value: string) => { + let update; + if (action === 'add') { + update = filterProps.map((item: SingleFilterProps) => item); + console.log('update pre splice', update); + update.splice(index + 1, 0, { + filterOperator: '', + combinationOperator: '', + values: [], + }); + console.log('filterProps', filterProps); + console.log('update post splice', update); + filterProps = update; + console.log('filterProps', filterProps); + } else if (action === 'delete') { + update = filterProps.map((item: SingleFilterProps) => item).splice(index, 1); + filterProps = update; + } else if (action === 'set') { + switch (property) { + case 'filterOperator': + update = filterProps.map((item: SingleFilterProps) => item); + update[index].filterOperator = value; + filterProps = update; + break; + case 'combinationOperator': + update = filterProps.map((item: SingleFilterProps) => item); + update[index].combinationOperator = value; + filterProps = update; + break; + case 'firstValue': + update = filterProps.map((item: SingleFilterProps) => item); + update[index].values[0] = value; + filterProps = update; + break; + case 'secondValue': + update = filterProps.map((item: SingleFilterProps) => item); + update[index].values[1] = value; + filterProps = update; + break; + default: + return; } - }, - [rowIds] - ); - - const incrementRows = useCallback(() => { - SetRowIds(current => current.splice(rowIds.length, 0, rowIds[rowIds.length - 1] + 1)); - }, [rowIds]); - - const filter = filters.join(' '); - - const context = useAdvancedSearch(); - - const updateFilter = useCallback(() => { - switch (attribute) { - case 'keyword': - return context?.SetKeywordFilter; - case 'description': - return context?.SetDescriptionFilter; - case 'comment': - return context?.SetCommentFilter; - case 'person': - return context?.SetPersonFilter; - case 'face-tag': - return context?.SetFaceTagFilter; - case 'location': - return context?.SetLocationFilter; - case 'collection': - return context?.SetCollectionFilter; - case 'archive': - return context?.SetArchiveFilter; - case 'timeRange': - return context?.SetTimeRangeFilter; - default: - return () => { - console.log("attribute doens't exist", attribute); - }; - } - }, [ - attribute, - context?.SetArchiveFilter, - context?.SetCollectionFilter, - context?.SetCommentFilter, - context?.SetDescriptionFilter, - context?.SetFaceTagFilter, - context?.SetKeywordFilter, - context?.SetLocationFilter, - context?.SetPersonFilter, - context?.SetTimeRangeFilter, - ]); - useEffect(() => { - if (updateFilter()) { - (updateFilter() as Dispatch>)(filter); } - }, [filter, updateFilter]); + + return; + }; return ( <> - {rowIds.map(id => ( + {filterProps.map((props, index) => ( ))} diff --git a/projects/bp-gallery/src/components/views/search/SearchFilterInputItem.tsx b/projects/bp-gallery/src/components/views/search/SearchFilterInputItem.tsx index 214afc758..2230330c4 100644 --- a/projects/bp-gallery/src/components/views/search/SearchFilterInputItem.tsx +++ b/projects/bp-gallery/src/components/views/search/SearchFilterInputItem.tsx @@ -1,29 +1,18 @@ +import { AddCircleOutlineOutlined, HighlightOff } from '@mui/icons-material'; import { MenuItem, Select, TextField } from '@mui/material'; -import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useState } from 'react'; import { useTranslation } from 'react-i18next'; -type FilterProps = { - attribute: string; - operatorOption: string; - firstValue: string; - secondValue: string; - combinatorOption: string; -}; - export const SearchFilterInputItem = ({ key, - id, + index, attribute, - deleteRow, - incrementRows, - setFilter, + updateFilterProps, }: { key: string; - id: string; + index: number; attribute: string; - deleteRow: (id: string) => void; - incrementRows: () => void; - setFilter: (id: string, filter: string) => void; + updateFilterProps: (index: number, action: string, property: string, value: string) => void; }) => { const { t } = useTranslation(); const DEFAULT_OPTION = 'default'; @@ -44,166 +33,19 @@ export const SearchFilterInputItem = ({ 'is-empty', 'is-not-empty', ]; - const Filter_COMBINATOR_OPTIONS = [DEFAULT_OPTION, 'and', 'or']; + const Filter_COMBINATOR_OPTIONS = ['and', 'or']; const filterOperatorOptions = attribute === 'timeRange' ? TIME_FILTER_OPERATOR_OPTIONS : Text_FILTER_OPERATOR_OPTIONS; const [displayedTextFieldsAmount, SetDisplayedTextFieldsAmount] = useState(1); - const [filterProps, SetFilterProps] = useState({ - attribute: attribute, - operatorOption: '', - firstValue: '', - secondValue: '', - combinatorOption: '', - }); - - const setFilterOperatorOption = useCallback((option: string) => { - SetFilterProps(filterProps => ({ ...filterProps, operatorOption: option })); - SetDisplayedTextFieldsAmount( - option === 'span' ? 2 : option === 'is-empty' || option === 'is-not-empty' ? 0 : 1 - ); - }, []); - - const setFilterValue = useCallback( - (key: T, value: string) => { - SetFilterProps(filterProps => ({ ...filterProps, [key]: value })); - }, - [SetFilterProps] - ); - - const setFilterCombinatorOption = useCallback( - (option: string) => { - if (option === DEFAULT_OPTION) { - SetFilterProps(filterProps => ({ ...filterProps, combinatorOption: '' })); - deleteRow(id); - } else { - SetFilterProps(filterProps => ({ ...filterProps, combinatorOption: option })); - incrementRows(); - } - }, - [deleteRow, incrementRows, id] - ); - - const optionTranslator = useCallback((option: string) => { - switch (option) { - case 'equal': - return '='; - case 'unequal': - return '!='; - case 'lower': - return '<'; - case 'lower-equal': - return '<='; - case 'greater': - return '>'; - case 'greater-equal': - return '>='; - case 'and': - return 'and'; - case 'or': - return 'or'; - default: - return ''; - } - }, []); - - const attributeTranslator = useCallback((attribute: string) => { - switch (attribute) { - case 'keyword': - return 'keyword_tags'; - case 'description': - return 'descriptions'; - case 'comment': - return 'comments'; - case 'person': - return 'person_tags'; - case 'face-tag': - return 'face_tags'; - case 'location': - return 'location_tags'; - case 'collection': - return 'collections'; - case 'archive': - return 'archive_tag'; - default: - return ''; - } - }, []); - - const startTimeParser = (startYear: string) => { - return Date.parse(`${startYear}-01-01T00:00:00Z`) / 1000; - }; - const endTimeparser = (endYear: string) => { - return Date.parse(`${endYear}-12-31T23:59:59Z`) / 1000; - }; - - const buildFilter = useCallback( - ({ - attribute, - operatorOption, - firstValue, - secondValue, - combinatorOption, - }: { - attribute: string; - operatorOption: string; - firstValue: string; - secondValue: string; - combinatorOption: string; - }) => { - const TIME_START = 'time_range_tag_start'; - const TIME_END = 'time_range_tag_end'; - - if (operatorOption === 'is-empty') { - return attribute !== 'timeRange' - ? `(${attributeTranslator(attribute)} IS EMPTY OR ${attributeTranslator( - attribute - )} IS NULL) ${optionTranslator(operatorOption)}` - : `((${TIME_START} IS EMPTY OR ${TIME_START} IS NULL) AND (${TIME_END} IS EMPTY OR ${TIME_END} IS NULL)) ${optionTranslator( - combinatorOption - )}`; - } else if (operatorOption === 'is-not-empty') { - return attribute !== 'timeRange' - ? `(${attributeTranslator(attribute)} IS NOT EMPTY AND ${attributeTranslator( - attribute - )} IS NOT NULL) ${optionTranslator(operatorOption)}` - : `(${TIME_START} IS NOT EMPTY AND ${TIME_START} IS NOT NULL AND ${TIME_END} IS NOT EMPTY AND ${TIME_END} IS NOT NULL) ${optionTranslator( - combinatorOption - )}`; - } - - if (attribute === 'timeRange') { - return operatorOption === 'span' - ? `(${TIME_START} >= ${startTimeParser(firstValue)} AND ${TIME_END} <= ${endTimeparser( - secondValue - )}) ${optionTranslator(combinatorOption)}` - : `(${TIME_START} ${optionTranslator(operatorOption)} ${startTimeParser( - firstValue - )} AND ${TIME_END} ${optionTranslator(operatorOption)} ${startTimeParser( - firstValue - )}) ${optionTranslator(combinatorOption)}`; - } else { - return `${attributeTranslator(attribute)} ${optionTranslator( - operatorOption - )} ${firstValue} ${optionTranslator(combinatorOption)}`; - } - }, - [optionTranslator, attributeTranslator] - ); - - const filter = useMemo(() => { - return buildFilter(filterProps); - }, [buildFilter, filterProps]); - - useEffect(() => { - setFilter(id, filter); - }, [id, filter, setFilter]); return (
{t(`search.${attribute}`)} setFilterCombinatorOption(event.target.value)} - defaultValue={'default'} + onChange={event => { + updateFilterProps(index, 'set', 'combinationOperator', event.target.value); + }} + defaultValue={'and'} renderValue={value => t(`search.${value}`)} > {Filter_COMBINATOR_OPTIONS.map(option => ( @@ -239,6 +91,10 @@ export const SearchFilterInputItem = ({ ))} + updateFilterProps(index, 'delete', '', '')}> + updateFilterProps(index, 'add', '', '')} + >
); }; diff --git a/projects/bp-gallery/src/components/views/search/SearchView.tsx b/projects/bp-gallery/src/components/views/search/SearchView.tsx index aa2b0d9b0..6b5f5339c 100644 --- a/projects/bp-gallery/src/components/views/search/SearchView.tsx +++ b/projects/bp-gallery/src/components/views/search/SearchView.tsx @@ -6,12 +6,10 @@ import usePromise from 'react-use-promise'; import { PictureFiltersInput } from '../../../graphql/APIConnector'; import { useFlag } from '../../../helpers/growthbook'; import useBulkOperations from '../../../hooks/bulk-operations.hook'; -import { useAdvancedSearch } from '../../../hooks/context-hooks'; import { HelpTooltip } from '../../common/HelpTooltip'; import PictureScrollGrid from '../../common/picture-gallery/PictureScrollGrid'; -import { AdvancedSearchProvider } from '../../provider/AdvancedSearchProvider'; import { ShowStats } from '../../provider/ShowStatsProvider'; -import AdvancedSearch from './AdvancedSearch'; +import AdvancedSearch, { AttributeFilterProps } from './AdvancedSearch'; import NoSearchResultsText from './NoSearchResultsText'; import SearchBar from './SearchBar'; import SearchBreadcrumbs from './SearchBreadcrumbs'; @@ -66,41 +64,33 @@ const SearchView = () => { }; }, [searchParams]); - const context = useAdvancedSearch(); - console.log(context?.keywordFilter); + // const context = useAdvancedSearch(); - const filter = useMemo(() => { - console.log(context?.keywordFilter); - const filters = [ - context?.keywordFilter ? context.keywordFilter : '', - context?.descriptionFilter ? context.descriptionFilter : '', - context?.commentFilter ? context.commentFilter : '', - context?.personFilter ? context.personFilter : '', - context?.faceTagFilter ? context.faceTagFilter : '', - context?.locationFilter ? context.locationFilter : '', - context?.collectionFilter ? context.collectionFilter : '', - context?.archiveFilter ? context.archiveFilter : '', - ]; + // const filter = useMemo(() => { + // const filters = [ + // context?.keywordFilter, + // context?.descriptionFilter, + // context?.commentFilter, + // context?.personFilter, + // context?.faceTagFilter, + // context?.locationFilter, + // context?.collectionFilter, + // context?.archiveFilter, + // ]; - console.log(filters); - return ( - filters - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - .filter(entry => { - entry === '' ? false : true; - }) - .join('AND') - ); - }, [ - context?.keywordFilter, - context?.descriptionFilter, - context?.commentFilter, - context?.personFilter, - context?.faceTagFilter, - context?.locationFilter, - context?.collectionFilter, - context?.archiveFilter, - ]); + // return filters.filter(entry => entry).join('AND'); + // }, [ + // context?.keywordFilter, + // context?.descriptionFilter, + // context?.commentFilter, + // context?.personFilter, + // context?.faceTagFilter, + // context?.locationFilter, + // context?.collectionFilter, + // context?.archiveFilter, + // ]); + + const filter = ''; const [searchResultIds, error, state] = usePromise( async () => @@ -117,50 +107,59 @@ const SearchView = () => { } return state === 'resolved' ? { id: { in: searchResultIds } } : {}; }, [searchResultIds, state, error]); - console.log(pictureFilter); const isOldSearchActive = useFlag('old_search'); - if (import.meta.env.MODE === 'development') { - console.log('resultids', searchResultIds); - } const { linkToCollection, bulkEdit } = useBulkOperations(); + const ATTRIBUTES = [ + 'keyword', + 'description', + 'comment', + 'person', + 'face-tag', + 'location', + 'collection', + 'archive', + 'timeRange', + ]; + const advancedSearchProps: AttributeFilterProps[] = ATTRIBUTES.map(attr => ({ + attribute: attr, + filterProps: [{ filterOperator: '', combinationOperator: '', values: [] }], + })); return (
- - -
- {(!areResultsEmpty || !search) && ( - - )} - -
- -
-
- {areResultsEmpty && search && } - {!search ? ( - - ) : ( - - { - setAreResultsEmpty(pictures <= 0); - }} - /> - + +
+ {(!areResultsEmpty || !search) && ( + )} - + +
+ +
+
+ {areResultsEmpty && search && } + {!search ? ( + + ) : ( + + { + setAreResultsEmpty(pictures <= 0); + }} + /> + + )}
); }; diff --git a/projects/bp-gallery/src/components/views/search/helpers/buildFilter.ts b/projects/bp-gallery/src/components/views/search/helpers/buildFilter.ts new file mode 100644 index 000000000..2efd1ea89 --- /dev/null +++ b/projects/bp-gallery/src/components/views/search/helpers/buildFilter.ts @@ -0,0 +1,114 @@ +const optionTranslator = useCallback((option: string) => { + switch (option) { + case 'equal': + return '='; + case 'unequal': + return '!='; + case 'lower': + return '<'; + case 'lower-equal': + return '<='; + case 'greater': + return '>'; + case 'greater-equal': + return '>='; + case 'and': + return 'and'; + case 'or': + return 'or'; + default: + return ''; + } +}, []); + +const attributeTranslator = useCallback((attribute: string) => { + switch (attribute) { + case 'keyword': + return 'keyword_tags'; + case 'description': + return 'descriptions'; + case 'comment': + return 'comments'; + case 'person': + return 'person_tags'; + case 'face-tag': + return 'face_tags'; + case 'location': + return 'location_tags'; + case 'collection': + return 'collections'; + case 'archive': + return 'archive_tag'; + default: + return ''; + } +}, []); + +const startTimeParser = (startYear: string) => { + return Date.parse(`${startYear}-01-01T00:00:00Z`) / 1000; +}; +const endTimeparser = (endYear: string) => { + return Date.parse(`${endYear}-12-31T23:59:59Z`) / 1000; +}; + +const buildFilter = useCallback( + ({ + attribute, + operatorOption, + firstValue, + secondValue, + combinatorOption, + }: { + attribute: string; + operatorOption: string; + firstValue: string; + secondValue: string; + combinatorOption: string; + }) => { + const TIME_START = 'time_range_tag_start'; + const TIME_END = 'time_range_tag_end'; + + if (operatorOption === 'is-empty') { + return attribute !== 'timeRange' + ? `(${attributeTranslator(attribute)} IS EMPTY OR ${attributeTranslator( + attribute + )} IS NULL) ${optionTranslator(operatorOption)}` + : `((${TIME_START} IS EMPTY OR ${TIME_START} IS NULL) AND (${TIME_END} IS EMPTY OR ${TIME_END} IS NULL)) ${optionTranslator( + combinatorOption + )}`; + } else if (operatorOption === 'is-not-empty') { + return attribute !== 'timeRange' + ? `(${attributeTranslator(attribute)} IS NOT EMPTY AND ${attributeTranslator( + attribute + )} IS NOT NULL) ${optionTranslator(operatorOption)}` + : `(${TIME_START} IS NOT EMPTY AND ${TIME_START} IS NOT NULL AND ${TIME_END} IS NOT EMPTY AND ${TIME_END} IS NOT NULL) ${optionTranslator( + combinatorOption + )}`; + } + + if (attribute === 'timeRange') { + return operatorOption === 'span' + ? `(${TIME_START} >= ${startTimeParser(firstValue)} AND ${TIME_END} <= ${endTimeparser( + secondValue + )}) ${optionTranslator(combinatorOption)}` + : `(${TIME_START} ${optionTranslator(operatorOption)} ${startTimeParser( + firstValue + )} AND ${TIME_END} ${optionTranslator(operatorOption)} ${startTimeParser( + firstValue + )}) ${optionTranslator(combinatorOption)}`; + } else { + return `${attributeTranslator(attribute)} ${optionTranslator( + operatorOption + )} ${firstValue} ${optionTranslator(combinatorOption)}`; + } + }, + [optionTranslator, attributeTranslator] +); + +const filter = useMemo(() => { + return buildFilter(filterProps); +}, [buildFilter, filterProps]); + +useEffect(() => { + setFilter(id, filter); +}, [id, filter, setFilter]); From 744fb91a6dfec1f2a281ae9f5b845520336a0e39 Mon Sep 17 00:00:00 2001 From: baerlach-git <83013270+baerlach-git@users.noreply.github.com> Date: Thu, 29 Jun 2023 16:44:51 +0200 Subject: [PATCH 11/34] things are starting to work --- .../views/search/AdvancedSearch.tsx | 147 +++++++++++++++++- .../views/search/SearchFilterInput.tsx | 29 ++-- .../views/search/SearchFilterInputItem.tsx | 62 ++++++-- .../components/views/search/SearchView.tsx | 48 +----- 4 files changed, 206 insertions(+), 80 deletions(-) diff --git a/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx b/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx index c39a7395c..5da788987 100644 --- a/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx +++ b/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx @@ -1,4 +1,4 @@ -import { Button } from '@mui/material'; +import { Dispatch, SetStateAction, useCallback, useState } from 'react'; import { SearchFilterInput } from './SearchFilterInput'; export type SingleFilterProps = { @@ -9,11 +9,144 @@ export type SingleFilterProps = { export type AttributeFilterProps = { attribute: string; filterProps: SingleFilterProps[] }; -export const AdvancedSearch = ({ - advancedSearchProps, -}: { - advancedSearchProps: AttributeFilterProps[]; -}) => { +export const AdvancedSearch = ({ setFilter }: { setFilter: Dispatch> }) => { + const ATTRIBUTES = [ + 'keyword', + 'description', + 'comment', + 'person', + 'face-tag', + 'location', + 'collection', + 'archive', + 'timeRange', + ]; + + const [advancedSearchProps, SetAdvancedSearchProps] = useState( + ATTRIBUTES.map(attr => ({ + attribute: attr, + filterProps: [{ filterOperator: '', combinationOperator: '', values: [] }], + })) + ); + + const optionTranslator = useCallback((option: string) => { + switch (option) { + case 'equal': + return '='; + case 'unequal': + return '!='; + case 'lower': + return '<'; + case 'lower-equal': + return '<='; + case 'greater': + return '>'; + case 'greater-equal': + return '>='; + case 'and': + return 'and'; + case 'or': + return 'or'; + default: + return ''; + } + }, []); + + const attributeTranslator = useCallback((attribute: string) => { + switch (attribute) { + case 'keyword': + return 'keyword_tags'; + case 'description': + return 'descriptions'; + case 'comment': + return 'comments'; + case 'person': + return 'person_tags'; + case 'face-tag': + return 'face_tags'; + case 'location': + return 'location_tags'; + case 'collection': + return 'collections'; + case 'archive': + return 'archive_tag'; + default: + return ''; + } + }, []); + + const startTimeParser = (startYear: string) => { + return Date.parse(`${startYear}-01-01T00:00:00Z`) / 1000; + }; + const endTimeparser = (endYear: string) => { + return Date.parse(`${endYear}-12-31T23:59:59Z`) / 1000; + }; + + const buildSingleFilter = useCallback( + ( + attribute: string, + operatorOption: string, + firstValue: string, + secondValue: string, + combinatorOption: string + ) => { + const TIME_START = 'time_range_tag_start'; + const TIME_END = 'time_range_tag_end'; + + if (operatorOption === 'is-empty') { + return attribute !== 'timeRange' + ? `(${attributeTranslator(attribute)} IS EMPTY OR ${attributeTranslator( + attribute + )} IS NULL) ${optionTranslator(operatorOption)}` + : `((${TIME_START} IS EMPTY OR ${TIME_START} IS NULL) AND (${TIME_END} IS EMPTY OR ${TIME_END} IS NULL)) ${optionTranslator( + combinatorOption + )}`; + } else if (operatorOption === 'is-not-empty') { + return attribute !== 'timeRange' + ? `(${attributeTranslator(attribute)} IS NOT EMPTY AND ${attributeTranslator( + attribute + )} IS NOT NULL) ${optionTranslator(operatorOption)}` + : `(${TIME_START} IS NOT EMPTY AND ${TIME_START} IS NOT NULL AND ${TIME_END} IS NOT EMPTY AND ${TIME_END} IS NOT NULL) ${optionTranslator( + combinatorOption + )}`; + } + + if (attribute === 'timeRange') { + return operatorOption === 'span' + ? `(${TIME_START} >= ${startTimeParser(firstValue)} AND ${TIME_END} <= ${endTimeparser( + secondValue + )}) ${optionTranslator(combinatorOption)}` + : `(${TIME_START} ${optionTranslator(operatorOption)} ${startTimeParser( + firstValue + )} AND ${TIME_END} ${optionTranslator(operatorOption)} ${startTimeParser( + firstValue + )}) ${optionTranslator(combinatorOption)}`; + } else { + return `${attributeTranslator(attribute)} ${optionTranslator( + operatorOption + )} ${firstValue} ${optionTranslator(combinatorOption)}`; + } + }, + [optionTranslator, attributeTranslator] + ); + + // const buildAttributeFilter = useCallback( + // ({ attributeFilterProps }: { attributeFilterProps: AttributeFilterProps }) => { + // return attributeFilterProps.filterProps.reduce(( singleFilterProps => + // buildSingleFilter( + // attributeFilterProps.attribute, + // singleFilterProps.filterOperator, + // singleFilterProps.values[0], + // singleFilterProps.values[1], + // singleFilterProps.combinationOperator + // ) + // ).; + // }, + // [] + // ); + + const filter = advancedSearchProps; + return (
@@ -22,10 +155,10 @@ export const AdvancedSearch = ({ key={props.attribute} attribute={props.attribute} advancedSearchProps={advancedSearchProps} + setAdvancedSearchProps={SetAdvancedSearchProps} > ))}
-
); }; diff --git a/projects/bp-gallery/src/components/views/search/SearchFilterInput.tsx b/projects/bp-gallery/src/components/views/search/SearchFilterInput.tsx index da309edcd..fa0dd3f5e 100644 --- a/projects/bp-gallery/src/components/views/search/SearchFilterInput.tsx +++ b/projects/bp-gallery/src/components/views/search/SearchFilterInput.tsx @@ -1,3 +1,4 @@ +import { Dispatch, SetStateAction } from 'react'; import { AttributeFilterProps, SingleFilterProps } from './AdvancedSearch'; import { SearchFilterInputItem } from './SearchFilterInputItem'; @@ -5,57 +6,58 @@ export const SearchFilterInput = ({ key, attribute, advancedSearchProps, + setAdvancedSearchProps, }: { key: string; attribute: string; advancedSearchProps: AttributeFilterProps[]; + setAdvancedSearchProps: Dispatch>; }) => { - let filterProps = advancedSearchProps.filter(attrProps => attrProps.attribute === attribute)[0] + const filterProps = advancedSearchProps.filter(attrProps => attrProps.attribute === attribute)[0] .filterProps; const updateFilterProps = (index: number, action: string, property: string, value: string) => { - let update; + let update: SingleFilterProps[]; if (action === 'add') { update = filterProps.map((item: SingleFilterProps) => item); - console.log('update pre splice', update); update.splice(index + 1, 0, { filterOperator: '', combinationOperator: '', values: [], }); - console.log('filterProps', filterProps); - console.log('update post splice', update); - filterProps = update; - console.log('filterProps', filterProps); } else if (action === 'delete') { - update = filterProps.map((item: SingleFilterProps) => item).splice(index, 1); - filterProps = update; + if (filterProps.length === 1) { + return; + } + update = filterProps.slice(); + update.splice(index, 1); } else if (action === 'set') { switch (property) { case 'filterOperator': update = filterProps.map((item: SingleFilterProps) => item); update[index].filterOperator = value; - filterProps = update; break; case 'combinationOperator': update = filterProps.map((item: SingleFilterProps) => item); update[index].combinationOperator = value; - filterProps = update; break; case 'firstValue': update = filterProps.map((item: SingleFilterProps) => item); update[index].values[0] = value; - filterProps = update; break; case 'secondValue': update = filterProps.map((item: SingleFilterProps) => item); update[index].values[1] = value; - filterProps = update; + break; default: return; } } + const advancedSearchPropsUpdate: AttributeFilterProps[] = advancedSearchProps.map(entry => + entry.attribute === attribute ? { attribute: attribute, filterProps: update } : entry + ); + setAdvancedSearchProps(advancedSearchPropsUpdate); return; }; @@ -67,6 +69,7 @@ export const SearchFilterInput = ({ key={index.toString()} index={index} attribute={attribute} + advancedSearchProps={advancedSearchProps} updateFilterProps={updateFilterProps} > ))} diff --git a/projects/bp-gallery/src/components/views/search/SearchFilterInputItem.tsx b/projects/bp-gallery/src/components/views/search/SearchFilterInputItem.tsx index 2230330c4..b852835df 100644 --- a/projects/bp-gallery/src/components/views/search/SearchFilterInputItem.tsx +++ b/projects/bp-gallery/src/components/views/search/SearchFilterInputItem.tsx @@ -1,17 +1,20 @@ import { AddCircleOutlineOutlined, HighlightOff } from '@mui/icons-material'; import { MenuItem, Select, TextField } from '@mui/material'; -import { useState } from 'react'; +import { useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { AttributeFilterProps } from './AdvancedSearch'; export const SearchFilterInputItem = ({ key, index, attribute, updateFilterProps, + advancedSearchProps, }: { key: string; index: number; attribute: string; + advancedSearchProps: AttributeFilterProps[]; updateFilterProps: (index: number, action: string, property: string, value: string) => void; }) => { const { t } = useTranslation(); @@ -38,12 +41,30 @@ export const SearchFilterInputItem = ({ attribute === 'timeRange' ? TIME_FILTER_OPERATOR_OPTIONS : Text_FILTER_OPERATOR_OPTIONS; const [displayedTextFieldsAmount, SetDisplayedTextFieldsAmount] = useState(1); + const switchTextFieldsAmount = useCallback((option: string) => { + switch (option) { + case 'is-empty': + case 'is-not-empty': + SetDisplayedTextFieldsAmount(0); + break; + case 'span': + SetDisplayedTextFieldsAmount(2); + break; + default: + SetDisplayedTextFieldsAmount(1); + } + }, []); + + const AttributeFilterProps = advancedSearchProps.filter( + entry => entry.attribute === attribute + )[0]; return (
{t(`search.${attribute}`)} { - updateFilterProps(index, 'set', 'combinationOperator', event.target.value); - }} - defaultValue={'and'} - renderValue={value => t(`search.${value}`)} - > - {Filter_COMBINATOR_OPTIONS.map(option => ( - - {t(`search.${option}`)} - - ))} - - updateFilterProps(index, 'delete', '', '')}> + {AttributeFilterProps.filterProps[index + 1] ? ( + + ) : ( + <> + )} + {index !== 0 ? ( + updateFilterProps(index, 'delete', '', '')}> + ) : ( + <> + )} updateFilterProps(index, 'add', '', '')} > diff --git a/projects/bp-gallery/src/components/views/search/SearchView.tsx b/projects/bp-gallery/src/components/views/search/SearchView.tsx index 6b5f5339c..f912710ec 100644 --- a/projects/bp-gallery/src/components/views/search/SearchView.tsx +++ b/projects/bp-gallery/src/components/views/search/SearchView.tsx @@ -9,7 +9,7 @@ import useBulkOperations from '../../../hooks/bulk-operations.hook'; import { HelpTooltip } from '../../common/HelpTooltip'; import PictureScrollGrid from '../../common/picture-gallery/PictureScrollGrid'; import { ShowStats } from '../../provider/ShowStatsProvider'; -import AdvancedSearch, { AttributeFilterProps } from './AdvancedSearch'; +import AdvancedSearch from './AdvancedSearch'; import NoSearchResultsText from './NoSearchResultsText'; import SearchBar from './SearchBar'; import SearchBreadcrumbs from './SearchBreadcrumbs'; @@ -34,6 +34,7 @@ const isValidTimeSpecification = (searchRequest: string) => { const SearchView = () => { const [areResultsEmpty, setAreResultsEmpty] = useState(false); + const [filter, SetFilter] = useState(''); const { search }: Location = useLocation(); const { t } = useTranslation(); @@ -64,34 +65,6 @@ const SearchView = () => { }; }, [searchParams]); - // const context = useAdvancedSearch(); - - // const filter = useMemo(() => { - // const filters = [ - // context?.keywordFilter, - // context?.descriptionFilter, - // context?.commentFilter, - // context?.personFilter, - // context?.faceTagFilter, - // context?.locationFilter, - // context?.collectionFilter, - // context?.archiveFilter, - // ]; - - // return filters.filter(entry => entry).join('AND'); - // }, [ - // context?.keywordFilter, - // context?.descriptionFilter, - // context?.commentFilter, - // context?.personFilter, - // context?.faceTagFilter, - // context?.locationFilter, - // context?.collectionFilter, - // context?.archiveFilter, - // ]); - - const filter = ''; - const [searchResultIds, error, state] = usePromise( async () => (await getSearchResultPictureIds(queryParams, filter)).map(hit => @@ -110,25 +83,9 @@ const SearchView = () => { const isOldSearchActive = useFlag('old_search'); const { linkToCollection, bulkEdit } = useBulkOperations(); - const ATTRIBUTES = [ - 'keyword', - 'description', - 'comment', - 'person', - 'face-tag', - 'location', - 'collection', - 'archive', - 'timeRange', - ]; - const advancedSearchProps: AttributeFilterProps[] = ATTRIBUTES.map(attr => ({ - attribute: attr, - filterProps: [{ filterOperator: '', combinationOperator: '', values: [] }], - })); return (
-
{(!areResultsEmpty || !search) && ( @@ -136,6 +93,7 @@ const SearchView = () => {
+
{areResultsEmpty && search && } From 5fa9e01b58beadfd14e2c14309f3d14d5165de35 Mon Sep 17 00:00:00 2001 From: baerlach-git <83013270+baerlach-git@users.noreply.github.com> Date: Sat, 1 Jul 2023 14:28:16 +0200 Subject: [PATCH 12/34] filters should now be build properly --- .../provider/AdvancedSearchContext.tsx | 24 ---- .../provider/AdvancedSearchProvider.tsx | 37 ------ .../views/search/AdvancedSearch.tsx | 83 ++++++++----- .../views/search/SearchFilterInput.tsx | 1 - .../views/search/SearchFilterInputItem.tsx | 5 +- .../views/search/helpers/buildFilter.ts | 114 ------------------ .../bp-gallery/src/hooks/context-hooks.ts | 5 - 7 files changed, 57 insertions(+), 212 deletions(-) delete mode 100644 projects/bp-gallery/src/components/provider/AdvancedSearchContext.tsx delete mode 100644 projects/bp-gallery/src/components/provider/AdvancedSearchProvider.tsx delete mode 100644 projects/bp-gallery/src/components/views/search/helpers/buildFilter.ts diff --git a/projects/bp-gallery/src/components/provider/AdvancedSearchContext.tsx b/projects/bp-gallery/src/components/provider/AdvancedSearchContext.tsx deleted file mode 100644 index 70ab1de97..000000000 --- a/projects/bp-gallery/src/components/provider/AdvancedSearchContext.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { Dispatch, SetStateAction, createContext } from 'react'; - -export type AdvancedSearchContext = { - keywordFilter: string; - SetKeywordFilter: Dispatch>; - descriptionFilter: string; - SetDescriptionFilter: Dispatch>; - commentFilter: string; - SetCommentFilter: Dispatch>; - personFilter: string; - SetPersonFilter: Dispatch>; - faceTagFilter: string; - SetFaceTagFilter: Dispatch>; - locationFilter: string; - SetLocationFilter: Dispatch>; - collectionFilter: string; - SetCollectionFilter: Dispatch>; - archiveFilter: string; - SetArchiveFilter: Dispatch>; - timeRangeFilter: string; - SetTimeRangeFilter: Dispatch>; -}; - -export const AdvancedSearchContext = createContext(null); diff --git a/projects/bp-gallery/src/components/provider/AdvancedSearchProvider.tsx b/projects/bp-gallery/src/components/provider/AdvancedSearchProvider.tsx deleted file mode 100644 index 7581fd1c4..000000000 --- a/projects/bp-gallery/src/components/provider/AdvancedSearchProvider.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { PropsWithChildren, useState } from 'react'; -import { AdvancedSearchContext } from './AdvancedSearchContext'; - -export const AdvancedSearchProvider = ({ children }: PropsWithChildren<{}>) => { - const [keywordFilter, SetKeywordFilter] = useState(''); - const [descriptionFilter, SetDescriptionFilter] = useState(''); - const [commentFilter, SetCommentFilter] = useState(''); - const [personFilter, SetPersonFilter] = useState(''); - const [faceTagFilter, SetFaceTagFilter] = useState(''); - const [locationFilter, SetLocationFilter] = useState(''); - const [collectionFilter, SetCollectionFilter] = useState(''); - const [archiveFilter, SetArchiveFilter] = useState(''); - const [timeRangeFilter, SetTimeRangeFilter] = useState(''); - - const value = { - keywordFilter, - SetKeywordFilter, - descriptionFilter, - SetDescriptionFilter, - commentFilter, - SetCommentFilter, - personFilter, - SetPersonFilter, - faceTagFilter, - SetFaceTagFilter, - locationFilter, - SetLocationFilter, - collectionFilter, - SetCollectionFilter, - archiveFilter, - SetArchiveFilter, - timeRangeFilter, - SetTimeRangeFilter, - }; - - return {children}; -}; diff --git a/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx b/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx index 5da788987..a3a3f38b1 100644 --- a/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx +++ b/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx @@ -1,3 +1,4 @@ +import { Button } from '@mui/material'; import { Dispatch, SetStateAction, useCallback, useState } from 'react'; import { SearchFilterInput } from './SearchFilterInput'; @@ -25,7 +26,7 @@ export const AdvancedSearch = ({ setFilter }: { setFilter: Dispatch( ATTRIBUTES.map(attr => ({ attribute: attr, - filterProps: [{ filterOperator: '', combinationOperator: '', values: [] }], + filterProps: [{ filterOperator: '', combinationOperator: '', values: ['', ''] }], })) ); @@ -44,9 +45,9 @@ export const AdvancedSearch = ({ setFilter }: { setFilter: Dispatch='; case 'and': - return 'and'; + return 'AND'; case 'or': - return 'or'; + return 'OR'; default: return ''; } @@ -92,60 +93,74 @@ export const AdvancedSearch = ({ setFilter }: { setFilter: Dispatch { const TIME_START = 'time_range_tag_start'; const TIME_END = 'time_range_tag_end'; - - if (operatorOption === 'is-empty') { + if (operatorOption === 'default' || operatorOption === '') { + return ''; + } else if (operatorOption === 'is-empty') { return attribute !== 'timeRange' ? `(${attributeTranslator(attribute)} IS EMPTY OR ${attributeTranslator( attribute - )} IS NULL) ${optionTranslator(operatorOption)}` + )} IS NULL) ${optionTranslator(combinatorOption)} ` : `((${TIME_START} IS EMPTY OR ${TIME_START} IS NULL) AND (${TIME_END} IS EMPTY OR ${TIME_END} IS NULL)) ${optionTranslator( combinatorOption - )}`; + )} `; } else if (operatorOption === 'is-not-empty') { return attribute !== 'timeRange' ? `(${attributeTranslator(attribute)} IS NOT EMPTY AND ${attributeTranslator( attribute - )} IS NOT NULL) ${optionTranslator(operatorOption)}` + )} IS NOT NULL) ${optionTranslator(combinatorOption)} ` : `(${TIME_START} IS NOT EMPTY AND ${TIME_START} IS NOT NULL AND ${TIME_END} IS NOT EMPTY AND ${TIME_END} IS NOT NULL) ${optionTranslator( combinatorOption - )}`; - } - - if (attribute === 'timeRange') { + )} `; + } else if (attribute === 'timeRange') { return operatorOption === 'span' ? `(${TIME_START} >= ${startTimeParser(firstValue)} AND ${TIME_END} <= ${endTimeparser( secondValue - )}) ${optionTranslator(combinatorOption)}` + )}) ${optionTranslator(combinatorOption)} ` : `(${TIME_START} ${optionTranslator(operatorOption)} ${startTimeParser( firstValue )} AND ${TIME_END} ${optionTranslator(operatorOption)} ${startTimeParser( firstValue - )}) ${optionTranslator(combinatorOption)}`; + )}) ${optionTranslator(combinatorOption)} `; } else { return `${attributeTranslator(attribute)} ${optionTranslator( operatorOption - )} ${firstValue} ${optionTranslator(combinatorOption)}`; + )} ${firstValue} ${optionTranslator(combinatorOption)} `; } }, [optionTranslator, attributeTranslator] ); - // const buildAttributeFilter = useCallback( - // ({ attributeFilterProps }: { attributeFilterProps: AttributeFilterProps }) => { - // return attributeFilterProps.filterProps.reduce(( singleFilterProps => - // buildSingleFilter( - // attributeFilterProps.attribute, - // singleFilterProps.filterOperator, - // singleFilterProps.values[0], - // singleFilterProps.values[1], - // singleFilterProps.combinationOperator - // ) - // ).; - // }, - // [] - // ); + const buildAttributeFilter = useCallback( + (attributeFilterProps: AttributeFilterProps) => { + const filter = `${attributeFilterProps.filterProps.reduce( + (accumulator, currentValue) => + `${accumulator}${buildSingleFilter( + attributeFilterProps.attribute, + currentValue.filterOperator, + currentValue.values[0], + currentValue.values[1], + currentValue.combinationOperator + )}`, + '' + )}`; + return filter !== '' ? `(${filter})` : ''; + }, + [buildSingleFilter] + ); - const filter = advancedSearchProps; + const filter: string = advancedSearchProps + .map(attributeFilterProps => buildAttributeFilter(attributeFilterProps)) + .reduce((accumulator, currentvalue) => { + if (accumulator !== '' && currentvalue === '') { + return accumulator; + } else if (accumulator === '' && currentvalue !== '') { + return currentvalue; + } else if (accumulator !== '' && currentvalue !== '') { + return `${accumulator} AND ${currentvalue}`; + } else { + return ''; + } + }, ''); return (
@@ -159,6 +174,14 @@ export const AdvancedSearch = ({ setFilter }: { setFilter: Dispatch ))}
+
); }; diff --git a/projects/bp-gallery/src/components/views/search/SearchFilterInput.tsx b/projects/bp-gallery/src/components/views/search/SearchFilterInput.tsx index fa0dd3f5e..cc2e47de0 100644 --- a/projects/bp-gallery/src/components/views/search/SearchFilterInput.tsx +++ b/projects/bp-gallery/src/components/views/search/SearchFilterInput.tsx @@ -48,7 +48,6 @@ export const SearchFilterInput = ({ case 'secondValue': update = filterProps.map((item: SingleFilterProps) => item); update[index].values[1] = value; - break; default: return; diff --git a/projects/bp-gallery/src/components/views/search/SearchFilterInputItem.tsx b/projects/bp-gallery/src/components/views/search/SearchFilterInputItem.tsx index b852835df..6eaf23bad 100644 --- a/projects/bp-gallery/src/components/views/search/SearchFilterInputItem.tsx +++ b/projects/bp-gallery/src/components/views/search/SearchFilterInputItem.tsx @@ -125,7 +125,10 @@ export const SearchFilterInputItem = ({ <> )} updateFilterProps(index, 'add', '', '')} + onClick={() => { + updateFilterProps(index, 'set', 'combinationOperator', 'and'); + updateFilterProps(index, 'add', '', ''); + }} >
); diff --git a/projects/bp-gallery/src/components/views/search/helpers/buildFilter.ts b/projects/bp-gallery/src/components/views/search/helpers/buildFilter.ts deleted file mode 100644 index 2efd1ea89..000000000 --- a/projects/bp-gallery/src/components/views/search/helpers/buildFilter.ts +++ /dev/null @@ -1,114 +0,0 @@ -const optionTranslator = useCallback((option: string) => { - switch (option) { - case 'equal': - return '='; - case 'unequal': - return '!='; - case 'lower': - return '<'; - case 'lower-equal': - return '<='; - case 'greater': - return '>'; - case 'greater-equal': - return '>='; - case 'and': - return 'and'; - case 'or': - return 'or'; - default: - return ''; - } -}, []); - -const attributeTranslator = useCallback((attribute: string) => { - switch (attribute) { - case 'keyword': - return 'keyword_tags'; - case 'description': - return 'descriptions'; - case 'comment': - return 'comments'; - case 'person': - return 'person_tags'; - case 'face-tag': - return 'face_tags'; - case 'location': - return 'location_tags'; - case 'collection': - return 'collections'; - case 'archive': - return 'archive_tag'; - default: - return ''; - } -}, []); - -const startTimeParser = (startYear: string) => { - return Date.parse(`${startYear}-01-01T00:00:00Z`) / 1000; -}; -const endTimeparser = (endYear: string) => { - return Date.parse(`${endYear}-12-31T23:59:59Z`) / 1000; -}; - -const buildFilter = useCallback( - ({ - attribute, - operatorOption, - firstValue, - secondValue, - combinatorOption, - }: { - attribute: string; - operatorOption: string; - firstValue: string; - secondValue: string; - combinatorOption: string; - }) => { - const TIME_START = 'time_range_tag_start'; - const TIME_END = 'time_range_tag_end'; - - if (operatorOption === 'is-empty') { - return attribute !== 'timeRange' - ? `(${attributeTranslator(attribute)} IS EMPTY OR ${attributeTranslator( - attribute - )} IS NULL) ${optionTranslator(operatorOption)}` - : `((${TIME_START} IS EMPTY OR ${TIME_START} IS NULL) AND (${TIME_END} IS EMPTY OR ${TIME_END} IS NULL)) ${optionTranslator( - combinatorOption - )}`; - } else if (operatorOption === 'is-not-empty') { - return attribute !== 'timeRange' - ? `(${attributeTranslator(attribute)} IS NOT EMPTY AND ${attributeTranslator( - attribute - )} IS NOT NULL) ${optionTranslator(operatorOption)}` - : `(${TIME_START} IS NOT EMPTY AND ${TIME_START} IS NOT NULL AND ${TIME_END} IS NOT EMPTY AND ${TIME_END} IS NOT NULL) ${optionTranslator( - combinatorOption - )}`; - } - - if (attribute === 'timeRange') { - return operatorOption === 'span' - ? `(${TIME_START} >= ${startTimeParser(firstValue)} AND ${TIME_END} <= ${endTimeparser( - secondValue - )}) ${optionTranslator(combinatorOption)}` - : `(${TIME_START} ${optionTranslator(operatorOption)} ${startTimeParser( - firstValue - )} AND ${TIME_END} ${optionTranslator(operatorOption)} ${startTimeParser( - firstValue - )}) ${optionTranslator(combinatorOption)}`; - } else { - return `${attributeTranslator(attribute)} ${optionTranslator( - operatorOption - )} ${firstValue} ${optionTranslator(combinatorOption)}`; - } - }, - [optionTranslator, attributeTranslator] -); - -const filter = useMemo(() => { - return buildFilter(filterProps); -}, [buildFilter, filterProps]); - -useEffect(() => { - setFilter(id, filter); -}, [id, filter, setFilter]); diff --git a/projects/bp-gallery/src/hooks/context-hooks.ts b/projects/bp-gallery/src/hooks/context-hooks.ts index 8d9fac50f..c017d6798 100644 --- a/projects/bp-gallery/src/hooks/context-hooks.ts +++ b/projects/bp-gallery/src/hooks/context-hooks.ts @@ -1,5 +1,4 @@ import { Context, useContext } from 'react'; -import { AdvancedSearchContext } from '../components/provider/AdvancedSearchContext'; import { ClipboardEditorContext } from '../components/provider/ClipboardEditorProvider'; import { FaceTaggingContext } from '../components/provider/FaceTaggingContext'; import { MobileContext } from '../components/provider/MobileProvider'; @@ -35,7 +34,3 @@ export const useFaceTagging = () => { export const useMobile = () => { return useContext(MobileContext); }; - -export const useAdvancedSearch = () => { - return useContext(AdvancedSearchContext); -}; From dfe8f10603c97910cb78a9aa27695c05043b94cd Mon Sep 17 00:00:00 2001 From: baerlach-git <83013270+baerlach-git@users.noreply.github.com> Date: Sun, 2 Jul 2023 21:28:08 +0200 Subject: [PATCH 13/34] improved the UI --- .../views/search/AdvancedSearch.tsx | 78 ++++++++--- .../src/components/views/search/SearchBar.tsx | 2 +- .../views/search/SearchFilterInputItem.tsx | 122 +++++++++--------- .../components/views/search/SearchView.tsx | 40 +++--- .../search/helpers/addNewParamToSearchPath.ts | 4 +- .../bp-gallery/src/shared/locales/de.json | 1 + 6 files changed, 142 insertions(+), 105 deletions(-) diff --git a/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx b/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx index a3a3f38b1..b58e6cf5a 100644 --- a/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx +++ b/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx @@ -1,5 +1,8 @@ -import { Button } from '@mui/material'; +import { ExpandMore } from '@mui/icons-material'; +import { Accordion, AccordionSummary, Button, Typography } from '@mui/material'; +import { useTheme } from '@mui/material/styles'; import { Dispatch, SetStateAction, useCallback, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { SearchFilterInput } from './SearchFilterInput'; export type SingleFilterProps = { @@ -11,6 +14,13 @@ export type SingleFilterProps = { export type AttributeFilterProps = { attribute: string; filterProps: SingleFilterProps[] }; export const AdvancedSearch = ({ setFilter }: { setFilter: Dispatch> }) => { + const { t } = useTranslation(); + const { + palette: { + primary: { main: $primaryColor }, + }, + } = useTheme(); + const ATTRIBUTES = [ 'keyword', 'description', @@ -163,26 +173,54 @@ export const AdvancedSearch = ({ setFilter }: { setFilter: Dispatch -
- {advancedSearchProps.map(props => ( - - ))} + + }> + {t('search.advanced-search-title')} + +
+
+
+ {advancedSearchProps + .filter(props => ATTRIBUTES.slice(0, 4).includes(props.attribute)) + .map(props => ( +
+ +
+ ))} +
+
+ {advancedSearchProps + .filter(props => ATTRIBUTES.slice(4, 8).includes(props.attribute)) + .map(props => ( +
+ +
+ ))} +
+
+
+ +
- -
+ ); }; diff --git a/projects/bp-gallery/src/components/views/search/SearchBar.tsx b/projects/bp-gallery/src/components/views/search/SearchBar.tsx index c09d594ce..b32fa5b08 100644 --- a/projects/bp-gallery/src/components/views/search/SearchBar.tsx +++ b/projects/bp-gallery/src/components/views/search/SearchBar.tsx @@ -58,7 +58,7 @@ const SearchBar = ({ !isAllSearchActive && Array.from(searchParams.entries()).length !== 0; const onSearchStart = (searchInput: string) => { - if (searchInput === '') return; + // if (searchInput === '') return; // Spaces are our delimiter for different search terms const newSearchRequest = searchInput.split(' ').map(encodeURIComponent).join(' '); diff --git a/projects/bp-gallery/src/components/views/search/SearchFilterInputItem.tsx b/projects/bp-gallery/src/components/views/search/SearchFilterInputItem.tsx index 6eaf23bad..bc546e57e 100644 --- a/projects/bp-gallery/src/components/views/search/SearchFilterInputItem.tsx +++ b/projects/bp-gallery/src/components/views/search/SearchFilterInputItem.tsx @@ -60,76 +60,78 @@ export const SearchFilterInputItem = ({ )[0]; return ( -
- {t(`search.${attribute}`)} - - {displayedTextFieldsAmount === 2 ? ( - <> - { - updateFilterProps(index, 'set', 'firstValue', event.target.value); - }} - > - { - updateFilterProps(index, 'set', 'secondValue', event.target.value); - }} - > - - ) : displayedTextFieldsAmount === 1 ? ( - { - updateFilterProps(index, 'set', 'firstValue', event.target.value); - }} - > - ) : ( - <> - )} - - {AttributeFilterProps.filterProps[index + 1] ? ( +
+ {index === 0 ? {t(`search.${attribute}`)} : <>} +
- ) : ( - <> - )} - {index !== 0 ? ( - updateFilterProps(index, 'delete', '', '')}> - ) : ( - <> - )} - { - updateFilterProps(index, 'set', 'combinationOperator', 'and'); - updateFilterProps(index, 'add', '', ''); - }} - > + {displayedTextFieldsAmount === 2 ? ( + <> + { + updateFilterProps(index, 'set', 'firstValue', event.target.value); + }} + > + { + updateFilterProps(index, 'set', 'secondValue', event.target.value); + }} + > + + ) : displayedTextFieldsAmount === 1 ? ( + { + updateFilterProps(index, 'set', 'firstValue', event.target.value); + }} + > + ) : ( + <> + )} + + {AttributeFilterProps.filterProps[index + 1] ? ( + + ) : ( + <> + )} + {index !== 0 ? ( + updateFilterProps(index, 'delete', '', '')}> + ) : ( + <> + )} + { + updateFilterProps(index, 'set', 'combinationOperator', 'and'); + updateFilterProps(index, 'add', '', ''); + }} + > +
); }; diff --git a/projects/bp-gallery/src/components/views/search/SearchView.tsx b/projects/bp-gallery/src/components/views/search/SearchView.tsx index f912710ec..7933a6aea 100644 --- a/projects/bp-gallery/src/components/views/search/SearchView.tsx +++ b/projects/bp-gallery/src/components/views/search/SearchView.tsx @@ -13,7 +13,6 @@ import AdvancedSearch from './AdvancedSearch'; import NoSearchResultsText from './NoSearchResultsText'; import SearchBar from './SearchBar'; import SearchBreadcrumbs from './SearchBreadcrumbs'; -import SearchHub from './SearchHub'; import './SearchView.scss'; import { isValidYear } from './helpers/addNewParamToSearchPath'; import getSearchResultPictureIds from './helpers/getSearchResultPictureIds'; @@ -97,27 +96,24 @@ const SearchView = () => {
{areResultsEmpty && search && } - {!search ? ( - - ) : ( - - { - setAreResultsEmpty(pictures <= 0); - }} - /> - - )} + + + { + setAreResultsEmpty(pictures <= 0); + }} + /> +
); }; diff --git a/projects/bp-gallery/src/components/views/search/helpers/addNewParamToSearchPath.ts b/projects/bp-gallery/src/components/views/search/helpers/addNewParamToSearchPath.ts index ada2fcb28..950ef362e 100644 --- a/projects/bp-gallery/src/components/views/search/helpers/addNewParamToSearchPath.ts +++ b/projects/bp-gallery/src/components/views/search/helpers/addNewParamToSearchPath.ts @@ -1,4 +1,4 @@ -import { isEmpty } from 'lodash'; +// import { isEmpty } from 'lodash'; import { SearchType } from './search-filters'; import { fromURLSearchParam, toURLSearchParam } from './url-search-params'; @@ -34,7 +34,7 @@ export const addNewParamToSearchPath = ( } paramValues.forEach(element => { - if (!isDuplicatedSearchParam(element, newParamType, searchParams) && !isEmpty(element)) { + if (!isDuplicatedSearchParam(element, newParamType, searchParams) /* && !isEmpty(element) */) { searchParams.append(toURLSearchParam(newParamType), element); } }); diff --git a/projects/bp-gallery/src/shared/locales/de.json b/projects/bp-gallery/src/shared/locales/de.json index ad248364a..2e2f46e8c 100755 --- a/projects/bp-gallery/src/shared/locales/de.json +++ b/projects/bp-gallery/src/shared/locales/de.json @@ -134,6 +134,7 @@ "location": "Orte", "collection": "Sammlungen", "archive": "Archive", + "advanced-search-title": "Erweiterte Suche", "timeRange": "Zeitspannen", "default": "Nichts ausgewählt", "equal": "enthalten", From c04c16d52e961a05050af4a937a65509699623ed Mon Sep 17 00:00:00 2001 From: baerlach-git <83013270+baerlach-git@users.noreply.github.com> Date: Mon, 3 Jul 2023 19:26:39 +0200 Subject: [PATCH 14/34] more styling --- .../views/search/AdvancedSearch.tsx | 125 +++++++++++------- .../components/views/search/SearchBar.scss | 13 +- .../views/search/SearchFilterInput.tsx | 4 +- .../views/search/SearchFilterInputItem.tsx | 22 ++- .../components/views/search/SearchView.scss | 10 +- .../components/views/search/SearchView.tsx | 18 +-- .../bp-gallery/src/shared/locales/de.json | 1 + 7 files changed, 119 insertions(+), 74 deletions(-) diff --git a/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx b/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx index b58e6cf5a..b1f46a59c 100644 --- a/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx +++ b/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx @@ -1,8 +1,12 @@ import { ExpandMore } from '@mui/icons-material'; -import { Accordion, AccordionSummary, Button, Typography } from '@mui/material'; +import { Accordion, AccordionSummary, Typography } from '@mui/material'; import { useTheme } from '@mui/material/styles'; import { Dispatch, SetStateAction, useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { HelpTooltip } from '../../common/HelpTooltip'; +import PrimaryButton from '../../common/PrimaryButton'; +import SearchBar from './SearchBar'; +import SearchBreadcrumbs from './SearchBreadcrumbs'; import { SearchFilterInput } from './SearchFilterInput'; export type SingleFilterProps = { @@ -13,7 +17,15 @@ export type SingleFilterProps = { export type AttributeFilterProps = { attribute: string; filterProps: SingleFilterProps[] }; -export const AdvancedSearch = ({ setFilter }: { setFilter: Dispatch> }) => { +export const AdvancedSearch = ({ + setFilter, + searchParams, + isAllSearchActive, +}: { + setFilter: Dispatch>; + searchParams: URLSearchParams; + isAllSearchActive: boolean; +}) => { const { t } = useTranslation(); const { palette: { @@ -173,54 +185,77 @@ export const AdvancedSearch = ({ setFilter }: { setFilter: Dispatch - }> - {t('search.advanced-search-title')} - -
-
-
- {advancedSearchProps - .filter(props => ATTRIBUTES.slice(0, 4).includes(props.attribute)) - .map(props => ( -
- +
+
+ +
+
+
+ + + }> + {t('search.advanced-search-title')} + +
+
+
+ {advancedSearchProps + .filter(props => ATTRIBUTES.slice(0, 4).includes(props.attribute)) + .map(props => ( +
+ +
+ ))}
- ))} -
-
- {advancedSearchProps - .filter(props => ATTRIBUTES.slice(4, 8).includes(props.attribute)) - .map(props => ( -
- +
+ {advancedSearchProps + .filter(props => ATTRIBUTES.slice(4, 8).includes(props.attribute)) + .map(props => ( +
+ +
+ ))}
- ))} -
+
+
+
+ { + setFilter(filter); + console.log(advancedSearchProps); + }} + > + {t('search.appy-filter')} + +
+
+
+
-
- +
+
- +
); }; diff --git a/projects/bp-gallery/src/components/views/search/SearchBar.scss b/projects/bp-gallery/src/components/views/search/SearchBar.scss index 8da5d2f79..e93a1450d 100644 --- a/projects/bp-gallery/src/components/views/search/SearchBar.scss +++ b/projects/bp-gallery/src/components/views/search/SearchBar.scss @@ -2,10 +2,10 @@ .search-bar-wrapper { display: flex; - z-index: 25; - margin: auto; + // z-index: 25; + margin: 0; width: 100%; - min-width: 0; + min-width: 100%; @media only screen and (min-width: $mobile-breakpoint) { max-width: 800px; @@ -43,8 +43,9 @@ } .search-bar { - background: #e5e5e5; - box-shadow: 0px 3px 3px 0px rgba(0, 0, 0, 0.3); + background: #e9e9e9; + box-shadow: 0px 1px 0px 0px rgba(0, 0, 0, 0.3); + border-bottom: 1px black; width: 100%; @media only screen and (max-width: $mobile-breakpoint) { @@ -98,7 +99,7 @@ .MuiInputBase-root { color: black; border-radius: 0.2rem; - background: #e5e5e5; + background: #e9e9e9; transition: margin-left 0.25s, margin-top 0.25s, width 0.25s, border-radius 0.25s; input { diff --git a/projects/bp-gallery/src/components/views/search/SearchFilterInput.tsx b/projects/bp-gallery/src/components/views/search/SearchFilterInput.tsx index cc2e47de0..205cf053e 100644 --- a/projects/bp-gallery/src/components/views/search/SearchFilterInput.tsx +++ b/projects/bp-gallery/src/components/views/search/SearchFilterInput.tsx @@ -62,7 +62,7 @@ export const SearchFilterInput = ({ }; return ( - <> +
{filterProps.map((props, index) => ( ))} - +
); }; diff --git a/projects/bp-gallery/src/components/views/search/SearchFilterInputItem.tsx b/projects/bp-gallery/src/components/views/search/SearchFilterInputItem.tsx index bc546e57e..cd096a412 100644 --- a/projects/bp-gallery/src/components/views/search/SearchFilterInputItem.tsx +++ b/projects/bp-gallery/src/components/views/search/SearchFilterInputItem.tsx @@ -1,5 +1,5 @@ import { AddCircleOutlineOutlined, HighlightOff } from '@mui/icons-material'; -import { MenuItem, Select, TextField } from '@mui/material'; +import { MenuItem, Select, TextField, Typography } from '@mui/material'; import { useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { AttributeFilterProps } from './AdvancedSearch'; @@ -60,10 +60,15 @@ export const SearchFilterInputItem = ({ )[0]; return ( -
- {index === 0 ? {t(`search.${attribute}`)} : <>} +
+ {index === 0 ? ( + {t(`search.${attribute}`)} + ) : ( + <> + )}
{ updateFilterProps(index, 'set', 'combinationOperator', event.target.value); }} @@ -121,11 +130,16 @@ export const SearchFilterInputItem = ({ <> )} {index !== 0 ? ( - updateFilterProps(index, 'delete', '', '')}> + updateFilterProps(index, 'delete', '', '')} + > ) : ( <> )} { updateFilterProps(index, 'set', 'combinationOperator', 'and'); updateFilterProps(index, 'add', '', ''); diff --git a/projects/bp-gallery/src/components/views/search/SearchView.scss b/projects/bp-gallery/src/components/views/search/SearchView.scss index a3ef43734..a0fa3debf 100644 --- a/projects/bp-gallery/src/components/views/search/SearchView.scss +++ b/projects/bp-gallery/src/components/views/search/SearchView.scss @@ -1,8 +1,10 @@ @import 'src/shared/style.module'; .search-bar-container { - display: grid; - grid-template-columns: 1fr 50px; + display: flex; + flex-flow: column nowrap; + margin: none; + width: fit-content; .search-bar-wrapper { grid-row: 1; @@ -11,8 +13,8 @@ .breadcrumb { background: rgba(255, 255, 255, 0.1); - grid-row: 2; - grid-column: 1; + // grid-row: 2; + // grid-column: 1; .search-breadcrumbs { display: flex; diff --git a/projects/bp-gallery/src/components/views/search/SearchView.tsx b/projects/bp-gallery/src/components/views/search/SearchView.tsx index 7933a6aea..5e5be659b 100644 --- a/projects/bp-gallery/src/components/views/search/SearchView.tsx +++ b/projects/bp-gallery/src/components/views/search/SearchView.tsx @@ -6,13 +6,10 @@ import usePromise from 'react-use-promise'; import { PictureFiltersInput } from '../../../graphql/APIConnector'; import { useFlag } from '../../../helpers/growthbook'; import useBulkOperations from '../../../hooks/bulk-operations.hook'; -import { HelpTooltip } from '../../common/HelpTooltip'; import PictureScrollGrid from '../../common/picture-gallery/PictureScrollGrid'; import { ShowStats } from '../../provider/ShowStatsProvider'; import AdvancedSearch from './AdvancedSearch'; import NoSearchResultsText from './NoSearchResultsText'; -import SearchBar from './SearchBar'; -import SearchBreadcrumbs from './SearchBreadcrumbs'; import './SearchView.scss'; import { isValidYear } from './helpers/addNewParamToSearchPath'; import getSearchResultPictureIds from './helpers/getSearchResultPictureIds'; @@ -85,16 +82,11 @@ const SearchView = () => { return (
-
- {(!areResultsEmpty || !search) && ( - - )} - -
- - -
-
+ {areResultsEmpty && search && } diff --git a/projects/bp-gallery/src/shared/locales/de.json b/projects/bp-gallery/src/shared/locales/de.json index 2e2f46e8c..2c587652a 100755 --- a/projects/bp-gallery/src/shared/locales/de.json +++ b/projects/bp-gallery/src/shared/locales/de.json @@ -135,6 +135,7 @@ "collection": "Sammlungen", "archive": "Archive", "advanced-search-title": "Erweiterte Suche", + "appy-filter": "Filter Anwenden", "timeRange": "Zeitspannen", "default": "Nichts ausgewählt", "equal": "enthalten", From 3c9aea0fa514ec4acc7f53c26612a5c64fc9f146 Mon Sep 17 00:00:00 2001 From: baerlach-git <83013270+baerlach-git@users.noreply.github.com> Date: Tue, 4 Jul 2023 12:58:06 +0200 Subject: [PATCH 15/34] search results now refresh upon applying a new filter --- .../src/components/views/search/AdvancedSearch.tsx | 10 +++------- .../src/components/views/search/SearchView.tsx | 6 +++--- .../views/search/helpers/addNewParamToSearchPath.ts | 4 ++-- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx b/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx index b1f46a59c..8f1b6b324 100644 --- a/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx +++ b/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx @@ -1,8 +1,8 @@ import { ExpandMore } from '@mui/icons-material'; import { Accordion, AccordionSummary, Typography } from '@mui/material'; -import { useTheme } from '@mui/material/styles'; import { Dispatch, SetStateAction, useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { useVisit } from '../../../helpers/history'; import { HelpTooltip } from '../../common/HelpTooltip'; import PrimaryButton from '../../common/PrimaryButton'; import SearchBar from './SearchBar'; @@ -27,11 +27,7 @@ export const AdvancedSearch = ({ isAllSearchActive: boolean; }) => { const { t } = useTranslation(); - const { - palette: { - primary: { main: $primaryColor }, - }, - } = useTheme(); + const { visit } = useVisit(); const ATTRIBUTES = [ 'keyword', @@ -185,7 +181,7 @@ export const AdvancedSearch = ({ }, ''); return ( -
+
diff --git a/projects/bp-gallery/src/components/views/search/SearchView.tsx b/projects/bp-gallery/src/components/views/search/SearchView.tsx index 5e5be659b..10687d0c3 100644 --- a/projects/bp-gallery/src/components/views/search/SearchView.tsx +++ b/projects/bp-gallery/src/components/views/search/SearchView.tsx @@ -30,7 +30,7 @@ const isValidTimeSpecification = (searchRequest: string) => { const SearchView = () => { const [areResultsEmpty, setAreResultsEmpty] = useState(false); - const [filter, SetFilter] = useState(''); + const [filter, setFilter] = useState(''); const { search }: Location = useLocation(); const { t } = useTranslation(); @@ -66,7 +66,7 @@ const SearchView = () => { (await getSearchResultPictureIds(queryParams, filter)).map(hit => (hit.id as number).toString() ), - [queryParams] + [queryParams, filter] ); const pictureFilter: PictureFiltersInput = useMemo(() => { @@ -83,7 +83,7 @@ const SearchView = () => { return (
diff --git a/projects/bp-gallery/src/components/views/search/helpers/addNewParamToSearchPath.ts b/projects/bp-gallery/src/components/views/search/helpers/addNewParamToSearchPath.ts index 950ef362e..ada2fcb28 100644 --- a/projects/bp-gallery/src/components/views/search/helpers/addNewParamToSearchPath.ts +++ b/projects/bp-gallery/src/components/views/search/helpers/addNewParamToSearchPath.ts @@ -1,4 +1,4 @@ -// import { isEmpty } from 'lodash'; +import { isEmpty } from 'lodash'; import { SearchType } from './search-filters'; import { fromURLSearchParam, toURLSearchParam } from './url-search-params'; @@ -34,7 +34,7 @@ export const addNewParamToSearchPath = ( } paramValues.forEach(element => { - if (!isDuplicatedSearchParam(element, newParamType, searchParams) /* && !isEmpty(element) */) { + if (!isDuplicatedSearchParam(element, newParamType, searchParams) && !isEmpty(element)) { searchParams.append(toURLSearchParam(newParamType), element); } }); From 1467a1c91d167d76bcff238f2be9bd0ad7bdd08f Mon Sep 17 00:00:00 2001 From: baerlach-git <83013270+baerlach-git@users.noreply.github.com> Date: Tue, 4 Jul 2023 21:10:29 +0200 Subject: [PATCH 16/34] fixed an error in the filter building methods --- projects/bp-gallery/package.json | 6 +- .../views/search/AdvancedSearch.tsx | 42 ++++----- .../components/views/search/SearchView.tsx | 12 ++- projects/bp-strapi/config/plugins.js | 91 ++++++++----------- 4 files changed, 73 insertions(+), 78 deletions(-) diff --git a/projects/bp-gallery/package.json b/projects/bp-gallery/package.json index c4e614748..99f307465 100755 --- a/projects/bp-gallery/package.json +++ b/projects/bp-gallery/package.json @@ -114,7 +114,9 @@ "lint": "eslint --cache --ext .ts,.tsx,.js,.jsx .", "format": "prettier --write src && yarn lint --fix", "replace-error-policy": "replace-in-file \"import { gql } from '@apollo/client\" \"import { ErrorPolicy, gql } from '@apollo/client\" ./src/graphql/APIConnector.tsx && replace-in-file \"errorPolicy: 'all'\" \"errorPolicy: 'all' as ErrorPolicy\" ./src/graphql/APIConnector.tsx && replace-in-file \"/* eslint-disable react-refresh/only-export-components */ import\" \"import\" ./src/graphql/APIConnector.tsx", - "generate-api": "node ../bp-graphql/build/generateIndexFiles.js && tsc -p ../bp-graphql && (yarn upgrade bp-graphql &) && (cd ../bp-strapi && yarn upgrade bp-graphql &) && node ../bp-graphql/build/generateOperationGraphQL.js && tsc -p src/graphql/plugins && graphql-codegen --config src/graphql/codegen.yml && replace-in-file \"import { gql } from '@apollo/client\" \"/* eslint-disable react-refresh/only-export-components */ import { ErrorPolicy, gql } from '@apollo/client\" ./src/graphql/APIConnector.tsx && yarn eslint --fix --no-ignore ./src/graphql/APIConnector.tsx && yarn replace-error-policy", - "generate-api:e2e": "env-cmd -f ./environments/.env.test yarn generate-api" + "generate-api": "node ../bp-graphql/build/generateIndexFiles.js && cd ../bp-graphql && yarn build && cd ../bp-gallery && yarn upgrade bp-graphql && cd ../bp-strapi && yarn upgrade bp-graphql && cd ../bp-gallery && node ../bp-graphql/build/generateOperationGraphQL.js && tsc -p src/graphql/plugins && graphql-codegen --config src/graphql/codegen.yml && replace-in-file \"import { gql } from '@apollo/client\" \"/* eslint-disable react-refresh/only-export-components */ import { ErrorPolicy, gql } from '@apollo/client\" ./src/graphql/APIConnector.tsx && yarn eslint --fix --no-ignore ./src/graphql/APIConnector.tsx && yarn replace-error-policy", + "generate-api:e2e": "env-cmd -f ./environments/.env.test yarn generate-api", + "simple-generate-api": "node ../bp-graphql/build/generateIndexFiles.js && cd ../bp-graphql && yarn build && cd ../bp-gallery && yarn upgrade bp-graphql && cd ../bp-strapi && yarn upgrade bp-graphql", + "generate-api:win": "node ../bp-graphql/build/generateIndexFiles.js && tsc -p ../bp-graphql && start /b yarn upgrade bp-graphql && start /d ..\\bp-strapi /b yarn upgrade bp-graphql && node ../bp-graphql/build/generateOperationGraphQL.js && tsc -p src/graphql/plugins && graphql-codegen --config src/graphql/codegen.yml && replace-in-file \"import { gql } from '@apollo/client\" \"/* eslint-disable react-refresh/only-export-components */ import { ErrorPolicy, gql } from '@apollo/client\" ./src/graphql/APIConnector.tsx && yarn eslint --fix --no-ignore ./src/graphql/APIConnector.tsx && yarn replace-error-policy" } } diff --git a/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx b/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx index a9c361274..a8bd81e06 100644 --- a/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx +++ b/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx @@ -2,7 +2,6 @@ import { ExpandMore } from '@mui/icons-material'; import { Accordion, AccordionSummary, Button, Typography } from '@mui/material'; import { Dispatch, SetStateAction, useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useVisit } from '../../../helpers/history'; import { HelpTooltip } from '../../common/HelpTooltip'; import SearchBar from './SearchBar'; import SearchBreadcrumbs from './SearchBreadcrumbs'; @@ -26,7 +25,6 @@ export const AdvancedSearch = ({ isAllSearchActive: boolean; }) => { const { t } = useTranslation(); - const { visit } = useVisit(); const ATTRIBUTES = [ 'keyword', @@ -50,21 +48,21 @@ export const AdvancedSearch = ({ const optionTranslator = useCallback((option: string) => { switch (option) { case 'equal': - return '='; + return ' = '; case 'unequal': - return '!='; + return ' != '; case 'lower': - return '<'; + return ' < '; case 'lower-equal': - return '<='; + return ' <= '; case 'greater': - return '>'; + return ' > '; case 'greater-equal': - return '>='; + return ' >= '; case 'and': - return 'AND'; + return ' AND '; case 'or': - return 'OR'; + return ' OR '; default: return ''; } @@ -116,32 +114,32 @@ export const AdvancedSearch = ({ return attribute !== 'timeRange' ? `(${attributeTranslator(attribute)} IS EMPTY OR ${attributeTranslator( attribute - )} IS NULL) ${optionTranslator(combinatorOption)} ` - : `((${TIME_START} IS EMPTY OR ${TIME_START} IS NULL) AND (${TIME_END} IS EMPTY OR ${TIME_END} IS NULL)) ${optionTranslator( + )} IS NULL)${optionTranslator(combinatorOption)}` + : `((${TIME_START} IS EMPTY OR ${TIME_START} IS NULL) AND (${TIME_END} IS EMPTY OR ${TIME_END} IS NULL))${optionTranslator( combinatorOption - )} `; + )}`; } else if (operatorOption === 'is-not-empty') { return attribute !== 'timeRange' ? `(${attributeTranslator(attribute)} IS NOT EMPTY AND ${attributeTranslator( attribute - )} IS NOT NULL) ${optionTranslator(combinatorOption)} ` - : `(${TIME_START} IS NOT EMPTY AND ${TIME_START} IS NOT NULL AND ${TIME_END} IS NOT EMPTY AND ${TIME_END} IS NOT NULL) ${optionTranslator( + )} IS NOT NULL)${optionTranslator(combinatorOption)}` + : `(${TIME_START} IS NOT EMPTY AND ${TIME_START} IS NOT NULL AND ${TIME_END} IS NOT EMPTY AND ${TIME_END} IS NOT NULL)${optionTranslator( combinatorOption - )} `; + )}`; } else if (attribute === 'timeRange') { return operatorOption === 'span' ? `(${TIME_START} >= ${startTimeParser(firstValue)} AND ${TIME_END} <= ${endTimeparser( secondValue - )}) ${optionTranslator(combinatorOption)} ` - : `(${TIME_START} ${optionTranslator(operatorOption)} ${startTimeParser( + )})${optionTranslator(combinatorOption)}` + : `(${TIME_START}${optionTranslator(operatorOption)}${startTimeParser( firstValue - )} AND ${TIME_END} ${optionTranslator(operatorOption)} ${startTimeParser( + )} AND ${TIME_END}${optionTranslator(operatorOption)}${startTimeParser( firstValue - )}) ${optionTranslator(combinatorOption)} `; + )})${optionTranslator(combinatorOption)}`; } else { - return `${attributeTranslator(attribute)} ${optionTranslator( + return `${attributeTranslator(attribute)}${optionTranslator( operatorOption - )} ${firstValue} ${optionTranslator(combinatorOption)} `; + )}${firstValue}${optionTranslator(combinatorOption)}`; } }, [optionTranslator, attributeTranslator] diff --git a/projects/bp-gallery/src/components/views/search/SearchView.tsx b/projects/bp-gallery/src/components/views/search/SearchView.tsx index 0d65149e7..fae0ad695 100644 --- a/projects/bp-gallery/src/components/views/search/SearchView.tsx +++ b/projects/bp-gallery/src/components/views/search/SearchView.tsx @@ -1,11 +1,13 @@ import { Location } from 'history'; -import { useContext, useMemo, useState } from 'react'; +import { sortBy } from 'lodash'; +import { useCallback, useContext, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useLocation } from 'react-router-dom'; import usePromise from 'react-use-promise'; import { PictureFiltersInput } from '../../../graphql/APIConnector'; import { useFlag } from '../../../helpers/growthbook'; import useBulkOperations from '../../../hooks/bulk-operations.hook'; +import { FlatPicture } from '../../../types/additionalFlatTypes'; import PictureScrollGrid from '../../common/picture-gallery/PictureScrollGrid'; import { ExhibitionIdContext } from '../../provider/ExhibitionProvider'; import { ShowStats } from '../../provider/ShowStatsProvider'; @@ -69,6 +71,7 @@ const SearchView = () => { ), [queryParams, filter] ); + console.log(searchResultIds); const pictureFilter: PictureFiltersInput = useMemo(() => { if (error) { @@ -77,6 +80,12 @@ const SearchView = () => { } return state === 'resolved' ? { id: { in: searchResultIds } } : {}; }, [searchResultIds, state, error]); + + const customSort = useCallback( + (pictures: FlatPicture[]) => sortBy(pictures, picture => searchResultIds?.indexOf(picture.id)), + [searchResultIds] + ); + const isOldSearchActive = useFlag('old_search'); const { linkToCollection, createSequence, bulkEdit, addToExhibition } = useBulkOperations(); @@ -102,6 +111,7 @@ const SearchView = () => { // by ANDing the isAllsearchActive flag with the isOldsearchActive flag we can make sure // the isAllSearchActive property will always be false if we want to use Meilisearch isAllSearchActive={isOldSearchActive && isAllSearchActive} + customSort={customSort} hashbase={search} bulkOperations={[ linkToCollection, diff --git a/projects/bp-strapi/config/plugins.js b/projects/bp-strapi/config/plugins.js index 019a431e4..c733502b5 100755 --- a/projects/bp-strapi/config/plugins.js +++ b/projects/bp-strapi/config/plugins.js @@ -4,7 +4,7 @@ const { /* eslint-disable no-unused-vars */ -const dateToTimeStamp = (date) => { +const dateToTimeStamp = date => { return Date.parse(date) / 1000; }; @@ -73,33 +73,29 @@ module.exports = ({ env }) => ({ }, }, meilisearch: - env("MEILISEARCH_ENABLED", "false") === "true" + env('MEILISEARCH_ENABLED', 'false') === 'true' ? { config: { - host: env("MEILISEARCH_HOST"), - apiKey: env("MEILISEARCH_API_KEY"), + host: env('MEILISEARCH_HOST'), + apiKey: env('MEILISEARCH_API_KEY'), picture: { transformEntry({ entry }) { const transformedEntry = { id: entry.id, likes: entry.likes, - descriptions: entry.descriptions.map( - (description) => description.text - ), - comments: entry.comments.map((comment) => comment.text), + descriptions: entry.descriptions.map(description => description.text), + comments: entry.comments.map(comment => comment.text), keyword_tags: entry.keyword_tags - .map((tag) => tag.name) - .concat(entry.verified_keyword_tags.map((tag) => tag.name)), + .map(tag => tag.name) + .concat(entry.verified_keyword_tags.map(tag => tag.name)), person_tags: entry.person_tags - .map((tag) => tag.name) - .concat(entry.verified_person_tags.map((tag) => tag.name)), + .map(tag => tag.name) + .concat(entry.verified_person_tags.map(tag => tag.name)), location_tags: entry.location_tags - .map((tag) => tag.name) - .concat( - entry.verified_location_tags.map((tag) => tag.name) - ), - face_tags: entry.face_tags.map((tag) => tag.name), - collections: entry.collections.map((tag) => tag.name), + .map(tag => tag.name) + .concat(entry.verified_location_tags.map(tag => tag.name)), + face_tags: entry.face_tags.map(tag => tag.name), + collections: entry.collections.map(tag => tag.name), archive_tag: entry.archive_tag, time_range_tag_start: entry?.time_range_tag ? dateToTimeStamp(entry.time_range_tag.start) @@ -117,47 +113,36 @@ module.exports = ({ env }) => ({ }, settings: { //for reference: https://www.meilisearch.com/docs/reference/api/settings - displayedAttributes: ["id"], + displayedAttributes: ['id'], // the order of the attributes in searchableAttributes determines the priorization // of search results i.e. a match in the first searchable attribute will always outrank a match in any other searchable attribute searchableAttributes: [ - "descriptions", - "keyword_tags", - "location_tags", - "time_range_tag_start", - "time_range_tag_end", - "face_tags", - "person_tags", - "collections", - "archive_tag", - "comments", + 'descriptions', + 'location_tags', + 'face_tags', + 'person_tags', + 'keyword_tags', + 'time_range_tag_start', + 'time_range_tag_end', + 'collections', + 'archive_tag', + 'comments', ], filterableAttributes: [ - "keyword_tags", - "location_tags", - "time_range_tag_start", - "time_range_tag_end", - "face_tags", - "person_tags", - "descriptions", - "comments", - "collections", - "archive_tag", - "is_text", - ], - sortableAttributes: [ - "time_range_tag_start", - "time_range_tag_end", - "likes", - ], - rankingRules: [ - "words", - "typo", - "proximity", - "attribute", - "sort", - "exactness", + 'keyword_tags', + 'location_tags', + 'time_range_tag_start', + 'time_range_tag_end', + 'face_tags', + 'person_tags', + 'descriptions', + 'comments', + 'collections', + 'archive_tag', + 'is_text', ], + sortableAttributes: ['time_range_tag_start', 'time_range_tag_end', 'likes'], + rankingRules: ['words', 'typo', 'proximity', 'attribute', 'sort', 'exactness'], // words that are ignored during searches, useful for common words // that do not carry a meaning on their own like articles, pronomina etc. // we do not use this setting, since our data on user searchers suggests, that From a69f76bccc3ea985754649218ea9d4e3b4d9bcd9 Mon Sep 17 00:00:00 2001 From: baerlach-git <83013270+baerlach-git@users.noreply.github.com> Date: Wed, 5 Jul 2023 12:47:57 +0200 Subject: [PATCH 17/34] added advanced search helptooltip + minor styling --- .../src/components/views/search/AdvancedSearch.tsx | 9 ++++++++- projects/bp-gallery/src/shared/locales/de.json | 6 ++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx b/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx index a8bd81e06..160370dc7 100644 --- a/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx +++ b/projects/bp-gallery/src/components/views/search/AdvancedSearch.tsx @@ -196,7 +196,13 @@ export const AdvancedSearch = ({ }} > }> - {t('search.advanced-search-title')} +
+ {t('search.advanced-search-title')} + +
@@ -232,6 +238,7 @@ export const AdvancedSearch = ({