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) => (
+ -
+
+
+ ))}
+
+ );
+ 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 (
+
+
+
+ {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 (
+
+
+
+ {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 (
+ //
+ //
+ //
+ // {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
: ''}