diff --git a/src/components/manage/Blocks/ArticlesSparql/style.css b/src/components/manage/Blocks/ArticlesSparql/style.css index 5a1b64f..a992662 100644 --- a/src/components/manage/Blocks/ArticlesSparql/style.css +++ b/src/components/manage/Blocks/ArticlesSparql/style.css @@ -9,7 +9,10 @@ } .articles .article { - display: block; + display: flex; + flex-direction: column; + justify-content: center; + height: 100%; } .articles .articles-row.can-be-half:last-child { @@ -25,6 +28,9 @@ .articles .article.hero img { border-radius: 2em; + max-height: 200px; + width: auto; + height: auto; } .articles .article-header, diff --git a/src/components/manage/Blocks/DiscodataOpenlayersMapBlock/View.jsx b/src/components/manage/Blocks/DiscodataOpenlayersMapBlock/View.jsx index 9928903..0dd4d4c 100644 --- a/src/components/manage/Blocks/DiscodataOpenlayersMapBlock/View.jsx +++ b/src/components/manage/Blocks/DiscodataOpenlayersMapBlock/View.jsx @@ -942,6 +942,11 @@ const OpenlayersMapView = (props) => { } currentZoom = newZoom; } + props.setQueryParam({ + queryParam: { + extent: map.getView().calculateExtent(map.getSize()), + }, + }); } }); setState({ diff --git a/src/components/manage/Blocks/DiscodataTableBlock/Edit.jsx b/src/components/manage/Blocks/DiscodataTableBlock/Edit.jsx new file mode 100644 index 0000000..12f09fe --- /dev/null +++ b/src/components/manage/Blocks/DiscodataTableBlock/Edit.jsx @@ -0,0 +1,173 @@ +import React, { useEffect } from 'react'; +import { connect } from 'react-redux'; +import { compose } from 'redux'; +import { injectIntl } from 'react-intl'; +import View from './View'; +import DiscodataSqlBuilderEdit from 'volto-datablocks/DiscodataSqlBuilder/Edit'; + +const schema = { + itemsCountKey: { + title: 'Items count key', + type: 'text', + }, + hiddenRowTypes: { + title: 'Hidden row columns types', + type: 'array', + }, + metadata: { + title: 'Metadata', + type: 'schema', + fieldSetTitle: 'Table metadata', + fieldSetId: 'table-metadata', + fieldSetSchema: { + fieldsets: [ + { + id: 'default', + title: 'title', + fields: [ + 'tableType', + 'title', + 'id', + 'dataType', + 'show', + 'urlFieldId', + 'queriesToSet', + 'discodataQueriesKeys', + 'hiddenRowType', + 'className', + ], + }, + ], + properties: { + tableType: { + type: 'string', + title: 'Table type', + description: 'Used for data table fieldset', + choices: [ + ['Table header', 'Table header'], + ['Hidden row', 'Hidden row'], + ['Invisible', 'Invisible'], + ], + }, + id: { + type: 'string', + title: 'Field id', + description: 'Description', + }, + title: { + type: 'string', + title: 'Title', + }, + dataType: { + type: 'string', + title: 'Data type', + choices: [ + ['string', 'String'], + ['textarea', 'Text area'], + ['array', 'Array'], + ['object', 'Object'], + ['button', 'Button'], + ], + }, + show: { + type: 'string', + title: 'Display option', + choices: formData => { + if (['string', 'textarea'].includes(formData.dataType)) + return [['value', 'Value'], ['link_value', 'Link value']]; + if (['button'].includes(formData.dataType)) + return [['link_value', 'Link value']]; + if (['array', 'object'].includes(formData.dataType)) + return [ + ['length', 'Length'], + ['keys', 'Keys'], + ['link_length', 'Link length'], + ['link_keys', 'Link keys'], + ]; + return []; + }, + }, + urlFieldId: { + disabled: formData => + !['link_value', 'link_length', 'link_keys'].includes( + formData?.show, + ), + type: 'string', + title: 'Url field id or url', + description: 'Add only if "Display option" is set to "Link*"', + }, + queriesToSet: { + disabled: formData => + !['link_value', 'link_length', 'link_keys'].includes( + formData?.show, + ), + type: 'array', + title: 'Queries to set', + items: { + choices: [], + }, + }, + discodataQueriesKeys: { + disabled: formData => + !['link_value', 'link_length', 'link_keys'].includes( + formData?.show, + ), + type: 'array', + title: 'Discodata queries keys', + items: { + choices: [], + }, + }, + hiddenRowType: { + disabled: formData => formData?.tableType !== 'Hidden row', + type: 'string', + title: 'Hidden row type', + description: "Add only if 'Table type' is set to 'Hidden row'", + choices: [], + }, + className: { + disabled: formData => formData?.dataType !== 'button', + type: 'string', + title: 'Add a classname', + description: "Add only if 'button' data type is selected", + }, + }, + required: ['tableType', 'id', 'title', 'dataType', 'show'], + }, + editFieldset: false, + deleteFieldset: false, + }, +}; + +const Edit = React.forwardRef(props => { + useEffect(() => { + if (schema.metadata?.fieldSetSchema?.properties?.hiddenRowType) { + schema.metadata.fieldSetSchema.properties.hiddenRowType.choices = props + .data?.hiddenRowTypes?.value + ? props.data.hiddenRowTypes.value.map(type => [type, type]) + : []; + } + /* eslint-disable-next-line */ + }, [props.data?.hiddenRowTypes?.value]); + + if (__SERVER__) { + return
; + } + + return ( + + + + ); +}); + +export default compose( + injectIntl, + connect(state => ({ + content: state.content.data, + })), +)(Edit); diff --git a/src/components/manage/Blocks/DiscodataTableBlock/View.jsx b/src/components/manage/Blocks/DiscodataTableBlock/View.jsx new file mode 100644 index 0000000..b8c1b37 --- /dev/null +++ b/src/components/manage/Blocks/DiscodataTableBlock/View.jsx @@ -0,0 +1,461 @@ +import React, { useState, useEffect } from 'react'; +import { connect } from 'react-redux'; +import { compose } from 'redux'; +import { isString } from 'lodash'; +import { Link } from 'react-router-dom'; +import qs from 'query-string'; +import { Table, Pagination } from 'semantic-ui-react'; +import downSVG from '@plone/volto/icons/down-key.svg'; +import upSVG from '@plone/volto/icons/up-key.svg'; +import { Icon } from '@plone/volto/components'; +import DiscodataSqlBuilderView from 'volto-datablocks/DiscodataSqlBuilder/View'; +import { setQueryParam } from 'volto-datablocks/actions'; +import { Dimmer, Loader } from 'semantic-ui-react'; + +const components = { + object_link_length: (schemaMetadata, itemMetadata, item) => { + return ( + + {item[itemMetadata] && Object.keys(item[itemMetadata]).length}{' '} + {schemaMetadata.title} + + ); + }, + object_link_keys: (schemaMetadata, itemMetadata, item) => { + return ( + <> + {item[itemMetadata] && + Object.keys(item[itemMetadata])?.map( + (key, index) => + index < 3 && {key}, + )} + {item[itemMetadata] && Object.keys(item[itemMetadata]).length > 3 && ( + + {Object.keys(item[itemMetadata]).length - 3} {schemaMetadata.title} + + )} + + ); + }, + textarea_link_value: (schemaMetadata, itemMetadata, item) => { + return ( + <> +
+ + {schemaMetadata.title} + + + ); + }, + button_link_value: (schemaMetadata, itemMetadata, item, props) => { + const { search } = props.discodata_query; + let newSearch = { ...search }; + let updatedSearch = false; + schemaMetadata.queriesToSet.forEach((key, index) => { + if ( + schemaMetadata.discodataQueriesKeys[index] && + (!newSearch[key] || + (newSearch[key] && + newSearch[key] !== + item[schemaMetadata.discodataQueriesKeys[index]])) + ) { + updatedSearch = true; + newSearch[key] = item[schemaMetadata.discodataQueriesKeys[index]]; + } + }); + return ( +
+ { + if (updatedSearch) props.setQueryParam({ queryParam: newSearch }); + }} + to={ + schemaMetadata.urlFieldId + '' + // (newSearch ? `?${qs.stringify(newSearch)}` : '') + } + > + {schemaMetadata.title} + +
+ ); + }, + default: (schemaMetadata, itemMetadata, item) => { + if (Array.isArray(item[itemMetadata])) return item[itemMetadata].join(', '); + if (typeof item[itemMetadata] === 'object' && item[itemMetadata] !== null) + return Object.keys(item[itemMetadata]).join(', '); + return item[itemMetadata]; + }, +}; + +const View = (props) => { + const [state, setState] = useState({ + metadata: {}, + tableHeaders: 0, + pagination: { + activePage: 1, + itemsPerPage: 25, + }, + selectedItemIndex: -1, + }); + const [collection, set_collection] = useState(''); + const [collection_count, set_collection_count] = useState(''); + const extent = props.discodata_query.search.extent || [ + -10686671.0000035, + -2430148.00000588, + 6199975.99999531, + 10421410.9999871, + ]; + const sqls = props.data?.sql?.value + ? JSON.parse(props.data.sql.value).properties + : {}; + const { activePage, itemsPerPage } = state.pagination; + let items = [], + totalItems = 0; + if ( + (sqls && Object.keys(sqls).length >= 2 && items, + props.data.itemsCountKey?.value) + ) { + const local_collection = Object.keys(sqls).filter( + (key) => !key.includes('collection_count'), + )[0]; + const local_collection_count = Object.keys(sqls).filter((key) => + key.includes('collection_count'), + )[0]; + if (collection !== local_collection) { + set_collection(local_collection); + } + if (collection_count !== local_collection_count) { + set_collection_count(local_collection_count); + } + items = props.discodata_resources.data[local_collection] || []; + totalItems = + props.data.itemsCountKey?.value && + props.discodata_resources.data[local_collection_count] && + Array.isArray(props.discodata_resources.data[local_collection_count]) && + props.discodata_resources.data[local_collection_count].length > 0 + ? props.discodata_resources.data[local_collection_count].reduce( + (acc, el) => + acc[props.data.itemsCountKey.value] + + el[props.data.itemsCountKey.value], + )[props.data.itemsCountKey.value] + : 0; + } + useEffect(() => { + const metadata = props.data?.metadata + ? isString(props.data.metadata.value) + ? JSON.parse(props.data.metadata.value) + : props.data.metadata.value + : {}; + setState({ + ...state, + metadata, + tableHeaders: metadata?.fieldsets?.[0]?.fields?.length, + }); + /* eslint-disable-next-line */ + }, [props.data?.metadata?.value]) + + const loader = + props.discodata_resources.pendingRequests[collection] || + props.discodata_resources.pendingRequests[collection_count]; + + const additionalWhereStatements = [ + `x_3857 >= ${extent[0]} AND x_3857 <= ${extent[2]}`, + `y_3857 >= ${extent[1]} AND y_3857 <= ${extent[3]}`, + ]; + + return ( +
+ + {items?.length ? ( + + + {/* ==== TABLE HEADER ==== */} + + + {state.metadata?.fieldsets?.[0]?.fields?.map( + (meta) => + state.metadata.properties[meta].tableType === + 'Table header' && ( + + {state.metadata.properties[meta].title} + + ), + )} + + + + {/* ==== TABLE BODY ==== */} + + {items?.map((item, trIndex) => ( + + {/* ==== TABLE ROW ====*/} + + {state.metadata?.fieldsets?.[0]?.fields?.map( + (meta, cellIndex) => { + if ( + state.metadata.properties[meta].tableType === + 'Table header' + ) { + const dataType = + state.metadata.properties[meta].dataType; + const show = state.metadata.properties[meta].show; + + return ( + + {components[`${dataType}_${show}`] + ? components[`${dataType}_${show}`]( + state.metadata.properties[meta], + meta, + item, + props, + ) + : components.default( + state.metadata.properties[meta], + meta, + item, + props, + )} + + ); + } + return null; + }, + )} + + + + + {/* ==== TABLE HIDDEN ROW ==== */} + + +
+
+ {props.data?.hiddenRowTypes?.value + ?.filter((type) => type !== 'Action') + .map((type) => ( +
+ {type !== 'Action' && ( + {type} + )} +
+ {state.metadata?.fieldsets?.[0]?.fields?.map( + (meta) => { + if ( + state.metadata.properties[meta] + .tableType === 'Hidden row' && + state.metadata.properties[meta] + .hiddenRowType === type + ) { + const dataType = + state.metadata.properties[meta] + .dataType; + const show = + state.metadata.properties[meta] + .show; + + return ( + + {components[`${dataType}_${show}`] + ? components[ + `${dataType}_${show}` + ]( + state.metadata.properties[ + meta + ], + meta, + item, + props, + ) + : components.default( + state.metadata.properties[ + meta + ], + meta, + item, + props, + )} + + ); + } + return null; + }, + )} +
+
+ ))} +
+
+ {props.data?.hiddenRowTypes?.value + ?.filter((type) => type === 'Action') + .map((type) => ( +
+
+ {state.metadata?.fieldsets?.[0]?.fields?.map( + (meta) => { + if ( + state.metadata.properties[meta] + .tableType === 'Hidden row' && + state.metadata.properties[meta] + .hiddenRowType === type + ) { + const dataType = + state.metadata.properties[meta] + .dataType; + const show = + state.metadata.properties[meta] + .show; + + return ( + + {components[`${dataType}_${show}`] + ? components[ + `${dataType}_${show}` + ]( + state.metadata.properties[ + meta + ], + meta, + item, + props, + ) + : components.default( + state.metadata.properties[ + meta + ], + meta, + item, + props, + )} + + ); + } + return null; + }, + )} +
+
+ ))} +
+
+
+
+
+ ))} +
+ {/* ==== TABLE FOOTER ==== */} + + + + { + setState({ + ...state, + pagination: { + ...state.pagination, + activePage: pagination.activePage, + }, + selectedItemIndex: -1, + }); + }} + totalPages={Math.ceil(totalItems / itemsPerPage)} + firstItem={null} + lastItem={null} + /> + + + +
+
+ ) : ( +
+ )} + + + European Environment Agency + +
+ ); +}; + +export default compose( + connect( + (state, props) => ({ + discodata_query: state.discodata_query, + discodata_resources: state.discodata_resources, + }), + { setQueryParam }, + ), +)(View); diff --git a/src/localconfig.js b/src/localconfig.js index 2a26dc5..663fbc4 100644 --- a/src/localconfig.js +++ b/src/localconfig.js @@ -44,6 +44,9 @@ import DummyBlockEdit from '~/components/manage/Blocks/DummyBlock/Edit'; import DummyBlockView from '~/components/manage/Blocks/DummyBlock/View'; // Discodata components +import DiscodataTableBlockEdit from '~/components/manage/Blocks/DiscodataTableBlock/Edit'; +import DiscodataTableBlockView from '~/components/manage/Blocks/DiscodataTableBlock/View'; + import DiscodataComponentsBlockEdit from '~/components/manage/Blocks/DiscodataComponentsBlock/Edit'; import DiscodataComponentsBlockView from '~/components/manage/Blocks/DiscodataComponentsBlock/View'; @@ -224,6 +227,15 @@ export function applyConfig(voltoConfig) { // DISCODATA COMPONENTS + config.blocks.blocksConfig.discodata_components_table_block = { + id: 'discodata_components_table_block', + title: 'Table block', + group: 'discodata_components', + view: DiscodataTableBlockView, + edit: DiscodataTableBlockEdit, + icon: packSVG, + }; + config.blocks.blocksConfig.discodata_components_text = { id: 'discodata_components_text', title: 'Text',