@eeacms/volto-clms-theme 1.1.82 → 1.1.84

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,11 +4,27 @@ All notable changes to this project will be documented in this file. Dates are d
4
4
 
5
5
  Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
6
6
 
7
- ### [1.1.82](https://github.com/eea/volto-clms-theme/compare/1.1.81...1.1.82) - 21 November 2023
7
+ ### [1.1.84](https://github.com/eea/volto-clms-theme/compare/1.1.83...1.1.84) - 24 November 2023
8
+
9
+ #### :bug: Bug Fixes
10
+
11
+ - fix: logout cache fix [Unai - [`9da13b1`](https://github.com/eea/volto-clms-theme/commit/9da13b1a94ab442d81c9c142c0f34016caca0ac4)]
12
+
13
+ #### :hammer_and_wrench: Others
14
+
15
+ - form button style fix [Unai - [`e9f335c`](https://github.com/eea/volto-clms-theme/commit/e9f335c3183e7356aba43dd091ef83b38846c73c)]
16
+ ### [1.1.83](https://github.com/eea/volto-clms-theme/compare/1.1.82...1.1.83) - 24 November 2023
17
+
18
+ #### :bug: Bug Fixes
19
+
20
+ - fix: CLMS-2844 breadcrumbs not shown at /cart /profile /cart-downloads /all-downloads [Unai - [`507dbee`](https://github.com/eea/volto-clms-theme/commit/507dbeedd821d070e1e9107d8e980b0d3fb85700)]
21
+ - fix:CLMS-2838 form view customization to style the submit button [Unai - [`95ca208`](https://github.com/eea/volto-clms-theme/commit/95ca208262883c759ddacfcee8e001c1a7b3ac1d)]
8
22
 
9
23
  #### :hammer_and_wrench: Others
10
24
 
11
- - ul lien z-index eta margina [Zaloa Etxaniz - [`522ae5f`](https://github.com/eea/volto-clms-theme/commit/522ae5fa48e0a1c394866606470fa6737b0de02c)]
25
+ - CLMS-2848 replaced window.location.href with window.location.assign [Unai - [`b1b644c`](https://github.com/eea/volto-clms-theme/commit/b1b644c573f547f5b6e8fd4d75e8313b23bc613b)]
26
+ ### [1.1.82](https://github.com/eea/volto-clms-theme/compare/1.1.81...1.1.82) - 21 November 2023
27
+
12
28
  ### [1.1.81](https://github.com/eea/volto-clms-theme/compare/1.1.80...1.1.81) - 20 November 2023
13
29
 
14
30
  #### :rocket: New Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-clms-theme",
3
- "version": "1.1.82",
3
+ "version": "1.1.84",
4
4
  "description": "volto-clms-theme: Volto theme for CLMS site",
5
5
  "main": "src/index.js",
6
6
  "author": "CodeSyntax for the European Environment Agency",
@@ -19,18 +19,18 @@ const CclHomeProductsBlockView = (props) => {
19
19
  }
20
20
  tabIndex={0}
21
21
  onClick={() => {
22
- window.location.href = isUrl(product?.linkSelector)
23
- ? product.linkSelector
22
+ isUrl(product?.linkSelector)
23
+ ? window.location.assign(product.linkSelector)
24
24
  : isUrl('http://' + product?.linkSelector)
25
- ? 'http://' + product.linkSelector
26
- : '#';
25
+ ? window.location.assign('http://' + product.linkSelector)
26
+ : window.location.replace(window.location + '#');
27
27
  }}
28
28
  onKeyDown={() => {
29
- window.location.href = isUrl(product?.linkSelector)
30
- ? product.linkSelector
29
+ isUrl(product?.linkSelector)
30
+ ? window.location.assign(product.linkSelector)
31
31
  : isUrl('http://' + product?.linkSelector)
32
- ? 'http://' + product.linkSelector
33
- : '#';
32
+ ? window.location.assign('http://' + product.linkSelector)
33
+ : window.location.replace(window.location + '#');
34
34
  }}
35
35
  id={product.productIcon === 'iconless' ? '' : product.productIcon}
36
36
  >
@@ -1,19 +1,14 @@
1
- /**
2
- * Breadcrumbs components.
3
- * @module components/theme/Breadcrumbs/Breadcrumbs
4
- */
5
-
6
- import React, { Component } from 'react';
7
- import PropTypes from 'prop-types';
8
- import { connect } from 'react-redux';
9
- import { compose } from 'redux';
10
- import { Link } from 'react-router-dom';
1
+ import { useEffect } from 'react';
2
+ import { defineMessages, useIntl } from 'react-intl';
3
+ import { useDispatch, useSelector, shallowEqual } from 'react-redux';
4
+ import { Link, useLocation } from 'react-router-dom';
11
5
  import { Container } from 'semantic-ui-react';
12
- import { defineMessages, injectIntl } from 'react-intl';
13
6
 
14
7
  import { getBreadcrumbs } from '@plone/volto/actions';
15
8
  import { getBaseUrl, hasApiExpander } from '@plone/volto/helpers';
16
9
 
10
+ import PropTypes from 'prop-types';
11
+
17
12
  const messages = defineMessages({
18
13
  home: {
19
14
  id: 'Home',
@@ -25,113 +20,78 @@ const messages = defineMessages({
25
20
  },
26
21
  });
27
22
 
28
- /**
29
- * Breadcrumbs container class.
30
- */
31
- export class BreadcrumbsComponent extends Component {
32
- /**
33
- * Property types.
34
- * @property {Object} propTypes Property types.
35
- * @static
36
- */
37
- static propTypes = {
38
- getBreadcrumbs: PropTypes.func.isRequired,
39
- pathname: PropTypes.string.isRequired,
40
- root: PropTypes.string,
41
- items: PropTypes.arrayOf(
42
- PropTypes.shape({
43
- title: PropTypes.string,
44
- url: PropTypes.string,
45
- }),
46
- ).isRequired,
47
- extraItems: PropTypes.arrayOf(
48
- PropTypes.shape({
49
- title: PropTypes.string,
50
- url: PropTypes.string,
51
- }),
52
- ),
53
- };
23
+ const BreadcrumbsComponent = ({ pathname }) => {
24
+ const intl = useIntl();
25
+ const dispatch = useDispatch();
26
+ const { pathname: realPath } = useLocation();
27
+ const noBreadCrumbsItems = [];
54
28
 
55
- componentDidMount() {
56
- if (!hasApiExpander('breadcrumbs', getBaseUrl(this.props.pathname))) {
57
- this.props.getBreadcrumbs(getBaseUrl(this.props.pathname));
58
- }
59
- }
29
+ const items = useSelector(
30
+ (state) =>
31
+ realPath.endsWith('/cart') ||
32
+ realPath.endsWith('profile') ||
33
+ realPath.endsWith('cart-downloads') ||
34
+ realPath.endsWith('all-downloads')
35
+ ? noBreadCrumbsItems
36
+ : state.breadcrumbs.items,
37
+ shallowEqual,
38
+ );
39
+ const root = useSelector((state) => state.breadcrumbs.root);
60
40
 
61
- /**
62
- * Component will receive props
63
- * @method componentWillReceiveProps
64
- * @param {Object} nextProps Next properties
65
- * @returns {undefined}
66
- */
67
- UNSAFE_componentWillReceiveProps(nextProps) {
68
- if (nextProps.pathname !== this.props.pathname) {
69
- if (!hasApiExpander('breadcrumbs', getBaseUrl(this.props.pathname))) {
70
- this.props.getBreadcrumbs(getBaseUrl(nextProps.pathname));
71
- }
41
+ useEffect(() => {
42
+ if (!hasApiExpander('breadcrumbs', getBaseUrl(pathname))) {
43
+ dispatch(getBreadcrumbs(getBaseUrl(pathname)));
72
44
  }
73
- }
45
+ }, [dispatch, items, pathname]);
74
46
 
75
- /**
76
- * Render method.
77
- * @method render
78
- * @returns {string} Markup for the component.
79
- */
80
- render() {
81
- return (
82
- <>
83
- {this.props.items.length > 0 && (
84
- <nav
85
- aria-label={this.props.intl.formatMessage(messages.breadcrumbs)}
86
- className="ccl-breadcrumb"
87
- >
88
- <Container>
89
- <span className="ccl-u-sr-only">You are here:</span>
90
- <ol className="ccl-breadcrumb__segments-wrapper">
91
- <li className="ccl-breadcrumb__segment ccl-breadcrumb__segment--first">
92
- <Link
93
- to={this.props.root || '/'}
94
- className="ccl-link ccl-link--inverted ccl-link--standalone ccl-breadcrumb__link"
95
- title={this.props.intl.formatMessage(messages.home)}
96
- >
97
- {this.props.intl.formatMessage(messages.home)}
98
- </Link>
99
- </li>
100
- {this?.props?.items?.map((item, index, items) => [
101
- index < items.length - 1 ? (
102
- <li key={index} className="ccl-breadcrumb__segment">
103
- <Link
104
- to={item.url}
105
- className="ccl-link ccl-link--inverted ccl-link--standalone ccl-breadcrumb__link"
106
- >
107
- {item.title}
108
- </Link>
109
- </li>
110
- ) : (
111
- <li
112
- key={index}
113
- className="ccl-breadcrumb__segment ccl-breadcrumb__segment--last"
47
+ return (
48
+ <>
49
+ {items.length > 0 && (
50
+ <nav
51
+ aria-label={intl.formatMessage(messages.breadcrumbs)}
52
+ className="ccl-breadcrumb"
53
+ >
54
+ <Container>
55
+ <span className="ccl-u-sr-only">You are here:</span>
56
+ <ol className="ccl-breadcrumb__segments-wrapper">
57
+ <li className="ccl-breadcrumb__segment ccl-breadcrumb__segment--first">
58
+ <Link
59
+ to={root || '/'}
60
+ className="ccl-link ccl-link--inverted ccl-link--standalone ccl-breadcrumb__link"
61
+ title={intl.formatMessage(messages.home)}
62
+ >
63
+ {intl.formatMessage(messages.home)}
64
+ </Link>
65
+ </li>
66
+ {items?.map((item, index, items) => [
67
+ index < items.length - 1 ? (
68
+ <li key={index} className="ccl-breadcrumb__segment">
69
+ <Link
70
+ to={item.url}
71
+ className="ccl-link ccl-link--inverted ccl-link--standalone ccl-breadcrumb__link"
114
72
  >
115
- <span>{item.title}</span>
116
- </li>
117
- ),
118
- ])}
119
- </ol>
120
- </Container>
121
- </nav>
122
- )}
123
- </>
124
- );
125
- }
126
- }
73
+ {item.title}
74
+ </Link>
75
+ </li>
76
+ ) : (
77
+ <li
78
+ key={index}
79
+ className="ccl-breadcrumb__segment ccl-breadcrumb__segment--last"
80
+ >
81
+ <span>{item.title}</span>
82
+ </li>
83
+ ),
84
+ ])}
85
+ </ol>
86
+ </Container>
87
+ </nav>
88
+ )}
89
+ </>
90
+ );
91
+ };
92
+
93
+ BreadcrumbsComponent.propTypes = {
94
+ pathname: PropTypes.string.isRequired,
95
+ };
127
96
 
128
- export default compose(
129
- injectIntl,
130
- connect(
131
- (state) => ({
132
- items: state.breadcrumbs.items,
133
- root: state.breadcrumbs.root,
134
- }),
135
- { getBreadcrumbs },
136
- ),
137
- )(BreadcrumbsComponent);
97
+ export default BreadcrumbsComponent;
@@ -1,64 +1,36 @@
1
- import { Component } from 'react';
2
- import { connect } from 'react-redux';
3
-
4
- import { logout, purgeMessages } from '@plone/volto/actions';
5
-
6
- import PropTypes from 'prop-types';
1
+ import { useEffect, useMemo } from 'react';
2
+ import { useDispatch, useSelector, shallowEqual } from 'react-redux';
3
+ import { useHistory } from 'react-router-dom';
7
4
  import qs from 'query-string';
5
+ import { logout, purgeMessages } from '@plone/volto/actions';
8
6
 
9
- /**
10
- * Login container.
11
- * @module components/theme/Logout/Logout
12
- */
13
-
14
- /**
15
- * Logout class.
16
- * @class Logout
17
- * @extends Component
18
- */
19
- class CclLogout extends Component {
20
- /**
21
- * Property types.
22
- * @property {Object} propTypes Property types.
23
- * @static
24
- */
25
- static propTypes = {
26
- logout: PropTypes.func.isRequired,
27
- purgeMessages: PropTypes.func.isRequired,
28
- query: PropTypes.shape({
29
- return_url: PropTypes.string,
30
- }),
31
- };
32
-
33
- /**
34
- * Default properties.
35
- * @property {Object} defaultProps Default properties.
36
- * @static
37
- */
38
- static defaultProps = {
39
- query: null,
40
- };
41
-
42
- componentDidMount() {
43
- // eslint-disable-next-line no-restricted-globals
44
- this.props.logout();
45
- window.location.href = '/';
46
- this.props.purgeMessages();
47
- }
48
-
49
- /**
50
- * Render method.
51
- * @method render
52
- * @returns {string} Markup for the component.
53
- */
54
- render() {
55
- return '';
56
- }
57
- }
58
-
59
- export default connect(
60
- (state, props) => ({
61
- query: qs.parse(props.location.search),
62
- }),
63
- { logout, purgeMessages },
64
- )(CclLogout);
7
+ const Logout = ({ location }) => {
8
+ const token = useSelector((state) => state.userSession.token, shallowEqual);
9
+ const history = useHistory();
10
+ const dispatch = useDispatch();
11
+
12
+ const returnUrl = useMemo(
13
+ () =>
14
+ qs.parse(location.search).return_url ||
15
+ location.pathname
16
+ .replace(/\/login\/?$/, '')
17
+ .replace(/\/logout\/?$/, '') ||
18
+ '/',
19
+ [location],
20
+ );
21
+
22
+ useEffect(() => {
23
+ dispatch(logout());
24
+ dispatch(purgeMessages());
25
+ }, [dispatch]);
26
+
27
+ useEffect(() => {
28
+ if (!token) {
29
+ window.location.href = '/';
30
+ }
31
+ }, [history, returnUrl, token]);
32
+
33
+ return '';
34
+ };
35
+
36
+ export default Logout;
@@ -0,0 +1,204 @@
1
+ import React from 'react';
2
+ import { useIntl, defineMessages } from 'react-intl';
3
+ import {
4
+ Segment,
5
+ Message,
6
+ Grid,
7
+ Form,
8
+ Progress,
9
+ Button,
10
+ } from 'semantic-ui-react';
11
+ import { getFieldName } from 'volto-form-block/components/utils';
12
+ import Field from 'volto-form-block/components/Field';
13
+ import config from '@plone/volto/registry';
14
+
15
+ /* Style */
16
+ import 'volto-form-block/components/FormView.css';
17
+ import CclButton from '@eeacms/volto-clms-theme/components/CclButton/CclButton';
18
+
19
+ const messages = defineMessages({
20
+ default_submit_label: {
21
+ id: 'form_default_submit_label',
22
+ defaultMessage: 'Submit',
23
+ },
24
+ error: {
25
+ id: 'Error',
26
+ defaultMessage: 'Error',
27
+ },
28
+ success: {
29
+ id: 'form_submit_success',
30
+ defaultMessage: 'Sent!',
31
+ },
32
+ empty_values: {
33
+ id: 'form_empty_values_validation',
34
+ defaultMessage: 'Fill in the required fields',
35
+ },
36
+ reset: {
37
+ id: 'form_reset',
38
+ defaultMessage: 'Clear',
39
+ },
40
+ });
41
+
42
+ const FormView = ({
43
+ formState,
44
+ formErrors,
45
+ formData,
46
+ onChangeFormData,
47
+ data,
48
+ onSubmit,
49
+ resetFormState,
50
+ resetFormOnError,
51
+ captcha,
52
+ id,
53
+ }) => {
54
+ const intl = useIntl();
55
+ const FieldSchema = config.blocks.blocksConfig.form.fieldSchema;
56
+
57
+ const isValidField = (field) => {
58
+ return formErrors?.indexOf(field) < 0;
59
+ };
60
+
61
+ return (
62
+ <div className="block form">
63
+ <div className="public-ui">
64
+ <Segment style={{ margin: '2rem 0' }} padded>
65
+ {data.title && <h2>{data.title}</h2>}
66
+ {data.description && (
67
+ <p className="description">{data.description}</p>
68
+ )}
69
+ {formState.error ? (
70
+ <Message error role="alert">
71
+ <Message.Header as="h4">
72
+ {intl.formatMessage(messages.error)}
73
+ </Message.Header>
74
+ <p>{formState.error}</p>
75
+ <Button secondary type="clear" onClick={resetFormOnError}>
76
+ {intl.formatMessage(messages.reset)}
77
+ </Button>
78
+ </Message>
79
+ ) : formState.result ? (
80
+ <Message positive role="alert">
81
+ <Message.Header as="h4">
82
+ {intl.formatMessage(messages.success)}
83
+ </Message.Header>
84
+ <p>{formState.result}</p>
85
+ <Button secondary type="clear" onClick={resetFormState}>
86
+ {intl.formatMessage(messages.reset)}
87
+ </Button>
88
+ </Message>
89
+ ) : (
90
+ <Form
91
+ id={id}
92
+ loading={formState.loading}
93
+ onSubmit={onSubmit}
94
+ autoComplete="off"
95
+ method="post"
96
+ >
97
+ <Grid columns={1} padded="vertically">
98
+ {data.static_fields?.map((field) => (
99
+ <Grid.Row key={field.field_id} className="static-field">
100
+ <Grid.Column>
101
+ <Field
102
+ {...field}
103
+ field_type={field.field_type || 'text'}
104
+ name={
105
+ 'static_field_' +
106
+ (field.field_id ??
107
+ field.name?.toLowerCase()?.replace(' ', ''))
108
+ }
109
+ value={field.value}
110
+ onChange={() => {}}
111
+ disabled
112
+ valid
113
+ formHasErrors={formErrors?.length > 0}
114
+ />
115
+ </Grid.Column>
116
+ </Grid.Row>
117
+ ))}
118
+ {data.subblocks?.map((subblock, index) => {
119
+ let name = getFieldName(subblock.label, subblock.id);
120
+
121
+ var fields_to_send = [];
122
+ var fieldSchemaProperties = FieldSchema(subblock)?.properties;
123
+ for (var key in fieldSchemaProperties) {
124
+ if (fieldSchemaProperties[key].send_to_backend) {
125
+ fields_to_send.push(key);
126
+ }
127
+ }
128
+
129
+ var fields_to_send_with_value = Object.assign(
130
+ {},
131
+ ...fields_to_send.map((field) => {
132
+ return {
133
+ [field]: subblock[field],
134
+ };
135
+ }),
136
+ );
137
+
138
+ return (
139
+ <Grid.Row key={'row' + index}>
140
+ <Grid.Column>
141
+ <Field
142
+ {...subblock}
143
+ name={name}
144
+ onChange={(field, value) =>
145
+ onChangeFormData(
146
+ subblock.id,
147
+ field,
148
+ value,
149
+ fields_to_send_with_value,
150
+ )
151
+ }
152
+ value={
153
+ subblock.field_type === 'static_text'
154
+ ? subblock.value
155
+ : formData[name]?.value
156
+ }
157
+ valid={isValidField(name)}
158
+ formHasErrors={formErrors?.length > 0}
159
+ />
160
+ </Grid.Column>
161
+ </Grid.Row>
162
+ );
163
+ })}
164
+ {captcha.render()}
165
+ {formErrors.length > 0 && (
166
+ <Message error role="alert">
167
+ <Message.Header as="h4">
168
+ {intl.formatMessage(messages.error)}
169
+ </Message.Header>
170
+ <p>{intl.formatMessage(messages.empty_values)}</p>
171
+ </Message>
172
+ )}
173
+ <Grid.Row centered className="row-padded-top">
174
+ <Grid.Column textAlign="center">
175
+ <CclButton
176
+ className="ccl-button ccl-button-green"
177
+ type="submit"
178
+ disabled={formState.loading}
179
+ >
180
+ {data.submit_label ||
181
+ intl.formatMessage(messages.default_submit_label)}
182
+
183
+ {formState.loading && (
184
+ <Progress
185
+ role="progressbar"
186
+ percent={100}
187
+ active
188
+ success={false}
189
+ color="grey"
190
+ />
191
+ )}
192
+ </CclButton>
193
+ </Grid.Column>
194
+ </Grid.Row>
195
+ </Grid>
196
+ </Form>
197
+ )}
198
+ </Segment>
199
+ </div>
200
+ </div>
201
+ );
202
+ };
203
+
204
+ export default FormView;