From de4de97e4bfa714168b4e5ce949d07eb696f24a5 Mon Sep 17 00:00:00 2001 From: Miu Razvan Date: Tue, 8 Sep 2020 10:19:30 +0300 Subject: [PATCH] Update --- package.json | 6 +- .../manage/Blocks/DetailedLink/View.jsx | 2 + .../DiscodataComponents/Button/Edit.jsx | 70 -- .../DiscodataComponents/Button/View.jsx | 78 -- .../Blocks/DiscodataComponents/Link/Edit.jsx | 70 -- .../Blocks/DiscodataComponents/Link/View.jsx | 78 -- .../Blocks/DiscodataComponents/List/Edit.jsx | 145 ++-- .../Blocks/DiscodataComponents/List/View.jsx | 183 +++-- .../DiscodataComponents/Select/Edit.jsx | 88 ++- .../DiscodataComponents/Select/View.jsx | 192 ++--- .../Blocks/DiscodataComponents/Text/View.jsx | 99 ++- .../Widgets/ColorPickerWidget.jsx | 34 - .../DiscodataComponents/Widgets/style.css | 10 - .../Blocks/DiscodataComponents/schema.jsx | 178 +++-- .../Blocks/DiscodataComponentsBlock/Edit.jsx | 454 +++++++++++ .../Blocks/DiscodataComponentsBlock/View.jsx | 705 ++++++++++++++++++ .../Blocks/DiscodataComponentsBlock/style.css | 474 ++++++++++++ .../DiscodataOpenlayersMapBlock/Edit.jsx | 9 +- .../DiscodataOpenlayersMapBlock/View.jsx | 216 +++--- .../manage/Blocks/FiltersBlock/Edit.jsx | 2 +- .../manage/Blocks/FiltersBlock/View.jsx | 114 ++- src/components/manage/Blocks/Iframe/Edit.jsx | 2 +- .../manage/Blocks/LinkButton/Edit.jsx | 2 +- .../manage/Blocks/NavigationBlock/Edit.jsx | 2 +- .../manage/Blocks/QueryParamButton/Edit.jsx | 2 +- .../manage/Blocks/QueryParamText/Edit.jsx | 3 +- .../manage/Blocks/QueryParamText/View.jsx | 1 + .../manage/Blocks/QueryParamText/style.css | 4 + .../manage/Blocks/SidebarBlock/Edit.jsx | 2 +- src/config.js | 7 +- .../volto/components/theme/Search/Search.jsx | 2 - src/localconfig.js | 26 +- theme/site/globals/site.overrides | 18 + 33 files changed, 2555 insertions(+), 723 deletions(-) delete mode 100644 src/components/manage/Blocks/DiscodataComponents/Button/Edit.jsx delete mode 100644 src/components/manage/Blocks/DiscodataComponents/Button/View.jsx delete mode 100644 src/components/manage/Blocks/DiscodataComponents/Link/Edit.jsx delete mode 100644 src/components/manage/Blocks/DiscodataComponents/Link/View.jsx delete mode 100644 src/components/manage/Blocks/DiscodataComponents/Widgets/ColorPickerWidget.jsx create mode 100644 src/components/manage/Blocks/DiscodataComponentsBlock/Edit.jsx create mode 100644 src/components/manage/Blocks/DiscodataComponentsBlock/View.jsx create mode 100644 src/components/manage/Blocks/DiscodataComponentsBlock/style.css create mode 100644 src/components/manage/Blocks/QueryParamText/style.css diff --git a/package.json b/package.json index 971750b4..b5b4fe23 100644 --- a/package.json +++ b/package.json @@ -17,10 +17,10 @@ "@eeacms/volto-slate-metadata-mentions", "@eeacms/volto-metadata-block", "@eeacms/volto-tabs-block", - "@eeacms/volto-grid-block", "@eeacms/volto-widget-toggle", "volto-addons", - "volto-datablocks" + "volto-datablocks", + "@eeacms/volto-grid-block" ], "scripts": { "start": "razzle start", @@ -121,7 +121,7 @@ "ol": "^6.4.3", "performant-array-to-tree": "^1.7.1", "react": "^16.13.1", - "react-color": "^2.18.1", + "react-beautiful-dnd": "^13.0.0", "react-component-queries": "^2.3.0", "react-dom": "^16.13.1", "react-highlight-words": "^0.16.0", diff --git a/src/components/manage/Blocks/DetailedLink/View.jsx b/src/components/manage/Blocks/DetailedLink/View.jsx index ba57670a..efb7eaae 100644 --- a/src/components/manage/Blocks/DetailedLink/View.jsx +++ b/src/components/manage/Blocks/DetailedLink/View.jsx @@ -4,6 +4,7 @@ import { Link } from 'react-router-dom'; import { settings } from '~/config'; import cx from 'classnames'; import './style.css'; +import { useEffect } from 'react'; const getPath = (url) => { if (!url) return ''; @@ -26,6 +27,7 @@ const View = (props) => { descriptionClassname = '', buttonClassname = '', } = props.data; + return (
{ - return { - queryParam: { - title: 'Query parameter', - type: 'text', - }, - visible: { - title: 'Visible when', - type: 'array', - choices: [ - ['hasQuery', 'Query parameter exists'], - ['always', 'Always'], - ], - }, - leftText: { - title: 'Left text', - widget: 'textarea', - }, - rightText: { - title: 'Right text', - widget: 'textarea', - }, - color: { - title: 'Color', - type: 'text', - }, - component: { - title: 'Component type', - type: 'array', - choices: [ - ['h1', 'H1'], - ['h2', 'H2'], - ['h3', 'H3'], - ['p', 'Paragraph'], - ], - }, - }; -}; - -const Edit = (props) => { - const [state, setState] = useState({ - schema: getSchema({ ...props, providerUrl: settings.providerUrl }), - id: _uniqueId('block_'), - }); - return ( -
- - -
- ); -}; - -export default compose( - connect((state, props) => ({ - pathname: state.router.location.pathname, - })), -)(Edit); diff --git a/src/components/manage/Blocks/DiscodataComponents/Button/View.jsx b/src/components/manage/Blocks/DiscodataComponents/Button/View.jsx deleted file mode 100644 index 8a0c7dcc..00000000 --- a/src/components/manage/Blocks/DiscodataComponents/Button/View.jsx +++ /dev/null @@ -1,78 +0,0 @@ -/* REACT */ -import React, { useState } from 'react'; -import { compose } from 'redux'; -import { connect } from 'react-redux'; - -function isColor(strColor) { - return /^#[0-9A-F]{6}$/i.test(strColor); -} - -const components = { - h1: (text, color) => ( -

- {text} -

- ), - h2: (text, color) => ( -

- {text} -

- ), - h3: (text, color) => ( -

- {text} -

- ), - p: (text, color) => ( -

- {text} -

- ), -}; - -const View = ({ content, ...props }) => { - const { data } = props; - const { - visible = 'always', - component = 'h1', - queryParam = '', - leftText = '', - rightText = '', - color = '#000', - } = data; - - const queryText = props.search[queryParam] || ''; - - const text = `${leftText} ${queryText} ${rightText}`; - - const hasText = leftText || queryText || rightText; - - const textMayRender = - (visible === 'always' && hasText) || (visible === 'hasQuery' && queryText); - - return ( - <> - {props.mode === 'edit' ? ( - !textMayRender ? ( -

Query param text

- ) : ( - '' - ) - ) : ( - '' - )} - {textMayRender && components[component] - ? components[component](text, isColor(color) ? color : '#000') - : ''} - - ); -}; - -export default compose( - connect((state, props) => ({ - query: state.router.location.search, - content: - state.prefetch?.[state.router.location.pathname] || state.content.data, - search: state.discodata_query.search, - })), -)(View); diff --git a/src/components/manage/Blocks/DiscodataComponents/Link/Edit.jsx b/src/components/manage/Blocks/DiscodataComponents/Link/Edit.jsx deleted file mode 100644 index a142e25e..00000000 --- a/src/components/manage/Blocks/DiscodataComponents/Link/Edit.jsx +++ /dev/null @@ -1,70 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { connect } from 'react-redux'; -import { compose } from 'redux'; -import _uniqueId from 'lodash/uniqueId'; -import RenderFields from 'volto-addons/Widgets/RenderFields'; -import View from './View'; -import { settings } from '~/config'; - -const getSchema = (props) => { - return { - queryParam: { - title: 'Query parameter', - type: 'text', - }, - visible: { - title: 'Visible when', - type: 'array', - choices: [ - ['hasQuery', 'Query parameter exists'], - ['always', 'Always'], - ], - }, - leftText: { - title: 'Left text', - widget: 'textarea', - }, - rightText: { - title: 'Right text', - widget: 'textarea', - }, - color: { - title: 'Color', - type: 'text', - }, - component: { - title: 'Component type', - type: 'array', - choices: [ - ['h1', 'H1'], - ['h2', 'H2'], - ['h3', 'H3'], - ['p', 'Paragraph'], - ], - }, - }; -}; - -const Edit = (props) => { - const [state, setState] = useState({ - schema: getSchema({ ...props, providerUrl: settings.providerUrl }), - id: _uniqueId('block_'), - }); - return ( -
- - -
- ); -}; - -export default compose( - connect((state, props) => ({ - pathname: state.router.location.pathname, - })), -)(Edit); diff --git a/src/components/manage/Blocks/DiscodataComponents/Link/View.jsx b/src/components/manage/Blocks/DiscodataComponents/Link/View.jsx deleted file mode 100644 index 8a0c7dcc..00000000 --- a/src/components/manage/Blocks/DiscodataComponents/Link/View.jsx +++ /dev/null @@ -1,78 +0,0 @@ -/* REACT */ -import React, { useState } from 'react'; -import { compose } from 'redux'; -import { connect } from 'react-redux'; - -function isColor(strColor) { - return /^#[0-9A-F]{6}$/i.test(strColor); -} - -const components = { - h1: (text, color) => ( -

- {text} -

- ), - h2: (text, color) => ( -

- {text} -

- ), - h3: (text, color) => ( -

- {text} -

- ), - p: (text, color) => ( -

- {text} -

- ), -}; - -const View = ({ content, ...props }) => { - const { data } = props; - const { - visible = 'always', - component = 'h1', - queryParam = '', - leftText = '', - rightText = '', - color = '#000', - } = data; - - const queryText = props.search[queryParam] || ''; - - const text = `${leftText} ${queryText} ${rightText}`; - - const hasText = leftText || queryText || rightText; - - const textMayRender = - (visible === 'always' && hasText) || (visible === 'hasQuery' && queryText); - - return ( - <> - {props.mode === 'edit' ? ( - !textMayRender ? ( -

Query param text

- ) : ( - '' - ) - ) : ( - '' - )} - {textMayRender && components[component] - ? components[component](text, isColor(color) ? color : '#000') - : ''} - - ); -}; - -export default compose( - connect((state, props) => ({ - query: state.router.location.search, - content: - state.prefetch?.[state.router.location.pathname] || state.content.data, - search: state.discodata_query.search, - })), -)(View); diff --git a/src/components/manage/Blocks/DiscodataComponents/List/Edit.jsx b/src/components/manage/Blocks/DiscodataComponents/List/Edit.jsx index a142e25e..16481ae3 100644 --- a/src/components/manage/Blocks/DiscodataComponents/List/Edit.jsx +++ b/src/components/manage/Blocks/DiscodataComponents/List/Edit.jsx @@ -2,63 +2,120 @@ import React, { useState, useEffect } from 'react'; import { connect } from 'react-redux'; import { compose } from 'redux'; import _uniqueId from 'lodash/uniqueId'; -import RenderFields from 'volto-addons/Widgets/RenderFields'; import View from './View'; import { settings } from '~/config'; +import { isArray, isObject } from 'lodash'; + +import InlineForm from '@plone/volto/components/manage/Form/InlineForm'; +import { SidebarPortal } from '@plone/volto/components'; + +import { makeSelectSchema } from '../schema'; const getSchema = (props) => { - return { - queryParam: { - title: 'Query parameter', - type: 'text', - }, - visible: { - title: 'Visible when', - type: 'array', - choices: [ - ['hasQuery', 'Query parameter exists'], - ['always', 'Always'], - ], - }, - leftText: { - title: 'Left text', - widget: 'textarea', - }, - rightText: { - title: 'Right text', - widget: 'textarea', - }, - color: { - title: 'Color', - type: 'text', - }, - component: { - title: 'Component type', - type: 'array', - choices: [ - ['h1', 'H1'], - ['h2', 'H2'], - ['h3', 'H3'], - ['p', 'Paragraph'], - ], - }, - }; + return makeSelectSchema(props); }; const Edit = (props) => { + const [discodataValues, setDiscodataValues] = useState([]); + const [mounted, setMounted] = useState(false); + const { data } = props; + const { resources = [], subResources = [] } = data; const [state, setState] = useState({ - schema: getSchema({ ...props, providerUrl: settings.providerUrl }), + schema: getSchema({ ...props, discodataValues }), id: _uniqueId('block_'), }); + + const updateDiscodataValues = (mounted) => { + if ( + props.discodata_resources.data && + props.discodata_query.search && + mounted + ) { + let newDiscodataValues = []; + resources.forEach((resource) => { + if (isArray(props.discodata_resources.data[resource.package])) { + newDiscodataValues = [ + ...newDiscodataValues, + ...(props.discodata_resources.data[resource.package] || []), + ]; + } + }); + const selectedSubResources = subResources.map((subResource) => { + const keyValue = subResource.package?.split('@') || [null, null]; + return { + package: keyValue[0], + query: keyValue[1], + }; + }); + selectedSubResources.forEach((subResource) => { + const discodataPackage = resources.filter( + (resource) => resource.package === subResource.package, + )[0]; + if ( + props.discodata_query.search[discodataPackage.queryParameter] && + isArray( + props.discodata_resources.data[discodataPackage.package]?.[ + props.discodata_query.search[discodataPackage.queryParameter] + ]?.[subResource.query], + ) + ) { + newDiscodataValues = [ + ...newDiscodataValues, + ...(props.discodata_resources.data[discodataPackage.package]?.[ + props.discodata_query.search[discodataPackage.queryParameter] + ][subResource.query] || []), + ]; + } + }); + setDiscodataValues(newDiscodataValues); + return newDiscodataValues; + } + return []; + }; + + useEffect(() => { + setMounted(true); + updateDiscodataValues(true); + /* eslint-disable-next-line */ + }, []); + + useEffect(() => { + updateDiscodataValues(mounted); + /* eslint-disable-next-line */ + }, [props.discodata_query.search, props.discodata_resources.data]); + + useEffect(() => { + const schema = getSchema({ ...props, discodataValues }); + setState({ + ...state, + schema, + }); + /* eslint-disable-next-line */ + }, [discodataValues, props.data.isLink]); + return (
- + { + const { data } = props; + props.onChangeBlock(props.block, { + ...data, + [id]: value, + }); + }} + formData={props.data} + block={props.block} + /> + + -
); }; @@ -66,5 +123,7 @@ const Edit = (props) => { export default compose( connect((state, props) => ({ pathname: state.router.location.pathname, + discodata_resources: state.discodata_resources, + discodata_query: state.discodata_query, })), )(Edit); diff --git a/src/components/manage/Blocks/DiscodataComponents/List/View.jsx b/src/components/manage/Blocks/DiscodataComponents/List/View.jsx index 8a0c7dcc..7397f9e9 100644 --- a/src/components/manage/Blocks/DiscodataComponents/List/View.jsx +++ b/src/components/manage/Blocks/DiscodataComponents/List/View.jsx @@ -1,78 +1,153 @@ /* REACT */ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { compose } from 'redux'; import { connect } from 'react-redux'; +import { isArray } from 'lodash'; +import { Dropdown } from 'semantic-ui-react'; +import { setQueryParam } from 'volto-datablocks/actions'; -function isColor(strColor) { - return /^#[0-9A-F]{6}$/i.test(strColor); -} +import cx from 'classnames'; const components = { - h1: (text, color) => ( -

- {text} -

- ), - h2: (text, color) => ( -

- {text} -

- ), - h3: (text, color) => ( -

- {text} -

- ), - p: (text, color) => ( -

- {text} -

- ), + select: ( + options, + queryParameters, + search, + setQueryParam, + placeholder, + className, + mode, + ) => { + let activeValue = ''; + if (queryParameters[0]?.queryParameterToSet) { + activeValue = search[queryParameters[0].queryParameterToSet]; + } + return ( +
+ { + const queryParametersToSet = {}; + queryParameters.forEach((queryParam) => { + queryParametersToSet[ + queryParam.queryParameterToSet + ] = data.options.filter((opt) => { + return opt.value === data.value; + })[0]?.[queryParam.selectorOptionKey]; + }); + setQueryParam({ + queryParam: { + ...(queryParametersToSet || {}), + }, + }); + }} + placeholder={placeholder} + options={options} + value={activeValue} + /> +
+ ); + }, }; const View = ({ content, ...props }) => { + const [discodataValues, setDiscodataValues] = useState([]); + const [mounted, setMounted] = useState(false); const { data } = props; - const { - visible = 'always', - component = 'h1', - queryParam = '', - leftText = '', - rightText = '', - color = '#000', - } = data; + const { resources = [], subResources = [] } = data; + const { placeholder = 'Select', className = '' } = data; + const { key = '', value = '', text = '', queryParametersToSet = [] } = data; + + const options = discodataValues.map((discodata, index) => ({ + key: discodata[key] || index, + value: discodata[value] || index, + text: discodata[text] || index, + })); - const queryText = props.search[queryParam] || ''; + const updateDiscodataValues = (mounted) => { + if (props.discodata_resources && props.search && mounted) { + let newDiscodataValues = []; + resources.forEach((resource) => { + if (isArray(props.discodata_resources[resource.package])) { + newDiscodataValues = [ + ...newDiscodataValues, + ...(props.discodata_resources[resource.package] || []), + ]; + } + }); + const selectedSubResources = subResources.map((subResource) => { + const keyValue = subResource.package?.split('@') || [null, null]; + return { + package: keyValue[0], + query: keyValue[1], + }; + }); + selectedSubResources.forEach((subResource) => { + const discodataPackage = resources.filter( + (resource) => resource.package === subResource.package, + )[0]; + if ( + props.search[discodataPackage.queryParameter] && + isArray( + props.discodata_resources[discodataPackage.package]?.[ + props.search[discodataPackage.queryParameter] + ]?.[subResource.query], + ) + ) { + newDiscodataValues = [ + ...newDiscodataValues, + ...(props.discodata_resources[discodataPackage.package]?.[ + props.search[discodataPackage.queryParameter] + ][subResource.query] || []), + ]; + } + }); + setDiscodataValues(newDiscodataValues); + } + }; - const text = `${leftText} ${queryText} ${rightText}`; + useEffect(() => { + setMounted(true); + if (props.mode !== 'edit') { + updateDiscodataValues(true); + } else { + setDiscodataValues(props.discodataValues); + } + /* eslint-disable-next-line */ + }, []); - const hasText = leftText || queryText || rightText; + useEffect(() => { + if (props.mode !== 'edit') { + updateDiscodataValues(mounted); + } else { + setDiscodataValues(props.discodataValues); + } + /* eslint-disable-next-line */ + }, [props.search, props.discodata_resources]) - const textMayRender = - (visible === 'always' && hasText) || (visible === 'hasQuery' && queryText); return ( <> - {props.mode === 'edit' ? ( - !textMayRender ? ( -

Query param text

- ) : ( - '' - ) - ) : ( - '' + {components.select( + options, + queryParametersToSet, + props.search, + props.setQueryParam, + placeholder, + className, + props.mode, )} - {textMayRender && components[component] - ? components[component](text, isColor(color) ? color : '#000') - : ''} ); }; export default compose( - connect((state, props) => ({ - query: state.router.location.search, - content: - state.prefetch?.[state.router.location.pathname] || state.content.data, - search: state.discodata_query.search, - })), + connect( + (state, props) => ({ + query: state.router.location.search, + search: state.discodata_query.search, + discodata_resources: state.discodata_resources.data, + }), + { setQueryParam }, + ), )(View); diff --git a/src/components/manage/Blocks/DiscodataComponents/Select/Edit.jsx b/src/components/manage/Blocks/DiscodataComponents/Select/Edit.jsx index 4f941fd4..013428ba 100644 --- a/src/components/manage/Blocks/DiscodataComponents/Select/Edit.jsx +++ b/src/components/manage/Blocks/DiscodataComponents/Select/Edit.jsx @@ -9,25 +9,96 @@ import { isArray, isObject } from 'lodash'; import InlineForm from '@plone/volto/components/manage/Form/InlineForm'; import { SidebarPortal } from '@plone/volto/components'; -import { makeTextSchema } from '../schema'; +import { makeSelectSchema } from '../schema'; const getSchema = (props) => { - return makeTextSchema(props); + return makeSelectSchema(props); }; const Edit = (props) => { + const [discodataValues, setDiscodataValues] = useState([]); + const [mounted, setMounted] = useState(false); + const { data } = props; + const { resources = [], subResources = [] } = data; const [state, setState] = useState({ - schema: getSchema({ ...props }), + schema: getSchema({ ...props, discodataValues }), id: _uniqueId('block_'), }); + const updateDiscodataValues = (mounted) => { + if ( + props.discodata_resources.data && + props.discodata_query.search && + mounted + ) { + let newDiscodataValues = []; + resources.forEach((resource) => { + if (isArray(props.discodata_resources.data[resource.package])) { + newDiscodataValues = [ + ...newDiscodataValues, + ...(props.discodata_resources.data[resource.package] || []), + ]; + } + }); + const selectedSubResources = subResources.map((subResource) => { + const keyValue = subResource.package?.split('@') || [null, null]; + return { + package: keyValue[0], + query: keyValue[1], + }; + }); + selectedSubResources.forEach((subResource) => { + const discodataPackage = resources.filter( + (resource) => resource.package === subResource.package, + )[0]; + if ( + props.discodata_query.search[discodataPackage.queryParameter] && + isArray( + props.discodata_resources.data[discodataPackage.package]?.[ + props.discodata_query.search[discodataPackage.queryParameter] + ]?.[subResource.query], + ) + ) { + newDiscodataValues = [ + ...newDiscodataValues, + ...(props.discodata_resources.data[discodataPackage.package]?.[ + props.discodata_query.search[discodataPackage.queryParameter] + ][subResource.query] || []), + ]; + } + }); + setDiscodataValues(newDiscodataValues); + return newDiscodataValues; + } + return []; + }; + + useEffect(() => { + setMounted(true); + updateDiscodataValues(true); + /* eslint-disable-next-line */ + }, []); + + useEffect(() => { + updateDiscodataValues(mounted); + /* eslint-disable-next-line */ + }, [props.discodata_query.search, props.discodata_resources.data]); + useEffect(() => { + const schema = getSchema({ ...props, discodataValues }); setState({ ...state, - schema: getSchema({ ...props }), + schema, }); /* eslint-disable-next-line */ - }, [props.discodata_query.search, props.discodata_resources.data]); + }, [ + discodataValues, + JSON.stringify(props.data.resources), + JSON.stringify(props.data.subResource), + JSON.stringify(props.discodata_resources.data), + JSON.stringify(props.discodata_query.search), + ]); + return (
@@ -45,7 +116,12 @@ const Edit = (props) => { block={props.block} /> - +
); }; diff --git a/src/components/manage/Blocks/DiscodataComponents/Select/View.jsx b/src/components/manage/Blocks/DiscodataComponents/Select/View.jsx index 3d3ffefd..0d67e4c3 100644 --- a/src/components/manage/Blocks/DiscodataComponents/Select/View.jsx +++ b/src/components/manage/Blocks/DiscodataComponents/Select/View.jsx @@ -2,37 +2,52 @@ import React, { useEffect, useState } from 'react'; import { compose } from 'redux'; import { connect } from 'react-redux'; -import ReactTooltip from 'react-tooltip'; +import { isArray } from 'lodash'; +import { Dropdown } from 'semantic-ui-react'; +import { setQueryParam } from 'volto-datablocks/actions'; -function isColor(strColor) { - return /^#[0-9A-F]{6}$/i.test(strColor); -} +import cx from 'classnames'; const components = { - h1: (text, color, tooltip = false, tooltipText = '') => ( -

- {text} -

- ), - h2: (text, color) => ( -

- {text} -

- ), - h3: (text, color) => ( -

- {text} -

- ), - p: (text, color) => ( -

- {text} -

- ), + select: ( + options, + queryParameters, + search, + setQueryParam, + placeholder, + className, + mode, + ) => { + let activeValue = ''; + if (queryParameters[0]?.queryParameterToSet) { + activeValue = search[queryParameters[0].queryParameterToSet]; + } + return ( +
+ { + const queryParametersToSet = {}; + queryParameters.forEach((queryParam) => { + queryParametersToSet[ + queryParam.queryParameterToSet + ] = data.options.filter((opt) => { + return opt.value === data.value; + })[0]?.[queryParam.selectorOptionKey]; + }); + setQueryParam({ + queryParam: { + ...(queryParametersToSet || {}), + }, + }); + }} + placeholder={placeholder} + options={options} + value={activeValue} + /> +
+ ); + }, }; const View = ({ content, ...props }) => { @@ -40,26 +55,28 @@ const View = ({ content, ...props }) => { const [mounted, setMounted] = useState(false); const { data } = props; const { resources = [], subResources = [] } = data; - const { - visible = 'always', - component = 'h1', - leftText = '', - rightText = '', - color = '#000', - order = 'dq', - } = data; - const { - isLink = false, - internalLink = false, - linkTarget = '_blank', - link = '/', - triggerOn = '_all', - } = data; - const { tooltip = false, tooltipText = '' } = data; + const { placeholder = 'Select', className = '' } = data; + const { key = '', value = '', text = '', queryParametersToSet = [] } = data; + + const options = discodataValues + .filter((discodata) => discodata[value]) + .map((discodata, index) => ({ + key: discodata[key] || index, + value: discodata[value] || index, + text: discodata[text] || index, + })); const updateDiscodataValues = (mounted) => { if (props.discodata_resources && props.search && mounted) { let newDiscodataValues = []; + resources.forEach((resource) => { + if (isArray(props.discodata_resources[resource.package])) { + newDiscodataValues = [ + ...newDiscodataValues, + ...(props.discodata_resources[resource.package] || []), + ]; + } + }); const selectedSubResources = subResources.map((subResource) => { const keyValue = subResource.package?.split('@') || [null, null]; return { @@ -71,12 +88,20 @@ const View = ({ content, ...props }) => { const discodataPackage = resources.filter( (resource) => resource.package === subResource.package, )[0]; - if (props.search[discodataPackage.queryParameter]) { - newDiscodataValues.push( + if ( + props.search[discodataPackage.queryParameter] && + isArray( props.discodata_resources[discodataPackage.package]?.[ props.search[discodataPackage.queryParameter] ]?.[subResource.query], - ); + ) + ) { + newDiscodataValues = [ + ...newDiscodataValues, + ...(props.discodata_resources[discodataPackage.package]?.[ + props.search[discodataPackage.queryParameter] + ][subResource.query] || []), + ]; } }); setDiscodataValues(newDiscodataValues); @@ -85,65 +110,46 @@ const View = ({ content, ...props }) => { useEffect(() => { setMounted(true); - updateDiscodataValues(true); + if (props.mode !== 'edit') { + updateDiscodataValues(true); + } else { + setDiscodataValues(props.discodataValues); + } /* eslint-disable-next-line */ }, []); useEffect(() => { - updateDiscodataValues(mounted); + if (props.mode !== 'edit') { + updateDiscodataValues(mounted); + } else { + setDiscodataValues(props.discodataValues); + } /* eslint-disable-next-line */ - }, [props.search, props.discodata_resources]) - - const queryParametersText = props.data.queryParameters - .map((value) => { - return props.search[value.queryParameter]; - }) - .filter((value) => value) - .join(' '); - - const discodataText = discodataValues.join(' '); + }, [props.search, props.discodata_resources, props.discodataValues]) - const text = `${leftText} ${ - order === 'dq' - ? `${discodataText} ${queryParametersText}` - : `${queryParametersText} ${discodataText}` - } ${rightText}`; - - const hasText = leftText || discodataText || queryParametersText || rightText; - - const textMayRender = - (visible === 'always' && hasText) || - (visible === 'hasQuery' && queryParametersText) || - (visible === 'hasDiscodata' && discodataText); return ( <> - {props.mode === 'edit' ? ( - !textMayRender ? ( -

Query param text

- ) : ( - '' - ) - ) : ( - '' + {components.select( + options, + queryParametersToSet, + props.search, + props.setQueryParam, + placeholder, + className, + props.mode, )} - {textMayRender && components[component] - ? components[component]( - text, - isColor(color) ? color : '#000', - tooltip, - tooltipText, - ) - : ''} - ); }; export default compose( - connect((state, props) => ({ - query: state.router.location.search, - search: state.discodata_query.search, - discodata_resources: state.discodata_resources.data, - })), + connect( + (state, props) => ({ + query: state.router.location.search, + search: state.discodata_query.search, + discodata_resources: state.discodata_resources.data, + }), + { setQueryParam }, + ), )(View); diff --git a/src/components/manage/Blocks/DiscodataComponents/Text/View.jsx b/src/components/manage/Blocks/DiscodataComponents/Text/View.jsx index b4e14957..ff65d531 100644 --- a/src/components/manage/Blocks/DiscodataComponents/Text/View.jsx +++ b/src/components/manage/Blocks/DiscodataComponents/Text/View.jsx @@ -4,6 +4,7 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import ReactTooltip from 'react-tooltip'; import Icon from '@plone/volto/components/theme/Icon/Icon'; +import { NavLink } from 'react-router-dom'; import infoSVG from '@plone/volto/icons/info.svg'; @@ -39,13 +40,30 @@ const components = { {text}

), + link: (text, internalLink, linkTarget, link, color) => { + return internalLink ? ( + + {text} + + ) : ( + + {text} + + ); + }, }; const View = ({ content, ...props }) => { const [discodataValues, setDiscodataValues] = useState([]); const [mounted, setMounted] = useState(false); const { data } = props; - const { resources = [], subResources = [] } = data; + const { resources = [], subResources = [], queryParameters = [] } = data; const { visible = 'always', component = 'h1', @@ -100,20 +118,23 @@ const View = ({ content, ...props }) => { /* eslint-disable-next-line */ }, [props.search, props.discodata_resources]) - const queryParametersText = props.data.queryParameters - .map((value) => { - return props.search[value.queryParameter]; - }) - .filter((value) => value) - .join(' '); + const queryParametersText = queryParameters + ? queryParameters + .map((value) => { + return props.search[value.queryParameter]; + }) + .filter((value) => value) + .join(' ') + : ''; const discodataText = discodataValues.join(' '); - const text = `${leftText} ${ + const byOrder = order === 'dq' ? `${discodataText} ${queryParametersText}` - : `${queryParametersText} ${discodataText}` - } ${rightText}`; + : `${queryParametersText} ${discodataText}`; + + const text = `${leftText} ${byOrder} ${rightText}`; const hasText = leftText || discodataText || queryParametersText || rightText; @@ -122,11 +143,65 @@ const View = ({ content, ...props }) => { (visible === 'hasQuery' && queryParametersText) || (visible === 'hasDiscodata' && discodataText); + const renderLinks = { + _all: ( + + {components.link( + `${leftText} ${byOrder} ${rightText}`, + internalLink, + linkTarget, + link, + color, + )} + + ), + _query: ( + + {leftText} + {order === 'dq' ? discodataText : ''} + {components.link( + `${queryParametersText}`, + internalLink, + linkTarget, + link, + color, + )} + {order === 'dq' ? '' : 'discodataText'} + {rightText} + + ), + _discodata: ( + + {leftText} {order === 'qd' ? queryParametersText : ''} + {components.link( + `${discodataText}`, + internalLink, + linkTarget, + link, + color, + )} + {order === 'qd' ? '' : queryParametersText} {rightText} + + ), + _left: ( + + {components.link(`${leftText}`, internalLink, linkTarget, link, color)}{' '} + {byOrder} {rightText} + + ), + _right: ( + + {leftText} {byOrder}{' '} + {components.link(`${rightText}`, internalLink, linkTarget, link, color)} + + ), + }; + return ( <> {props.mode === 'edit' ? ( !textMayRender ? ( -

Query param text

+

Discodata component text

) : ( '' ) @@ -135,7 +210,7 @@ const View = ({ content, ...props }) => { )} {textMayRender && components[component] ? components[component]( - text, + isLink ? renderLinks[triggerOn] : text, isColor(color) ? color : '#000', tooltip, tooltipText, diff --git a/src/components/manage/Blocks/DiscodataComponents/Widgets/ColorPickerWidget.jsx b/src/components/manage/Blocks/DiscodataComponents/Widgets/ColorPickerWidget.jsx deleted file mode 100644 index bfe814f0..00000000 --- a/src/components/manage/Blocks/DiscodataComponents/Widgets/ColorPickerWidget.jsx +++ /dev/null @@ -1,34 +0,0 @@ -import React, { useState } from 'react'; -import { connect } from 'react-redux'; -import { compose } from 'redux'; -import { CompactPicker } from 'react-color'; -import { TextareaWidget } from '@plone/volto/components'; -import './style.css'; - -const ColorPickerWidget = (props) => { - const [state, setState] = useState({ - color: '#fff', - }); - - const handleChangeComplete = (color) => { - props.onChange(props.id, color.hex); - }; - - return ( -
- {props.title} - -
- ); -}; - -export default compose( - connect((state, props) => ({ - pathname: state.router.location.pathname, - discodata_query: state.discodata_query, - })), -)(ColorPickerWidget); diff --git a/src/components/manage/Blocks/DiscodataComponents/Widgets/style.css b/src/components/manage/Blocks/DiscodataComponents/Widgets/style.css index 10c0f91e..b6f09fda 100644 --- a/src/components/manage/Blocks/DiscodataComponents/Widgets/style.css +++ b/src/components/manage/Blocks/DiscodataComponents/Widgets/style.css @@ -21,13 +21,3 @@ ul.query-parameters-list li button { border: none; color: #007EB1; } - -.color-picker-widget { - display: flex; - padding: 1em; - flex-flow: column; -} - -.color-picker-label { - margin-bottom: 1em; -} diff --git a/src/components/manage/Blocks/DiscodataComponents/schema.jsx b/src/components/manage/Blocks/DiscodataComponents/schema.jsx index 4bf5b15e..89e02f14 100644 --- a/src/components/manage/Blocks/DiscodataComponents/schema.jsx +++ b/src/components/manage/Blocks/DiscodataComponents/schema.jsx @@ -70,6 +70,35 @@ export const getQueryParametersSchema = (props) => { }; }; +export const getQueryParametersToSetSchema = (props) => { + return { + title: 'Query parameter', + fieldsets: [ + { + id: 'default', + title: 'Default', + fields: ['selectorOptionKey', 'queryParameterToSet'], + }, + ], + properties: { + selectorOptionKey: { + title: 'Selector option key', + type: 'array', + choices: [ + ['key', 'Key'], + ['value', 'Value'], + ['text', 'Text'], + ], + }, + queryParameterToSet: { + title: 'Query parameter to set', + type: 'string', + }, + }, + required: ['selectorOptionKey', 'queryParameterToSet'], + }; +}; + export const makeSchema = (props) => { const resourcesKeys = Object.keys({ ...props.discodata_resources.data }); const queriesKeys = Object.keys({ ...props.discodata_query.search }); @@ -83,10 +112,6 @@ export const makeSchema = (props) => { : props.discodata_resources.data?.[resource.package] || null; }); - const collectionsKeys = - Object.keys(resources).filter((key) => { - return isArray(resources[key]); - }) || []; const objectsKeys = Object.keys(resources).filter((key) => { return !isArray(resources[key]) && isObject(resources[key]); @@ -121,8 +146,6 @@ export const makeSchema = (props) => { }; export const makeTextSchema = (props) => { - const resources = props.data.resources; - const subResources = props.data.subResources; const schemaTitle = 'Text'; const schemaFieldsets = [ { @@ -146,7 +169,7 @@ export const makeTextSchema = (props) => { id: 'link', title: 'Link', fields: props.data.isLink - ? ['isLink', 'internalLink', 'linkTarget', 'link'] + ? ['isLink', 'internalLink', 'linkTarget', 'link', 'triggerOn'] : ['isLink'], }, { @@ -212,7 +235,7 @@ export const makeTextSchema = (props) => { title: 'Target', type: 'array', choices: [ - ['self', 'Same window'], + ['_self', 'Same window'], ['_blank', 'New window'], ], }, @@ -251,9 +274,79 @@ export const makeTextSchema = (props) => { }; export const makeSelectSchema = (props) => { - const resources = props.data.resources; - const subResources = props.data.subResources; - const schemaTitle = 'Select'; + const discodataValues = props.discodataValues; + const discodataKeys = + discodataValues && + discodataValues[0] && + !isArray(discodataValues[0]) && + isObject(discodataValues[0]) + ? makeChoices(Object.keys(discodataValues[0])) + : []; + const schemaTitle = 'Text'; + const schemaFieldsets = [ + { + id: 'settings', + title: 'Settings', + fields: [ + 'key', + 'value', + 'text', + 'queryParametersToSet', + 'placeholder', + 'className', + ], + }, + ]; + const schemaProperties = { + key: { + title: 'Selector key', + type: 'array', + choices: discodataKeys || [], + }, + value: { + title: 'Selector value', + type: 'array', + choices: discodataKeys || [], + }, + text: { + title: 'Selector text', + type: 'array', + choices: discodataKeys || [], + }, + queryParametersToSet: { + title: 'Query parameters', + widget: 'object_list', + schema: getQueryParametersToSetSchema(props), + }, + placeholder: { + title: 'Placeholder', + widget: 'text', + }, + className: { + title: 'Class name', + widget: 'text', + }, + }; + const schemaRequired = []; + return makeSchema({ + ...props, + schemaTitle, + schemaFieldsets, + schemaProperties, + schemaRequired, + }); +}; + +export const makeListSchema = (props) => { + const discodataValues = props.discodataValues; + const discodataKeys = + discodataValues && + discodataValues[0] && + !isArray(discodataValues[0]) && + isObject(discodataValues[0]) + ? makeChoices(Object.keys(discodataValues[0])) + : []; + const schemaTitle = 'Text'; const schemaFieldsets = [ { id: 'additional', @@ -264,12 +357,12 @@ export const makeSelectSchema = (props) => { id: 'settings', title: 'Settings', fields: [ - 'visible', - 'order', - 'leftText', - 'rightText', - 'color', - 'component', + 'key', + 'value', + 'text', + 'queryParametersToSet', + 'placeholder', + 'className', ], }, ]; @@ -279,44 +372,33 @@ export const makeSelectSchema = (props) => { widget: 'query_param_list', schema: getQueryParametersSchema(props), }, - visible: { - title: 'Visible', + key: { + title: 'Selector key', type: 'array', - choices: [ - ['always', 'Always'], - ['hasQuery', 'Has query'], - ['hasDiscodata', 'hasDiscodata'], - ], + choices: discodataKeys || [], }, - order: { - title: 'Text order', + value: { + title: 'Selector value', type: 'array', - choices: [ - ['qd', 'Query - Discodata'], - ['dq', 'Discodata - Query'], - ], + choices: discodataKeys || [], }, - leftText: { - title: 'Left text', - widget: 'textarea', + text: { + title: 'Selector text', + type: 'array', + choices: discodataKeys || [], }, - rightText: { - title: 'Right text', - widget: 'textarea', + queryParametersToSet: { + title: 'Query parameters', + widget: 'object_list', + schema: getQueryParametersToSetSchema(props), }, - color: { - title: 'Color', - widget: 'color_picker', + placeholder: { + title: 'Placeholder', + widget: 'text', }, - component: { - title: 'Component type', - type: 'array', - choices: [ - ['h1', 'H1'], - ['h2', 'H2'], - ['h3', 'H3'], - ['p', 'Paragraph'], - ], + className: { + title: 'Class name', + widget: 'text', }, }; const schemaRequired = []; diff --git a/src/components/manage/Blocks/DiscodataComponentsBlock/Edit.jsx b/src/components/manage/Blocks/DiscodataComponentsBlock/Edit.jsx new file mode 100644 index 00000000..f7a103e0 --- /dev/null +++ b/src/components/manage/Blocks/DiscodataComponentsBlock/Edit.jsx @@ -0,0 +1,454 @@ +import React, { useState, useEffect } from 'react'; +import { connect } from 'react-redux'; +import { compose } from 'redux'; +import qs from 'query-string'; +import View from './View'; +import DiscodataSqlBuilderEdit from 'volto-datablocks/DiscodataSqlBuilder/Edit'; + +const makeChoices = (keys) => keys && keys.map((k) => [k, k]); + +const classNames = [ + 'flex', + 'grid', + 'display-block', + 'responsive', + 'flex-row', + 'flex-column', + 'align-center', + 'space-between', + 'light-blue', + 'bold', + 'lighter', + 'info', + 'mt-0', + 'mt-1', + 'mt-2', + 'mt-3', + 'mb-0', + 'mb-1', + 'mb-2', + 'mb-3', + 'ml-0', + 'ml-1', + 'ml-2', + 'ml-3', + 'mr-0', + 'mr-1', + 'mr-2', + 'mr-3', + 'w-40', + 'w-50', + 'w-60', + 'w-70', + 'w-80', + 'w-90', + 'w-100', + 'pa-0', + 'pa-1', + 'pa-2', + 'pa-3', + 'float-left', + 'float-right', + 'clear-fix', + 'list-style-none', +]; + +const getSchema = (props) => { + const { query } = props; + const { search } = props.discodata_query; + const { data } = props.discodata_resources; + const globalQuery = { ...query, ...search }; + /* ===================== */ + const source_discodata_keys = props.data.source_discodata_keys?.value + ? JSON.parse(props.data.source_discodata_keys?.value).properties + : {}; + const components = props.data.components?.value + ? JSON.parse(props.data.components?.value).properties + : {}; + /* ===================== */ + const resourcePackageKey = props.data.resource_package_key?.value; + const key = props.data.key?.value; + const source = props.data.source?.value; + const source_query_param = props.data.source_query_param?.value; + const selectedResource = + resourcePackageKey && !key + ? data[resourcePackageKey] + : resourcePackageKey && key + ? data[resourcePackageKey]?.[globalQuery[key]] + : data; + let sourceData; + if ( + selectedResource && + source && + Object.keys(source).length && + selectedResource[source] && + globalQuery[source_query_param] && + selectedResource[source][globalQuery[source_query_param]] + ) { + sourceData = selectedResource[source][globalQuery[source_query_param]]; + } else if (selectedResource && source && selectedResource[source]) { + sourceData = selectedResource[source]; + } + return { + resource_package_key: { + title: 'Resource package key', + type: 'array', + choices: data ? makeChoices(Object.keys(data)) : [], + }, + key: { + title: 'Key', + type: 'array', + choices: globalQuery ? makeChoices(Object.keys(globalQuery)) : [], + }, + source: { + title: 'Source', + type: 'array', + choices: selectedResource + ? makeChoices(Object.keys(selectedResource)) + : [], + }, + source_query_param: { + title: 'Source query selector', + type: 'array', + choices: globalQuery ? makeChoices(Object.keys(globalQuery)) : [], + }, + source_discodata_keys: { + title: 'Source discodata keys', + type: 'schema', + fieldSetTitle: 'Source discodata keys metadata', + fieldSetId: 'source_discodata_keys_metadata', + fieldSetSchema: { + fieldsets: [ + { + id: 'default', + title: 'title', + fields: ['title', 'id', 'key'], + }, + ], + properties: { + title: { + title: 'Title', + type: 'text', + }, + id: { + title: 'Id', + type: 'text', + }, + key: { + title: 'Key', + type: 'array', + choices: + sourceData && Array.isArray(sourceData) + ? makeChoices(Object.keys(sourceData[0])) + : sourceData + ? makeChoices(Object.keys(sourceData)) + : [], + }, + }, + required: ['id', 'title', 'key'], + }, + editFieldset: false, + deleteFieldset: false, + }, + components: { + title: 'Components', + type: 'schema', + fieldSetTitle: 'Components metadata', + fieldSetId: 'components_metadata', + fieldSetSchema: { + fieldsets: [ + { + id: 'default', + title: 'title', + fields: [ + 'title', + 'id', + 'type', + 'static', + 'hasParent', + 'staticValue', + 'value', + 'urlValue', + 'queryToSet', + 'page', + 'maxElements', + 'placeholder', + 'valueClassName', + 'valueLabels', + 'valueLabelsClassName', + 'gridColumns', + 'className', + 'listItemClassName', + 'wrapperClassName', + 'parent', + ], + }, + ], + properties: { + title: { + type: 'text', + title: 'Title', + }, + id: { + type: 'text', + title: 'Id', + }, + type: { + type: 'select', + title: 'Component type', + choices: [ + ['container', 'Container'], + ['hr', 'Horizontal line'], + ['header', 'Header'], + ['select', 'Select'], + ['linkHeader', 'Header link'], + ['list', 'List'], + ['linkList', 'List Link'], + ['paragraph', 'Paragraph'], + ['metadataGrid', 'Metadata grid'], + ['table', 'Table'], + ['banner', 'Banner'], + ['eprtrCountrySelector', 'eprtrCountrySelector'], + ['eprtrCountryGroupSelector', 'eprtrCountryGroupSelector'], + ['eprtrBatDerogations', 'eprtrBatDerogations'], + ['eprtrBatConclusions', 'eprtrBatConclusions'], + ], + }, + static: { + type: 'boolean', + title: 'Only static data', + defaultValue: false, + disabled: (formData) => + [ + 'container', + 'hr', + 'metadataGrid', + 'table', + 'banner', + 'list', + 'linkList', + 'select', + ].includes(formData.type), + }, + hasParent: { + type: 'boolean', + title: 'Has parent', + defaultValue: false, + }, + staticValue: { + type: 'text', + title: 'Static value', + disabled: (formData) => + [ + 'container', + 'hr', + 'metadataGrid', + 'table', + 'banner', + 'list', + 'linkList', + 'select', + ].includes(formData.type), + }, + value: { + type: (formData) => { + if (['metadataGrid', 'table', 'banner'].includes(formData.type)) + return 'array'; + return 'select'; + }, + title: (formData) => { + if (['metadataGrid', 'table', 'banner'].includes(formData.type)) + return 'Metadata fields'; + return 'Metadata field'; + }, + items: (formData) => { + if (['metadataGrid', 'table', 'banner'].includes(formData.type)) { + return { + choices: selectedResource + ? makeChoices(Object.keys(selectedResource)) + : [], + }; + } + return undefined; + }, + choices: (formData) => { + if (!['metadataGrid', 'table', 'banner'].includes(formData.type)) + return selectedResource + ? makeChoices(Object.keys(selectedResource)) + : []; + return undefined; + }, + description: (formData) => { + if (['metadataGrid', 'table', 'banner'].includes(formData.type)) + return "If you want to add multiple columns for the same metadata field use '@{unique id}' suffix"; + return undefined; + }, + disabled: (formData) => + formData.static || ['container', 'hr'].includes(formData.type), + }, + urlValue: { + type: (formData) => { + if (['linkList', 'select'].includes(formData.type)) return 'text'; + return 'select'; + }, + title: (formData) => { + if (['linkList', 'select'].includes(formData.type)) + return 'Query to use'; + return 'URL metadata field'; + }, + choices: (formData) => { + if (['linkList', 'select'].includes(formData.type)) + return undefined; + return selectedResource + ? makeChoices(Object.keys(selectedResource)) + : []; + }, + disabled: (formData) => + !['linkHeader', 'linkList', 'select'].includes(formData.type), + }, + queryToSet: { + type: 'text', + title: 'Query to set', + disabled: (formData) => + !['linkHeader', 'linkList', 'select'].includes(formData.type), + }, + page: { + type: 'text', + title: 'Go to page', + disabled: (formData) => + !['linkHeader', 'linkList', 'select'].includes(formData.type), + }, + maxElements: { + type: 'text', + title: 'Max elements', + disabled: (formData) => !['linkList'].includes(formData.type), + }, + placeholder: { + type: 'text', + title: 'Placeholder', + disabled: (formData) => !['select'].includes(formData.type), + }, + valueClassName: { + type: 'array', + title: 'Class names for metadata fields', + items: { + choices: makeChoices(classNames), + }, + disabled: (formData) => + !['metadataGrid', 'table', 'banner'].includes(formData.type), + }, + valueLabels: { + type: 'array', + title: 'Labels for metadata fields', + disabled: (formData) => + !['metadataGrid', 'table', 'banner'].includes(formData.type), + }, + valueLabelsClassName: { + type: 'array', + title: 'Class names for labels of metadata fields', + items: { + choices: makeChoices(classNames), + }, + disabled: (formData) => + !['metadataGrid', 'table', 'banner'].includes(formData.type), + }, + gridColumns: { + type: 'text', + title: 'Grid columns', + disabled: (formData) => !['metadataGrid'].includes(formData.type), + }, + className: { + type: 'array', + title: 'Class name', + items: { + choices: makeChoices(classNames), + }, + disabled: (formData) => ['container'].includes(formData.type), + }, + listItemClassName: { + type: 'array', + title: 'List item class name', + items: { + choices: makeChoices(classNames), + }, + disabled: (formData) => + !['list', 'linkList'].includes(formData.type), + }, + wrapperClassName: { + type: 'array', + title: 'Wrapper class name', + items: { + choices: makeChoices(classNames), + }, + disabled: (formData) => + ['metadataGrid', 'table', 'banner'].includes(formData.type), + }, + parent: { + type: 'select', + title: 'Parent', + disabled: (formData) => !formData.hasParent, + choices: (formData) => { + const components_filtered = { ...components }; + components_filtered[formData.id] && + delete components_filtered[formData.id]; + return makeChoices(Object.keys(components_filtered)); + }, + }, + }, + required: (formData) => { + const requiredFields = ['title', 'id', 'type']; + if (['metadataGrid', 'table', 'banner'].includes(formData.type)) + requiredFields.push('valueLabels'); + if (['linkHeader', 'linkList', 'select'].includes(formData.type)) + requiredFields.push('urlValue', 'queryToSet'); + if (!formData.static && !['container', 'hr'].includes(formData.type)) + requiredFields.push('value'); + return requiredFields; + }, + }, + editFieldset: false, + deleteFieldset: false, + }, + requiredQueries: { + title: 'Required queries', + type: 'array', + items: { + choices: [], + }, + }, + }; +}; + +const Edit = (props) => { + const [state, setState] = useState({ + schema: getSchema({ ...props }), + }); + useEffect(() => { + setState({ + ...state, + schema: getSchema({ + ...props, + }), + }); + /* eslint-disable-next-line */ + }, [props.data, props.discodata_resources, props.discodata_query.search]) + return ( + +

Discodata components - edit mode

+ +
+ ); +}; + +export default compose( + connect((state, props) => ({ + query: qs.parse(state.router.location.search), + pathname: state.router.location.pathname, + discodata_resources: state.discodata_resources, + discodata_query: state.discodata_query, + })), +)(Edit); diff --git a/src/components/manage/Blocks/DiscodataComponentsBlock/View.jsx b/src/components/manage/Blocks/DiscodataComponentsBlock/View.jsx new file mode 100644 index 00000000..40088df9 --- /dev/null +++ b/src/components/manage/Blocks/DiscodataComponentsBlock/View.jsx @@ -0,0 +1,705 @@ +import React, { useState, useEffect } from 'react'; +import { useHistory } from 'react-router-dom'; +import _uniqueId from 'lodash/uniqueId'; +import { connect } from 'react-redux'; +import { compose } from 'redux'; +import moment from 'moment'; +import { arrayToTree } from 'performant-array-to-tree'; +import qs from 'query-string'; +import { Table, Dropdown, List, Header } from 'semantic-ui-react'; +import './style.css'; +import DiscodataSqlBuilderView from 'volto-datablocks/DiscodataSqlBuilder/View'; +import { setQueryParam, deleteQueryParam } from 'volto-datablocks/actions'; +import cx from 'classnames'; +import Icon from '@plone/volto/components/theme/Icon/Icon'; +import infoSVG from '@plone/volto/icons/info.svg'; +import blankSVG from '@plone/volto/icons/blank.svg'; + +const renderComponents = { + wrapper: function (tree, item, props) { + if (!tree.children || tree.children.length === 0) { + return ( + + {this[tree.data.type]({ + component: tree.data, + item, + ...props, + })} + + ); + } else if (tree.children.length > 0) { + return ( +
+ {this[tree.data.type]({ + component: tree.data, + item, + ...props, + })} + {tree.children.map((child) => { + if (child.children?.length > 0) { + return this.wrapper(child, item, props); + } + return ( + renderComponents[child.data.type] && ( + + {this[child.data.type]({ + component: child.data, + item, + ...props, + })} + + ) + ); + })} +
+ ); + } + }, + container: (props) => { + return ''; + }, + hr: (props) => { + return ( +
+ ); + }, + header: (props) => { + let value = props.item?.[props.component?.value]; + if (value && !isNaN(Date.parse(value)) && value.length >= 10) { + value = moment(props.item[value]).format('DD MMM YYYY'); + } + const text = props.component?.static + ? props.component?.staticValue + : props.component?.staticValue + ? `${props.component?.staticValue} ${value}` + : value; + const view = ( +

{text}

+ ); + return text && props ? view : ''; + }, + linkHeader: (props) => { + let value = props.item?.[props.component?.value]; + if (value && !isNaN(Date.parse(value)) && value.length >= 10) { + value = moment(props.item[value]).format('DD MMM YYYY'); + } + const text = props.component?.static + ? props.component?.staticValue + : props.component?.staticValue + ? `${props.component?.staticValue} ${value}` + : value; + const view = ( + +

{text}

+
+ ); + return text && props ? view : ''; + }, + select: (props) => { + let value = props.item?.[props.component?.value]; + const options = + value && + Object.keys(value) + .filter((key) => key) + .map((key) => { + return { key: key, value: key, text: key }; + }); + const trigger = ( + + {props.globalQuery?.[props.component?.urlValue] || + props.component?.placeholder || + ''} + + ); + const view = ( + { + if (props.component?.urlValue) { + props.setQueryParam && + typeof props.setQueryParam === 'function' && + props.setQueryParam({ + queryParam: { + [props.component.urlValue]: data.value, + }, + }); + } + }} + className={props.component?.className?.join(' ') || ''} + options={options} + /> + ); + return view; + }, + paragraph: (props) => { + let value = props.item?.[props.component?.value]; + if (value && !isNaN(Date.parse(value)) && value.length >= 10) { + value = moment(props.item[value]).format('DD MMM YYYY'); + } + const text = props.component?.static + ? props.component?.staticValue + : props.component?.staticValue + ? `${props.component?.staticValue} ${value || '-'}` + : value || '-'; + const view = ( +

{text}

+ ); + return text && props ? view : ''; + }, + list: (props) => { + let value = props.item?.[props.component?.value]; + let items = []; + if (Array.isArray(value)) { + items = [...value]; + } else if (value && Object.keys(value).length) { + items = Object.keys(value); + } + const view = ( +
    + {items.map((value) => ( +
  • + {value} +
  • + ))} +
+ ); + return view; + }, + linkList: (props) => { + let value = props.item?.[props.component?.value]; + let items = []; + if (Array.isArray(value)) { + items = [...value]; + } else if (value && Object.keys(value).length) { + items = Object.keys(value); + } + const maxElements = parseInt(props.component.maxElements) || 0; + if (maxElements && props.globalQuery.listFilter) { + items = items + .sort( + (a, b) => + b[props.globalQuery.listFilter] - a[props.globalQuery.listFilter], + ) + .slice(0, maxElements); + } + const view = ( +
    + {items.map((value) => ( +
  1. + +
  2. + ))} +
+ ); + return view; + }, + banner: (props) => { + return props.item && props.component ? ( +
+ {props.component.value && + props.component.value?.map((metadataField, index) => { + const key = metadataField.split('@')[0]; + const labelsClassName = props.component.valueLabelsClassName?.[ + index + ]?.split('@')?.[0]; + const valueClassName = props.component.valueClassName?.[ + index + ]?.split('@')?.[0]; + return ( + props.item[key] && ( +
+ {props.component.valueLabels?.[index] && ( +

+ {props.component.valueLabels[index]} +

+ )} +

+ {!isNaN(Date.parse(props.item[key])) && + props.item[key].length >= 10 + ? moment(props.item[key]).format('DD MMM YYYY') + : props.item[key]} +

+
+ ) + ); + })} +
+ ) : ( + '' + ); + }, + metadataGrid: (props) => { + return props.item && props.component ? ( +
+ {props.component.value && + props.component.value?.map((metadataField, index) => { + const key = metadataField.split('@')[0]; + const labelsClassName = props.component.valueLabelsClassName?.[ + index + ]?.split('@')?.[0]; + const valueClassName = props.component.valueClassName?.[ + index + ]?.split('@')?.[0]; + return ( +
+ {props.component.valueLabels?.[index] && ( +

+ {props.component.valueLabels[index]} +

+ )} +

+ {!isNaN(Date.parse(props.item[key])) && + props.item[key].length >= 10 + ? moment(props.item[key]).format('DD MMM YYYY') + : props.item[key]} +

+
+ ); + })} +
+ ) : ( + '' + ); + }, + table: (props) => { + return props.item && props.component ? ( +
+ {props.component.value && ( + + {/* ==== TABLE HEADER ==== */} + + + {props.component.valueLabels && + props.component.valueLabels.map((header, index) => ( + + {header} + + ))} + + + {/* ==== TABLE BODY ==== */} + {/* + {props.component.value.map((row, index) => ( + + {Object.keys(row).map(cell => ( + {row[cell]} + ))} + + ))} + */} +
+ )} +
+ ) : ( + '' + ); + }, + eprtrCountrySelector: (props) => { + const items = + props.item?.[props.component.value]?.map((item) => { + return { + key: item.CountryCode, + value: item.CountryName, + text: item.CountryName, + }; + }) || []; + return ( +
+
Industrial pollution in
+
+ {items && ( + { + props.setQueryParam({ + queryParam: { + countryCode: data.options.filter((opt) => { + return opt.value === data.value; + })[0]?.key, + countryName: data.value, + }, + }); + }} + placeholder={'Country'} + options={items} + value={props.globalQuery.countryName} + /> + )} +
+
+ ); + }, + eprtrCountryGroupSelector: (props) => { + const items = + props.item?.[props.component.value] + ?.filter((item) => item.countryGroupId) + .map((item) => { + return { + key: item.countryGroupId, + value: item.countryGroupId, + text: item.countryGroupId, + }; + }) || []; + const pollutionType = [ + { + key: 'airPollutionPerCapita', + value: 'airPollutionPerCapita', + text: 'Air', + }, + { + key: 'waterPollutionPerCapita', + value: 'waterPollutionPerCapita', + text: 'Water', + }, + ]; + return ( +
+
Industrial pollution in
+
+ {items && ( + { + props.setQueryParam({ + queryParam: { + countryGroupId: data.options.filter((opt) => { + return opt.value === data.value; + })[0]?.key, + }, + }); + }} + placeholder={'Country group'} + options={items} + value={props.globalQuery.countryGroupId} + /> + )} + {pollutionType && ( + { + props.setQueryParam({ + queryParam: { + listFilter: data.options.filter((opt) => { + return opt.value === data.value; + })[0]?.key, + }, + }); + }} + placeholder={'Pollution by'} + options={pollutionType} + value={props.globalQuery.listFilter} + /> + )} +
+
+ ); + }, + eprtrReprotingYears: (props) => { + // const items = + // props.item?.[props.component.value]?.map((item) => { + // return { + // key: item.CountryCode, + // value: item.CountryName, + // text: item.CountryName, + // }; + // }) || []; + return

OLA

; + // return ( + //
+ //
Industrial pollution in
+ //
+ // {items && ( + // { + // props.setQueryParam({ + // queryParam: { + // countryCode: data.options.filter((opt) => { + // return opt.value === data.value; + // })[0]?.key, + // countryName: data.value, + // }, + // }); + // }} + // placeholder={'Country'} + // options={items} + // value={props.globalQuery.countryName} + // /> + // )} + //
+ //
+ // ); + }, + eprtrBatDerogations: (props) => { + return ( +
+

BAT DEROGATIONS

+
+ ); + }, + eprtrBatConclusions: (props) => { + const batConclusions = props.item?.[props.component.value] || {}; + return Object.keys(batConclusions).length ? ( + <> +
+

BAT Conclusions

+ +
+
+ {Object.entries(batConclusions).map(([key, conclusion]) => ( +
+ +
+
+

Status

+

{conclusion[0].conclusionStatus}

+
+ {/*
+

Status Modified

+

{conclusion[0].Status}

+
*/} +
+ {props.show.aels && ( +
+

BAT AELs

+ {conclusion.map((ael, index) => ( +
+ +
+
+

Status

+

{ael.BATAELStatus}

+
+
+

Accepted date

+

{ael.BATAELAcceptedDate}

+
+
+

Description

+

{ael.Description}

+
+
+
+ ))} +
+ )} +
+ ))} +
+
+ +
+
+ + ) : ( + '' + ); + }, +}; + +const View = (props) => { + // providerUrl + const [state, setState] = useState({ + selectedResource: {}, + }); + const [show, setShow] = useState({}); + const [canRender, setCanRender] = useState(true); + const history = useHistory(); + const { query } = props; + const { search } = props.discodata_query; + const { data } = props.discodata_resources; + const globalQuery = { ...query, ...search }; + const source_discodata_keys = props.data.source_discodata_keys?.value + ? JSON.parse(props.data.source_discodata_keys?.value).properties + : {}; + const resourcePackageKey = props.data.resource_package_key?.value; + const key = props.data.key?.value; + const source = props.data.source?.value; + const source_query_param = props.data.source_query_param?.value; + const requiredQueries = props.data.requiredQueries?.value || []; + useEffect(() => { + const requiredQueriesLength = requiredQueries?.length + ? requiredQueries.filter((query) => props.discodata_query.search[query]) + .length + : true; + if (!requiredQueriesLength && canRender) { + setCanRender(false); + } else if (requiredQueriesLength && !canRender) { + setCanRender(true); + } + /* eslint-disable-next-line */ + }, [props.discodata_query.search]) + useEffect(() => { + const selectedResource = + resourcePackageKey && !key + ? data[resourcePackageKey] + : resourcePackageKey && key + ? data[resourcePackageKey]?.[globalQuery[key]] + : data; + let sourceData; + let selectedSourceData; + if ( + selectedResource && + source && + Object.keys(source).length && + selectedResource[source] && + globalQuery[source_query_param] && + selectedResource[source][globalQuery[source_query_param]] + ) { + sourceData = selectedResource[source][globalQuery[source_query_param]]; + } else if (selectedResource && source && selectedResource[source]) { + sourceData = selectedResource[source]; + } + if (sourceData && Array.isArray(sourceData) && source_discodata_keys) { + selectedSourceData = sourceData.filter((item) => { + let ok = true; + Object.entries(source_discodata_keys).forEach( + ([selectorKey, selector]) => { + if ( + globalQuery[selectorKey] && + item[selector.key] !== globalQuery[selectorKey] + ) { + ok = false; + } else if (!globalQuery[selectorKey]) { + ok = false; + } + }, + ); + return ok; + })[0]; + } + setState({ + ...state, + selectedResource: selectedSourceData + ? selectedSourceData + : selectedResource, + }); + /* eslint-disable-next-line */ + }, [props.data, props.discodata_resources, props.discodata_query.search]) + // Render components + const componentsSchema = + props.data?.components?.value && JSON.parse(props.data?.components?.value); + const components = {}; + componentsSchema?.fieldsets?.[0]?.fields && + componentsSchema.fieldsets[0].fields.forEach((component) => { + components[component] = { + ...componentsSchema.properties[component], + }; + }); + const componentsArray = + components && + Object.keys(components).map((key, index) => { + return { + ...components[key], + id: key, + parentId: components[key].parent || null, + }; + }); + let root = arrayToTree(componentsArray); + return ( + +
+
+ {(state.selectedResource && + canRender && + root.map((tree) => + renderComponents.wrapper(tree, state.selectedResource, { + globalQuery, + setQueryParam: props.setQueryParam, + history, + show, + setShow, + }), + )) || + (props.data.mode === 'edit' ?

Add components

: '')} +
+
+
+ ); +}; + +export default compose( + connect( + (state, props) => ({ + query: qs.parse(state.router.location.search), + pathname: state.router.location.pathname, + discodata_resources: state.discodata_resources, + discodata_query: state.discodata_query, + }), + { + setQueryParam, + deleteQueryParam, + }, + ), +)(View); diff --git a/src/components/manage/Blocks/DiscodataComponentsBlock/style.css b/src/components/manage/Blocks/DiscodataComponentsBlock/style.css new file mode 100644 index 00000000..e17e2ea7 --- /dev/null +++ b/src/components/manage/Blocks/DiscodataComponentsBlock/style.css @@ -0,0 +1,474 @@ +/* DISPLAY */ +.flex { + display: flex; +} + +.grid { + display: grid; + grid-gap: 20px; +} + +.display-block { + display: block; +} + +/* GRID LAYOUT */ +.grid-cl-2 { + grid-template-columns: repeat(2, 1fr); +} + +.grid-cl-3 { + grid-template-columns: repeat(3, 1fr); +} + +.grid-cl-4 { + grid-template-columns: repeat(4, 1fr); +} + +.grid-cl-5 { + grid-template-columns: repeat(5, 1fr); +} + +.grid-cl-6 { + grid-template-columns: repeat(6, 1fr); +} + +.grid-cl-7 { + grid-template-columns: repeat(7, 1fr); +} + +/* FLEX FLOW */ +div.flex-row { + flex-flow: row; +} + +div.flex-column { + flex-flow: column; +} + +/* ALIGN ITEMS */ +div.align-center { + align-items: center; +} +div.align-stretch { + align-items: stretch; +} +/* JUSTIFY CONTENT */ +div.space-between { + justify-content: space-between; +} + +div.banner { + background: #989898; + color: #000; + padding: 0 1rem; + border-radius: 10px; +} + +div.banner p { + color: #fff; +} + +div.banner.light-blue { + background: #4296B2; + color: #fff; +} + +.light-blue { + color: #4296B2; +} + +.red { + color: #EE7A72; +} + +div.flex-item { + margin: 1rem; +} + +/* PARAGRAPH */ +p.bold { + font-weight: bold; +} + +p.lighter { + font-weight: 300; +} + +p.info { + color: #989898; + font-weight: 300; + font-size: 14px; +} + +p.dark { + color: #333333; +} + +/* MARGINS */ +.mt-0, +h1.mt-0:first-child { + margin-top: 0; +} + +.mt-1, +h1.mt-1:first-child { + margin-top: 1rem; +} + +.mt-2, +h1.mt-2:first-child { + margin-top: 2rem; +} + +.mt-3, +h1.mt-3:first-child { + margin-top: 3rem; +} + +.mb-0 { + margin-bottom: 0; +} + +.mb-1 { + margin-bottom: 1rem; +} + +.mb-2 { + margin-bottom: 2rem; +} + +.mb-3 { + margin-bottom: 3rem; +} + +.ml-0 { + margin-left: 0; +} + +.ml-1 { + margin-left: 1rem; +} + +.ml-2 { + margin-left: 2rem; +} + +.mr-0 { + margin-right: 0; +} + +.mr-1 { + margin-right: 1rem; +} + +.mr-2 { + margin-right: 2rem; +} + +.ma-0 { + margin: 0; +} +.ma-1 { + margin: 1rem; +} +.ma-2 { + margin: 2rem; +} +.ma-3 { + margin: 3rem; +} + +/* WIDTHS */ +.w-40 { + width: 40%; +} + +.w-50 { + width: 50%; +} + +.w-60 { + width: 60%; +} + +.w-70 { + width: 70%; +} + +.w-80 { + width: 80%; +} + +.w-90 { + width: 90%; +} + +.w-100 { + width: 100%; +} + +/* PADDINGS */ +.pa-0 { + padding: 0; +} +.pa-1 { + padding: 1rem; +} +.pa-2 { + padding: 2rem; +} +.pa-3 { + padding: 3rem; +} + +.list-style-none { + list-style: none; +} + +.bat-container { + border: 1px solid #606060; + padding: 16px; + border-radius: 10px; + box-sizing: border-box; +} + +.bat-container .bat-title { + font-size: 18px; + line-height: 23px; + color: #EC776A; + font-weight: 300; + text-decoration: underline; +} + +.bat-container div.hr { + height: 1px; + background-color: #606060; +} + +.bat-container a, +.bat-container a:hover { + text-decoration: underline; +} + +.bat-aels-title { + font-weight: 600; + font-size: 18px; + line-height: 23px; + color: #333333; +} + +.bat-action button { + cursor: pointer; + background: transparent; + border: none; + color: #EC776A; + text-decoration: underline; +} + +.bat-action button:focus { + outline: none; +} + +.float-left { + float: left; +} + +.clear-fix:after, +.clear-fix:before { + content: ""; + clear: both; + display: table; +} + +.float-right { + float: right; +} + +.border-1 { + border: 1px solid #000; + +} + +.border-gray { + border-color: #606060; +} + +.border-radius-10 { + border-radius: 10px; +} + +div.hr { + height: 1px; + width: 100%; + background-color: #000; +} + +.bg-gray, +div.hr.bg-gray { + background-color: gray; +} + +.underline { + text-decoration: underline; +} + +.text-align-center { + text-align: center; +} + +.text-align-left { + text-align: left; +} + +.text-align-right { + text-align: right; +} + +a.small h1 { + font-size: 16px; +} + + +@media(max-width: 768px) { + div.responsive.flex { + flex-flow: column; + } + + div.responsive.flex>* { + width: 100%; + margin-bottom: 26px; + } + + div.responsive.align-center { + align-items: normal + } + + .responsive.grid.grid-cl-3, + .responsive.grid.grid-cl-4, + .responsive.grid.grid-cl-5, + .responsive.grid.grid-cl-6, + .responsive.grid.grid-cl-7 { + grid-template-columns: repeat(2, 1fr); + } + + .banner.float-right, + .banner.float-left { + float: none; + width: 100%; + margin-bottom: 2rem; + } +} + +.eprtr-box { + background-color: #4296B3; + border-radius: 10px; + padding: 1em 2em; + width: 600px; +} + +.white, +.ui.selection.dropdown.white { + color: #fff; +} + +.transparent, +.ui.selection.dropdown.transparent { + background-color: transparent; +} + +.border-0, +.ui.selection.dropdown.border-0 { + border: none; + border-bottom: none; + border-top: none; + border-left: none; + border-right: none; +} + +.dropdown-padding { + padding: 0.4em 1.1em 0.4em 0;; +} + +.min-height-auto, +.ui.selection.dropdown.min-height-auto { + min-height: auto; +} + +.ui.dropdown { + display: flex; + align-items: center; +} + +.ui.dropdown > .dropdown.icon { + font-size: 2em; + margin-left: 5px; +} + +.link-list { + color: #EC776A; + padding: 0; + list-style: none; +} + +.link-list li { + counter-increment: link-list-counter; +} + +.link-list li:before { + content: counter(link-list-counter)"."; + color: #EC776A; + font-size: 1.5rem; +} + +.link-list li button { + background: transparent; + border: none; + color: #EC776A; +} + +.link-list li button:focus { + outline: none; +} + +.eprtrSelection h1.ui.header { + font-weight: bold; + font-size: 36px; + line-height: 45px; + color: #EC776A; + margin-bottom: 0; +} + +.eprtrSelection .ui.selection.dropdown { + width: fit-content; + color: #EC776A; + padding: 0; + border-bottom: none; + min-width: unset; + margin-right: 40px; +} + +.eprtrSelection .ui.selection.dropdown > .default.text, +.eprtrSelection .ui.selection.dropdown > .text, +.eprtrSelection .ui.selection.visible.dropdown > .text:not(.default) { + color: #EC776A; + font-weight: bold; + font-size: 30px; + line-height: 39px; + padding: 10px; + padding-left: 0; +} + +.eprtrSelection .ui.selection.dropdown > .dropdown.icon { + font-size: 30px !important; + line-height: 39px; + position: relative; + padding: 10px 0; + margin: 0; + margin-left: 0 !important; + top: unset; + left: unset; + right: unset; + bottom: unset; +} + diff --git a/src/components/manage/Blocks/DiscodataOpenlayersMapBlock/Edit.jsx b/src/components/manage/Blocks/DiscodataOpenlayersMapBlock/Edit.jsx index 65ab96a6..87012dbb 100644 --- a/src/components/manage/Blocks/DiscodataOpenlayersMapBlock/Edit.jsx +++ b/src/components/manage/Blocks/DiscodataOpenlayersMapBlock/Edit.jsx @@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react'; import { connect } from 'react-redux'; import { compose } from 'redux'; import _uniqueId from 'lodash/uniqueId'; -import RenderFields from 'volto-addons/Widgets/RenderFields'; +import RenderFields from 'volto-datablocks/Utils/RenderFields'; import View from './View'; import { settings } from '~/config'; @@ -23,6 +23,11 @@ const getSchema = (props) => { title: 'Has sidebar', defaultValue: false, }, + hasRegionsFeatures: { + type: 'boolean', + title: 'Has regions features', + defaultValue: false, + }, filterSource: { type: 'array', title: 'Filter source', @@ -88,7 +93,7 @@ const Edit = (props) => { {...props} title="Discodata openlayers map" /> - + ); }; diff --git a/src/components/manage/Blocks/DiscodataOpenlayersMapBlock/View.jsx b/src/components/manage/Blocks/DiscodataOpenlayersMapBlock/View.jsx index 4c9a9fdb..a880c444 100644 --- a/src/components/manage/Blocks/DiscodataOpenlayersMapBlock/View.jsx +++ b/src/components/manage/Blocks/DiscodataOpenlayersMapBlock/View.jsx @@ -9,6 +9,7 @@ import qs from 'query-string'; import axios from 'axios'; import jsonp from 'jsonp'; import { settings } from '~/config'; +import { isArray, isObject } from 'lodash'; // VOLTO import { Icon as VoltoIcon } from '@plone/volto/components'; // VOLTO-DATABLOCKS @@ -96,6 +97,7 @@ const OpenlayersMapView = (props) => { const draggable = !!props.data?.draggable?.value; const hasPopups = !!props.data?.hasPopups?.value; const hasSidebar = !!props.data?.hasSidebar?.value; + const hasRegionsFeatures = !!props.data?.hasRegionsFeatures?.value; const filterSource = props.data?.filterSource?.value || 'query_params'; const zoomSwitch = 6; const currentMapZoom = state.map?.element @@ -106,7 +108,7 @@ const OpenlayersMapView = (props) => { try { queryParams = JSON.parse(props.data?.query?.value)?.properties; Object.entries(queryParams).forEach(([key, value]) => { - queryParams[key].sql = `(${value.param} = ':options')`; + queryParams[key].sql = `(${value.param} LIKE '%:options%')`; queryParams[key].type = 'string'; }); } catch { @@ -114,6 +116,12 @@ const OpenlayersMapView = (props) => { } } + const searchQueryParams = isObject(queryParams) + ? Object.entries(queryParams).map(([key, value]) => { + return props.discodata_query.search[value.param] || null; + }) + : []; + if (mapRendered && !firstFilteringUpdate) { updateFilters(); setFirstFilteringUpdate(true); @@ -189,10 +197,14 @@ const OpenlayersMapView = (props) => { JSON.stringify(props.discodata_query.search.region), JSON.stringify(props.discodata_query.search.riverBasin), JSON.stringify(props.discodata_query.search.townVillage), - // JSON.stringify(props.discodata_query.search.pollutantGroup), + JSON.stringify(props.discodata_query.search.pollutantGroup), JSON.stringify(props.discodata_query.search.pollutant), JSON.stringify(props.discodata_query.search.reportingYear), JSON.stringify(props.discodata_query.search.batConclusionCode), + JSON.stringify(props.discodata_query.search.batConclustionYear), + JSON.stringify(props.discodata_query.search.permitType), + JSON.stringify(props.discodata_query.search.permitYear), + JSON.stringify(searchQueryParams), ]); useEffect(() => { @@ -230,50 +242,55 @@ const OpenlayersMapView = (props) => { sql: `(siteName LIKE ':options%')`, type: 'string', }, - // Industries + // Industries EEASubSector: { - sql: `(eea_activities IN (:options))`, + sql: `(eea_activities LIKE '%:options%')`, + type: 'multiple', }, - nuts_regions: { + // Country / Region / Town + nuts_latest: { sql: `(nuts_regions LIKE '%:options%')`, type: 'multiple', }, - // // Country - // siteCountry: { - // sql: `(countryCode IN (:options))`, - // }, - // //? Regions - // region: { - // sql: `(region IN (:options))`, - // }, - // //? River Basin - // riverBasin: { - // sql: `(riverBasinDistrict IN (:options))`, - // }, - // //? Town/Village - // townVillage: { - // sql: `(townVillage IN (:options))`, - // }, - // Pollutant groups - // pollutantGroup: { - // sql: `(pollutantGroup IN (:options))`, - // }, + // River basin district + riverBasin: { + sql: `(rbds LIKE '%:options%')`, + type: 'multiple', + }, + // Pollutants grouops + pollutantGroup: { + sql: `((air_groups LIKE '%:options%') OR (water_groups LIKE '%:options%'))`, + type: 'multiple', + }, // Pollutants pollutant: { - sql: `(pollutants IN (:options))`, + sql: `(pollutants LIKE '%:options%')`, + type: 'multiple', }, // Reporting year reportingYear: { sql: `(Site_reporting_year IN (:options))`, }, - //! Installation specifics - //? BAT conclusion + // BAT conclusion batConclusionCode: { - sql: `(batConclusion IN (:options))`, + sql: `(bat_conclusions LIKE '%:options%')`, + type: 'multiple', + }, + // BAT conclusion year + batConclustionYear: { + sql: `(bat_conclusion_years LIKE '%:options%')`, + type: 'multiple', + }, + // Permit type + permitType: { + sql: `(permit_types LIKE '%:options%')`, + type: 'multiple', + }, + // Permit year + permitYear: { + sql: `(permit_years LIKE '%:options%')`, + type: 'multiple', }, - //! BAT conclusion year - //! Permit type - //! Permit year }; } else if (filterSource === 'query_params') { sitesSourceQuery.whereStatements = { @@ -284,9 +301,7 @@ const OpenlayersMapView = (props) => { Object.entries(sitesSourceQuery.whereStatements).forEach(([id, where]) => { let options; - if (where.type === 'string') { - options = props.discodata_query.search[id]; - } else if (where.type === 'multiple') { + if (['string', 'multiple'].includes(where.type)) { options = props.discodata_query.search[id]; } else if (!props.discodata_query.search[id]) { options = null; @@ -294,18 +309,19 @@ const OpenlayersMapView = (props) => { options = splitBy(props.discodata_query.search[id], ','); } if (where.type === 'multiple') { + options = isArray(options) ? options?.filter((option) => option) : []; const conditions = []; if (options?.length) { options.forEach((option) => { let baseSql = where.sql; - option && conditions.push(baseSql.replace(':options', option)); + option && conditions.push(baseSql.replace(/:options/g, option)); }); - where.sql = conditions.join(' AND '); + where.sql = `(${conditions.join(' OR ')})`; } else { where.sql = null; } } else { - where.sql = options ? where.sql.replace(':options', options) : null; + where.sql = options ? where.sql.replace(/:options/g, options) : null; } if (!where.sql) delete sitesSourceQuery.whereStatements[id]; }); @@ -320,6 +336,7 @@ const OpenlayersMapView = (props) => { ) { let updateMapPosition = null; if ( + sitesSourceQuery.whereStatements.siteId?.sql || sitesSourceQuery.whereStatements.siteTerm?.sql || sitesSourceQuery.whereStatements.siteName?.sql ) { @@ -636,9 +653,11 @@ const OpenlayersMapView = (props) => { url, { param: - qs.stringify({ - where: stateRef.current.map.sitesSourceQuery.where, - }) + '&callback', + (stateRef.current.map.sitesSourceQuery.where + ? qs.stringify({ + where: stateRef.current.map.sitesSourceQuery.where, + }) + : '') + '&callback', }, (error, response) => { reqs--; @@ -668,43 +687,45 @@ const OpenlayersMapView = (props) => { ), }); // Make regions source layer - regionsSource = new VectorSource({ - loader: function (extent, resolution, projection) { - var url = - 'https://services.arcgis.com/LcQjj2sL7Txk9Lag/ArcGIS/rest/services/ly_IED_SiteClusters_WM/FeatureServer/0/query/?f=json' + - '&returnGeometry=true&spatialRel=esriSpatialRelIntersects&geometry=' + - encodeURIComponent( - '{"xmin":' + - extent[0] + - ',"ymin":' + - extent[1] + - ',"xmax":' + - extent[2] + - ',"ymax":' + - extent[3] + - ',"spatialReference":{"wkid":102100}}', - ) + - '&geometryType=esriGeometryEnvelope&inSR=102100&outFields=*' + - '&outSR=102100'; - jsonp(url, null, (error, response) => { - if (error) { - console.log(error.message); - } else { - var features = esrijsonFormat.readFeatures(response, { - featureProjection: projection, - }); - if (features.length > 0) { - regionsSource.addFeatures(features); + if (hasRegionsFeatures) { + regionsSource = new VectorSource({ + loader: function (extent, resolution, projection) { + var url = + 'https://services.arcgis.com/LcQjj2sL7Txk9Lag/ArcGIS/rest/services/ly_IED_SiteClusters_WM/FeatureServer/0/query/?f=json' + + '&returnGeometry=true&spatialRel=esriSpatialRelIntersects&geometry=' + + encodeURIComponent( + '{"xmin":' + + extent[0] + + ',"ymin":' + + extent[1] + + ',"xmax":' + + extent[2] + + ',"ymax":' + + extent[3] + + ',"spatialReference":{"wkid":102100}}', + ) + + '&geometryType=esriGeometryEnvelope&inSR=102100&outFields=*' + + '&outSR=102100'; + jsonp(url, null, (error, response) => { + if (error) { + console.log(error.message); + } else { + var features = esrijsonFormat.readFeatures(response, { + featureProjection: projection, + }); + if (features.length > 0) { + regionsSource.addFeatures(features); + } } - } - }); - }, - strategy: tile( - createXYZ({ - tileSize: 512, - }), - ), - }); + }); + }, + strategy: tile( + createXYZ({ + tileSize: 512, + }), + ), + }); + } /* ======== SOURCE LAYERS ======== */ // Sites source layer sitesSourceLayer = new VectorLayer({ @@ -720,18 +741,20 @@ const OpenlayersMapView = (props) => { title: 'ly_IED_SiteMap_WM', }); // Regions source layer - regionsSourceLayer = new VectorLayer({ - source: regionsSource, - style: new Style({ - image: new CircleStyle({ - radius: 3, - fill: new Fill({ color: '#4296B2' }), - stroke: new Stroke({ color: '#6A6A6A', width: 1 }), + if (hasRegionsFeatures) { + regionsSourceLayer = new VectorLayer({ + source: regionsSource, + style: new Style({ + image: new CircleStyle({ + radius: 3, + fill: new Fill({ color: '#4296B2' }), + stroke: new Stroke({ color: '#6A6A6A', width: 1 }), + }), }), - }), - visible: true, - title: 'ly_IED_SiteClusters_WM', - }); + visible: true, + title: 'ly_IED_SiteClusters_WM', + }); + } /* ======== APPEND TO THE MAP ======== */ // Append TileLayers to the map map.addLayer( @@ -748,14 +771,16 @@ const OpenlayersMapView = (props) => { // Append source layers to the map map.addLayer( new Group({ - layers: [sitesSourceLayer, regionsSourceLayer], + layers: hasRegionsFeatures + ? [sitesSourceLayer, regionsSourceLayer] + : [sitesSourceLayer], }), ); // Center by user location if (navigator.geolocation) { - navigator.geolocation.getCurrentPosition((position) => - centerPosition(map, position, 12), - ); + navigator.geolocation.getCurrentPosition((position) => { + return centerPosition(map, position, 12); + }); } // Events sitesSource.on('updateFilters', function (e) { @@ -795,13 +820,13 @@ const OpenlayersMapView = (props) => { if (currentZoom !== newZoom) { if (newZoom > zoomSwitch) { sitesSourceLayer.setVisible(true); - regionsSourceLayer.setVisible(false); + hasRegionsFeatures && regionsSourceLayer.setVisible(false); } else if (newZoom > 2) { sitesSourceLayer.setVisible(false); - regionsSourceLayer.setVisible(true); + hasRegionsFeatures && regionsSourceLayer.setVisible(true); } else { sitesSourceLayer.setVisible(false); - regionsSourceLayer.setVisible(false); + hasRegionsFeatures && regionsSourceLayer.setVisible(false); } currentZoom = newZoom; } @@ -848,6 +873,7 @@ const OpenlayersMapView = (props) => { return (
+ {props.mode === 'edit' ?

Openlayer map

: ''}