diff --git a/src/components/Blocks/FacetedSearchBlockEdit.jsx b/src/components/Blocks/FacetedSearchBlockEdit.jsx index e9238263..cb2b8b33 100644 --- a/src/components/Blocks/FacetedSearchBlockEdit.jsx +++ b/src/components/Blocks/FacetedSearchBlockEdit.jsx @@ -18,7 +18,7 @@ const Edit = ({ data, onChangeBlock, block, selected }) => { - + ); }; diff --git a/src/components/Blocks/FacetedSearchBlockView.jsx b/src/components/Blocks/FacetedSearchBlockView.jsx index 14b27a2e..6cc0e92d 100644 --- a/src/components/Blocks/FacetedSearchBlockView.jsx +++ b/src/components/Blocks/FacetedSearchBlockView.jsx @@ -1,5 +1,3 @@ -import React from 'react'; - import FacetedSearch from '../Views/FacetedSearch'; const View = ({ data }) => { diff --git a/src/components/Blocks/schema.js b/src/components/Blocks/schema.js index 19a18112..2f90f0b8 100644 --- a/src/components/Blocks/schema.js +++ b/src/components/Blocks/schema.js @@ -69,12 +69,18 @@ export const SearchBlockSchema = ({ data = {}, intl }) => { 'allowed_content_types', 'allowed_review_states', 'searchedFields', + 'batchSize', ], }, { id: 'results', title: 'Results', - fields: ['extrainfo_fields', 'subjectsFieldname'], + fields: [ + 'extrainfo_fields', + 'subjectsFieldname', + 'showNewsItemPublishedDate', + 'showEventStartDate', + ], }, { id: 'divers', @@ -143,6 +149,11 @@ export const SearchBlockSchema = ({ data = {}, intl }) => { creatable: true, default: ['title^1.4', 'description^1.2', 'blocks_plaintext'], }, + batchSize: { + title: 'Batch size', + type: 'number', + default: 10, + }, facet_fields: { title: 'Facets', description: 'Fields to filter on.', @@ -167,7 +178,23 @@ export const SearchBlockSchema = ({ data = {}, intl }) => { title: 'Field name of tags field', description: 'Show tags to search for. Let the field empty to not show tags.', - default: '', + default: 'subjects', + }, + showNewsItemPublishedDate: { + title: 'Show published date of news items', + type: 'array', + widget: 'array', + items: { + vocabulary: { '@id': 'plone.app.vocabularies.UserFriendlyTypes' }, + }, + }, + showEventStartDate: { + title: 'Show start date of events', + type: 'array', + widget: 'array', + items: { + vocabulary: { '@id': 'plone.app.vocabularies.UserFriendlyTypes' }, + }, }, relocation: { title: 'Relocation', diff --git a/src/components/Searchkit/CustomESRequestSerializer.jsx b/src/components/Searchkit/CustomESRequestSerializer.jsx index 761245c8..4fe7e7f9 100644 --- a/src/components/Searchkit/CustomESRequestSerializer.jsx +++ b/src/components/Searchkit/CustomESRequestSerializer.jsx @@ -155,7 +155,7 @@ export class CustomESRequestSerializer { // fields with boosting let searchedFields = [...this.searchedFields]; - + let searchedFields_simple = searchedFields.map((fld) => { const fieldname = fld.split('^')[0]; return fld.replace(fieldname, `${fieldname}.${this.language}`); @@ -223,14 +223,19 @@ export class CustomESRequestSerializer { bodyParams['highlight'] = { number_of_fragments: 20, - fields: ['title', 'description', 'blocks_plaintext'].map(fieldname => { - return { - [fieldname]: { - matched_fields: [`${fieldname}.${this.language}`, `${fieldname}.${this.language}_exact`], - type: 'fvh', - }, - } - }) + fields: ['title', 'description', 'blocks_plaintext'].map( + (fieldname) => { + return { + [fieldname]: { + matched_fields: [ + `${fieldname}.${this.language}`, + `${fieldname}.${this.language}_exact`, + ], + type: 'fvh', + }, + }; + }, + ), }; } @@ -258,12 +263,14 @@ export class CustomESRequestSerializer { // Generate terms of global filters let terms = []; // If isMultilingual, search only in language - - this.language && volto_config.settings.isMultilingual && terms.push({ - terms: { - language: [this.language], - }, - }); + + this.language && + volto_config.settings.isMultilingual && + terms.push({ + terms: { + language: [this.language], + }, + }); this.allowed_content_types?.length > 0 && terms.push({ terms: { diff --git a/src/components/Searchkit/CustomESResponseSerializer.jsx b/src/components/Searchkit/CustomESResponseSerializer.jsx index 07cdfbee..8327a86d 100644 --- a/src/components/Searchkit/CustomESResponseSerializer.jsx +++ b/src/components/Searchkit/CustomESResponseSerializer.jsx @@ -41,7 +41,7 @@ export class CustomESResponseSerializer { hit._source['highlight'] = hit.highlight; return hit._source; }) || [], - total: hits?.total.value < 11 ? hits.hits.length : hits?.total.value || 0, + total: hits?.total.value || 0, }; return foo; } diff --git a/src/components/Searchkit/ElasticSearchHighlights.jsx b/src/components/Searchkit/ElasticSearchHighlights.jsx index c948b2c0..739fb2ab 100644 --- a/src/components/Searchkit/ElasticSearchHighlights.jsx +++ b/src/components/Searchkit/ElasticSearchHighlights.jsx @@ -6,7 +6,7 @@ import React from 'react'; import { useIntl } from 'react-intl'; import messages from '../../messages'; -export const ElasticSearchHighlights = ({ highlight, indexResult }) => { +export const ElasticSearchHighlights = ({ highlight }) => { const [toggleDetails, setToggleDetails] = React.useState(false); const intl = useIntl(); @@ -32,7 +32,6 @@ export const ElasticSearchHighlights = ({ highlight, indexResult }) => { onClick={showDetails} role="button" onKeyPress={showDetails} - tabIndex={indexResult} > {fragments.slice(0, 3).map((el, index) => { return
; @@ -44,7 +43,6 @@ export const ElasticSearchHighlights = ({ highlight, indexResult }) => { onClick={showDetails} role="button" onKeyPress={showDetails} - tabIndex={indexResult} > {Object.keys(highlight) .reverse() diff --git a/src/components/Searchkit/SearchBarSection.jsx b/src/components/Searchkit/SearchBarSection.jsx index 81939111..7c4917d4 100644 --- a/src/components/Searchkit/SearchBarSection.jsx +++ b/src/components/Searchkit/SearchBarSection.jsx @@ -13,7 +13,7 @@ const _SearchBarSection = (props) => { sortOrder: 'asc', layout: 'list', page: 1, - size: 10, + size: props.currentQueryState.data.batchSize, queryString: '', }, }; diff --git a/src/components/Searchkit/SectionsSearch.jsx b/src/components/Searchkit/SectionsSearch.jsx index 133ad80c..70b7b65d 100644 --- a/src/components/Searchkit/SectionsSearch.jsx +++ b/src/components/Searchkit/SectionsSearch.jsx @@ -67,7 +67,7 @@ const _SectionsSearch = (props) => { sortOrder: 'desc', layout: 'list', page: 1, - size: 10, + size: props.currentQueryState.data.batchSize, filters: currentQueryState.filters, }; if (currentQueryState.queryString) { diff --git a/src/components/Views/FacetedSearch.jsx b/src/components/Views/FacetedSearch.jsx index e5ebb623..7a157a9b 100644 --- a/src/components/Views/FacetedSearch.jsx +++ b/src/components/Views/FacetedSearch.jsx @@ -32,7 +32,7 @@ import { } from 'react-searchkit'; import { expandToBackendURL } from '@plone/volto/helpers'; -import { Icon } from '@plone/volto/components'; +import { FormattedDate, Icon } from '@plone/volto/components'; import leftAngle from '@plone/volto/icons/left-key.svg'; import rightAngle from '@plone/volto/icons/right-key.svg'; import firstAngle from '@plone/volto/icons/first.svg'; @@ -104,7 +104,7 @@ const _ExtraInfo = (props) => { const translate = (key, fieldname) => { let label = key; - if (querystringindexes || querystringindexes[fieldname]) { + if (querystringindexes && fieldname in querystringindexes) { label = querystringindexes[fieldname]?.values[key]?.title || key; } return label; @@ -131,7 +131,7 @@ const _ExtraInfo = (props) => { sortOrder: 'asc', layout: 'list', page: 1, - size: 10, + size: props.currentQueryState.data.batchSize, filters: [[`${extrainfo_key}_agg`, item]], }, }; @@ -182,7 +182,7 @@ const _ExtraInfo = (props) => { sortOrder: 'asc', layout: 'list', page: 1, - size: 10, + size: props.currentQueryState.data.batchSize, queryString: tito, }, }; @@ -206,15 +206,22 @@ const _ExtraInfo = (props) => { const ExtraInfo = withState(_ExtraInfo); const _CustomResultsListItem = (props) => { - const { result, index } = props; + const { result } = props; const backend_url = props.currentQueryState.data?.backend_url; const is_external_content = !result['@id'].includes(backend_url); const item_url = result['@id'].includes(backend_url) ? flattenESUrlToPath(result['@id']) : result['@id']; + const locale = useSelector((state) => state.query?.locale); const querystringindexes = useSelector( (state) => state.query?.data?.querystringindexes, ); + const showNewsItemPublishedDate = useSelector( + (state) => state.query?.data.showNewsItemPublishedDate, + ); + const showEventStartDate = useSelector( + (state) => state.query?.data.showEventStartDate, + ); const translate = (key) => { let label = key; @@ -226,7 +233,7 @@ const _CustomResultsListItem = (props) => { return ( @@ -241,7 +248,7 @@ const _CustomResultsListItem = (props) => { sortOrder: 'asc', layout: 'list', page: 1, - size: 10, + size: props.currentQueryState.data.batchSize, filters: [['informationtype_agg', item]], }, }; @@ -264,6 +271,17 @@ const _CustomResultsListItem = (props) => { > {result.title} + {showNewsItemPublishedDate.includes(result.portal_type) && + result.effective ? ( + + + + ) : null} + {showEventStartDate.includes(result.portal_type) && result.start ? ( + + + + ) : null} {truncate(result.description, { length: 200 })} @@ -275,6 +293,17 @@ const _CustomResultsListItem = (props) => { {result.title} + {showNewsItemPublishedDate.includes(result.portal_type) && + result.effective ? ( + + + + ) : null} + {showEventStartDate.includes(result.portal_type) && result.start ? ( + + + + ) : null} {truncate(result.description, { length: 200 })} @@ -283,10 +312,7 @@ const _CustomResultsListItem = (props) => { )} - + ); @@ -345,7 +371,7 @@ const CustomBucketAggregationElement = (props) => { * @returns */ const translate = (bucks) => { - if (querystringindexes[fieldname]) { + if (querystringindexes && fieldname in querystringindexes) { bucks.forEach((element) => { element.label = querystringindexes[fieldname].values[element.key]?.title || @@ -651,35 +677,6 @@ const sortValues = [ // }, ]; -const initialState = { - sortBy: 'bestmatch', - sortOrder: 'asc', - // sortBy: 'modified', - // sortOrder: 'desc', - queryString: '', - layout: 'list', - page: 1, - size: 10, -}; - -const defaultOverriddenComponents = { - 'ResultsList.item.elasticsearch': CustomResultsListItem, - 'Count.element': MyCountElement, - 'ActiveFilters.element': myActiveFiltersElement, - 'EmptyResults.element': customEmpytResultsElement, - 'Sort.element.volto': customSort, - 'Pagination.element': customPaginationElement, - 'Error.element': ErrorComponent, -}; - -const dropdownOverriddenComponents = { - 'BucketAggregation.element': CustomBucketAggregationElement, - 'BucketAggregationContainer.element': CustomBucketAggregationContainerElement, - 'BucketAggregationValues.element': withState( - CustomBucketAggregationValuesElement, - ), -}; - /** * FacetedSearch * @param {string} filterLayout default 'dropdown' @@ -704,6 +701,35 @@ const FacetedSearch = ({ data, overriddenComponents }) => { delete facet_fields_object.Subject; } + // TODO Get config from data + const initialState = { + page: 1, + queryString: '', + sortBy: 'modified', + sortOrder: 'desc', + size: data.batchSize, + layout: 'list', + }; + + const defaultOverriddenComponents = { + 'ResultsList.item.elasticsearch': CustomResultsListItem, + 'Count.element': MyCountElement, + 'ActiveFilters.element': myActiveFiltersElement, + 'EmptyResults.element': customEmpytResultsElement, + 'Sort.element.volto': customSort, + 'Pagination.element': customPaginationElement, + 'Error.element': ErrorComponent, + }; + + const dropdownOverriddenComponents = { + 'BucketAggregation.element': CustomBucketAggregationElement, + 'BucketAggregationContainer.element': + CustomBucketAggregationContainerElement, + 'BucketAggregationValues.element': withState( + CustomBucketAggregationValuesElement, + ), + }; + overriddenComponents = { ...defaultOverriddenComponents, ...(filterLayout === 'dropdown' && dropdownOverriddenComponents), @@ -712,8 +738,7 @@ const FacetedSearch = ({ data, overriddenComponents }) => { }; // TODO Check if check on client could be made simpler - const language = useSelector((state) => state.intl.locale); - console.debug('language', language); + const locale = useSelector((state) => state.intl.locale); const [isClient, setIsClient] = React.useState(null); React.useEffect(() => setIsClient(true), []); @@ -722,11 +747,13 @@ const FacetedSearch = ({ data, overriddenComponents }) => { {isClient && ( diff --git a/src/components/Views/TestSearchkitQuerystrings.jsx b/src/components/Views/TestSearchkitQuerystrings.jsx index 0d5e3148..5f456102 100644 --- a/src/components/Views/TestSearchkitQuerystrings.jsx +++ b/src/components/Views/TestSearchkitQuerystrings.jsx @@ -1,11 +1,13 @@ import React from 'react'; import { useIntl } from 'react-intl'; +import { createPortal } from 'react-dom'; import { Container, Header, Segment } from 'semantic-ui-react'; +import { useHistory } from 'react-router'; import { Link, useLocation } from 'react-router-dom'; -import { useDispatch, useSelector } from 'react-redux'; +import { useSelector } from 'react-redux'; import { OverridableContext } from 'react-overridable'; -import { getControlpanel } from '@plone/volto/actions'; -import { Icon as IconNext } from '@plone/volto/components'; +import { Icon, Toolbar } from '@plone/volto/components'; +import { getParentUrl } from '@plone/volto/helpers'; import backSVG from '@plone/volto/icons/back.svg'; import { @@ -21,6 +23,8 @@ import { ploneSearchApi } from './FacetedSearch'; import { ElasticSearchMatches } from '../Searchkit/ElasticSearchHighlights'; import messages from '../../messages'; +import config from '@plone/volto/registry'; + const sort_caseinsensitive = (a, b) => { var nameA = a.toUpperCase(); // Groß-/Kleinschreibung ignorieren var nameB = b.toUpperCase(); // Groß-/Kleinschreibung ignorieren @@ -103,27 +107,8 @@ const overriddenComponents = { const TestSearchkitQuerystrings = (props) => { const intl = useIntl(); - const dispatch = useDispatch(); - const searchkitblock_controlpanel = useSelector( - (state) => state.controlpanels.controlpanel?.data, - ); - const searchconfig = searchkitblock_controlpanel - ? { - elastic_search_api_url: - searchkitblock_controlpanel?.testsearch_elasticsearch_url, - elastic_search_api_index: - searchkitblock_controlpanel?.testsearch_elasticsearch_index, - - searchedFields: [], - facet_fields: [], - allowed_content_types: - searchkitblock_controlpanel?.allowed_content_types, - allowed_review_states: - searchkitblock_controlpanel?.allowed_review_states, - backend_url: searchkitblock_controlpanel?.testsearch_backend, - frontend_url: searchkitblock_controlpanel?.testsearch_frontend, - } - : {}; + const history = useHistory(); + const searchconfig = config.blocks.blocksConfig.searchkitblock.searchconfig; const initialState = { sortBy: 'bestmatch', @@ -145,69 +130,88 @@ const TestSearchkitQuerystrings = (props) => { return; }; + const locale = useSelector((state) => state.intl.locale); const [isClient, setIsClient] = React.useState(null); React.useEffect(() => setIsClient(true), []); - React.useEffect(() => { - dispatch(getControlpanel('volto_searchkit_block_control_panel')); - }, [dispatch]); - return ( - - -
- Matches -
-
- {isClient && searchkitblock_controlpanel && ( - - - <> - - {/* { - onchangehandler(event, data); - }} - /> */} - { - onchangehandler(event, data); - }} + + + +
+ Matches +
+
+ {isClient && ( + + + <> + + {/* { + onchangehandler(event, data); + }} + /> */} + { + onchangehandler(event, data); + }} + /> + + + + +
Documents with title and matches
+ +
+ +
+
+ )} +
+ + {isClient && + createPortal( + { + history.push(getParentUrl(location.pathname)); + }} + > + -
- - - -
Documents with title and matches
- -
- -
-
- )} - - - -
+ + } + />, + document.getElementById('toolbar'), + )} + ); }; diff --git a/src/components/helpers.jsx b/src/components/helpers.jsx index 919dffa5..71814bde 100644 --- a/src/components/helpers.jsx +++ b/src/components/helpers.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import { useSelector } from 'react-redux'; class NoSSR extends React.Component { state = { diff --git a/src/index.js b/src/index.js index 058e3fca..c6334a56 100644 --- a/src/index.js +++ b/src/index.js @@ -65,7 +65,6 @@ const applyConfig = (config) => { title: 'Test searchkit querystrings', }, ]; - config.addonRoutes = [ ...config.addonRoutes, { @@ -73,6 +72,20 @@ const applyConfig = (config) => { component: TestSearchkitQuerystrings, }, ]; + // Configure 'Test searchkit querystrings' controlpanel + config.blocks.blocksConfig.searchkitblock.searchconfig = { + searchedFields: [ + 'title^1.4', + 'description^1.2', + 'blocks_plaintext', + 'subjects^1.2', + ], + facet_fields: [], + allowed_content_types: ['Document', 'News Item', 'Event'], + allowed_review_states: [], + backend_url: 'http://host.docker.internal:8080/Plone', + frontend_url: 'http://localhost:3000', + }; // Fetch querystring indexes. // See /effective-volto/addons/asyncconnect diff --git a/src/messages.js b/src/messages.js index d733e786..ca12b01e 100644 --- a/src/messages.js +++ b/src/messages.js @@ -71,6 +71,10 @@ const messages = defineMessages({ id: 'Content', defaultMessage: 'Content', }, + cancel: { + id: 'Cancel', + defaultMessage: 'Cancel', + }, }); export default messages;