@eeacms/volto-clms-theme 1.1.38 → 1.1.40
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 +24 -0
- package/package.json +2 -1
- package/src/components/Blocks/CustomTemplates/VoltoSearchBlock/AccordionFacet.jsx +3 -3
- package/src/components/Blocks/CustomTemplates/VoltoSearchBlock/CheckboxTreeFacet.jsx +3 -3
- package/src/components/Blocks/CustomTemplates/VoltoSearchBlock/CheckboxTreeParentFacet.jsx +3 -3
- package/src/components/Blocks/CustomTemplates/VoltoSearchBlock/DoubleRangeFacet.jsx +3 -20
- package/src/components/Blocks/CustomTemplates/VoltoSearchBlock/DoubleRangeSpatialFacet.jsx +3 -21
- package/src/components/Blocks/CustomTemplates/VoltoSearchBlock/utils.js +36 -0
- package/src/components/CLMSDownloadCartView/CLMSDownloadCartView.jsx +0 -2
- package/src/components/CLMSDownloadCartView/conversion.js +1 -1
- package/src/components/CLMSProfileView/CLMSApiTokensView.jsx +1 -1
- package/src/components/CLMSProfileView/CLMSDeleteProfileView.jsx +1 -1
- package/src/components/CLMSProfileView/CLMSNewsletterSubscriberView.jsx +3 -3
- package/src/components/CLMSProfileView/CLMSProfileView.jsx +1 -1
- package/src/components/CLMSSubscriptionView/SubscriptionView.jsx +8 -4
- package/src/components/CLMSSubscriptionView/subscription_utils.js +1 -1
- package/src/components/CartIconCounter/CartIconCounter.jsx +190 -0
- package/src/components/CartIconCounter/CartIconCounter.test.jsx +141 -0
- package/src/customizations/volto/components/theme/Footer/Footer.jsx +35 -17
- package/src/customizations/volto/components/theme/Header/Header.jsx +4 -91
- package/theme/site/extras/custom.variables +3 -0
- package/theme/site/extras/styles.less +58 -41
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,30 @@ 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.40](https://github.com/eea/volto-clms-theme/compare/1.1.39...1.1.40) - 14 September 2023
|
|
8
|
+
|
|
9
|
+
#### :rocket: New Features
|
|
10
|
+
|
|
11
|
+
- feat: cart addition modal text modifications [Ion Lizarazu - [`bf93cf5`](https://github.com/eea/volto-clms-theme/commit/bf93cf5f336cc1e905dd444b17799bd2841b1c38)]
|
|
12
|
+
|
|
13
|
+
#### :bug: Bug Fixes
|
|
14
|
+
|
|
15
|
+
- fix: useCase card style [Ion Lizarazu - [`6ac7e36`](https://github.com/eea/volto-clms-theme/commit/6ac7e360066b9f55614b59ea4a102a150c7f00ba)]
|
|
16
|
+
|
|
17
|
+
#### :hammer_and_wrench: Others
|
|
18
|
+
|
|
19
|
+
- s/CLMS portal/CLMS website [Mikel Larreategi - [`76a17d7`](https://github.com/eea/volto-clms-theme/commit/76a17d7f2ffca88c8841a85315bbac8d8324af0c)]
|
|
20
|
+
- remove warnings [Ion Lizarazu - [`531db38`](https://github.com/eea/volto-clms-theme/commit/531db382e69cef045486d0067788307abf10a8e4)]
|
|
21
|
+
- jest tests [Ion Lizarazu - [`b5e7409`](https://github.com/eea/volto-clms-theme/commit/b5e7409a24e80cebcfed79b467af437cde70f023)]
|
|
22
|
+
- add test [Ion Lizarazu - [`ab76914`](https://github.com/eea/volto-clms-theme/commit/ab76914e901ce9e31faa5f32762f3e13cd157dae)]
|
|
23
|
+
- mv CartIconCounter [Ion Lizarazu - [`8fa987f`](https://github.com/eea/volto-clms-theme/commit/8fa987fdf652912233eab4e78ec17a30a662ab73)]
|
|
24
|
+
### [1.1.39](https://github.com/eea/volto-clms-theme/compare/1.1.38...1.1.39) - 12 September 2023
|
|
25
|
+
|
|
26
|
+
#### :hammer_and_wrench: Others
|
|
27
|
+
|
|
28
|
+
- remove duplications [Ion Lizarazu - [`80b0a43`](https://github.com/eea/volto-clms-theme/commit/80b0a435926157d2a5d9f0ea2416431356f3d7a4)]
|
|
29
|
+
- Add expanded by default option to the search block facets [Ion Lizarazu - [`f0a30f8`](https://github.com/eea/volto-clms-theme/commit/f0a30f868eca3ab6860e8465647178bdb33c9eb8)]
|
|
30
|
+
- replace newsletter with CLMS updates and modify some footer stuff [Ion Lizarazu - [`1f7255a`](https://github.com/eea/volto-clms-theme/commit/1f7255a81001de686e23c29b36e7f4d855fe2285)]
|
|
7
31
|
### [1.1.38](https://github.com/eea/volto-clms-theme/compare/1.1.37...1.1.38) - 7 September 2023
|
|
8
32
|
|
|
9
33
|
#### :hammer_and_wrench: Others
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eeacms/volto-clms-theme",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.40",
|
|
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",
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"@eeacms/volto-tabs-block": "3.0.1",
|
|
48
48
|
"@eeacms/volto-taxonomy": "3.0.1",
|
|
49
49
|
"@fortawesome/fontawesome-svg-core": "1.2.35",
|
|
50
|
+
"@fortawesome/free-brands-svg-icons": "6.4.2",
|
|
50
51
|
"@fortawesome/free-regular-svg-icons": "5.15.3",
|
|
51
52
|
"@fortawesome/free-solid-svg-icons": "5.15.3",
|
|
52
53
|
"@fortawesome/react-fontawesome": "0.1.14",
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Checkbox, List } from 'semantic-ui-react';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import {
|
|
4
|
-
selectFacetSchemaEnhancer,
|
|
5
4
|
selectFacetStateToValue,
|
|
6
5
|
selectFacetValueToQuery,
|
|
7
6
|
} from '@plone/volto/components/manage/Blocks/Search/components/base';
|
|
7
|
+
import { enhanceExpandedByDefault } from './utils.js';
|
|
8
8
|
|
|
9
9
|
const AccordionFacet = (props) => {
|
|
10
10
|
const {
|
|
@@ -17,7 +17,7 @@ const AccordionFacet = (props) => {
|
|
|
17
17
|
typeName,
|
|
18
18
|
} = props;
|
|
19
19
|
const facetValue = value;
|
|
20
|
-
var [open, setOpen] = React.useState(false);
|
|
20
|
+
var [open, setOpen] = React.useState(facet.expandedByDefault ?? false);
|
|
21
21
|
function isChoiceValue(isChecked, choiceValue) {
|
|
22
22
|
return isChecked ? choiceValue : null;
|
|
23
23
|
}
|
|
@@ -92,7 +92,7 @@ const AccordionFacet = (props) => {
|
|
|
92
92
|
);
|
|
93
93
|
};
|
|
94
94
|
|
|
95
|
-
AccordionFacet.schemaEnhancer =
|
|
95
|
+
AccordionFacet.schemaEnhancer = enhanceExpandedByDefault;
|
|
96
96
|
AccordionFacet.stateToValue = selectFacetStateToValue;
|
|
97
97
|
AccordionFacet.valueToQuery = selectFacetValueToQuery;
|
|
98
98
|
export default AccordionFacet;
|
|
@@ -5,11 +5,11 @@ import { Checkbox, List } from 'semantic-ui-react';
|
|
|
5
5
|
import { structure_taxonomy_terms } from '@eeacms/volto-clms-theme/components';
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
|
-
selectFacetSchemaEnhancer,
|
|
9
8
|
selectFacetStateToValue,
|
|
10
9
|
selectFacetValueToQuery,
|
|
11
10
|
} from '@plone/volto/components/manage/Blocks/Search/components/base';
|
|
12
11
|
import { checkAllChildren } from './utils';
|
|
12
|
+
import { enhanceExpandedByDefault } from './utils.js';
|
|
13
13
|
|
|
14
14
|
const hasAllChildrensSelected = (value, childrens) => {
|
|
15
15
|
var result = true;
|
|
@@ -40,7 +40,7 @@ const CheckboxTreeFacet = (props) => {
|
|
|
40
40
|
const { facet, choices, onChange, value, typeName } = props;
|
|
41
41
|
|
|
42
42
|
const facetValue = value;
|
|
43
|
-
var [open, setOpen] = useState(false);
|
|
43
|
+
var [open, setOpen] = useState(facet.expandedByDefault ?? false);
|
|
44
44
|
let options = [];
|
|
45
45
|
if (choices?.length > 0) {
|
|
46
46
|
options = structure_taxonomy_terms(choices);
|
|
@@ -161,7 +161,7 @@ const CheckboxListParent = ({ option, key, onChange, value, id }) => {
|
|
|
161
161
|
);
|
|
162
162
|
};
|
|
163
163
|
|
|
164
|
-
CheckboxTreeFacet.schemaEnhancer =
|
|
164
|
+
CheckboxTreeFacet.schemaEnhancer = enhanceExpandedByDefault;
|
|
165
165
|
CheckboxTreeFacet.stateToValue = selectFacetStateToValue;
|
|
166
166
|
CheckboxTreeFacet.valueToQuery = selectFacetValueToQuery;
|
|
167
167
|
export default CheckboxTreeFacet;
|
|
@@ -5,11 +5,11 @@ import { Checkbox, List } from 'semantic-ui-react';
|
|
|
5
5
|
import { structure_taxonomy_terms } from '@eeacms/volto-clms-theme/components';
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
|
-
selectFacetSchemaEnhancer,
|
|
9
8
|
selectFacetStateToValue,
|
|
10
9
|
selectFacetValueToQuery,
|
|
11
10
|
} from '@plone/volto/components/manage/Blocks/Search/components/base';
|
|
12
11
|
import { checkAllChildren, uncheckOptionAndChildren } from './utils';
|
|
12
|
+
import { enhanceExpandedByDefault } from './utils.js';
|
|
13
13
|
|
|
14
14
|
const hasAllChildrensSelected = (value, childrens) => {
|
|
15
15
|
var result = true;
|
|
@@ -39,7 +39,7 @@ const Wrapper = ({ typeName, children }) => {
|
|
|
39
39
|
const CheckboxTreeParentFacet = (props) => {
|
|
40
40
|
const { facet, choices, onChange, value, typeName } = props;
|
|
41
41
|
const facetValue = value;
|
|
42
|
-
var [open, setOpen] = useState(false);
|
|
42
|
+
var [open, setOpen] = useState(facet.expandedByDefault ?? false);
|
|
43
43
|
let options = [];
|
|
44
44
|
if (choices?.length > 0) {
|
|
45
45
|
options = structure_taxonomy_terms(choices);
|
|
@@ -153,7 +153,7 @@ const CheckboxListParent = ({ option, key, onChange, value, id }) => {
|
|
|
153
153
|
);
|
|
154
154
|
};
|
|
155
155
|
|
|
156
|
-
CheckboxTreeParentFacet.schemaEnhancer =
|
|
156
|
+
CheckboxTreeParentFacet.schemaEnhancer = enhanceExpandedByDefault;
|
|
157
157
|
CheckboxTreeParentFacet.stateToValue = selectFacetStateToValue;
|
|
158
158
|
CheckboxTreeParentFacet.valueToQuery = selectFacetValueToQuery;
|
|
159
159
|
export default CheckboxTreeParentFacet;
|
|
@@ -8,30 +8,12 @@ import {
|
|
|
8
8
|
selectFacetStateToValue,
|
|
9
9
|
selectFacetValueToQuery,
|
|
10
10
|
} from '@plone/volto/components/manage/Blocks/Search/components/base';
|
|
11
|
-
|
|
12
|
-
const doubleRangeFacetSchemaEnhancer = ({ schema, formData }) => {
|
|
13
|
-
// adds (enables) the 'multiple' field after the 'type' dropdown
|
|
14
|
-
let { fields } = schema.fieldsets[0];
|
|
15
|
-
const pos = fields.indexOf('type') + 1;
|
|
16
|
-
fields = [
|
|
17
|
-
...fields.slice(0, pos),
|
|
18
|
-
'step',
|
|
19
|
-
'multiple',
|
|
20
|
-
...fields.slice(pos, fields.length),
|
|
21
|
-
];
|
|
22
|
-
|
|
23
|
-
schema.properties = {
|
|
24
|
-
...schema.properties,
|
|
25
|
-
step: { title: 'Step', type: 'number', default: 1 },
|
|
26
|
-
};
|
|
27
|
-
schema.fieldsets[0].fields = fields;
|
|
28
|
-
return schema;
|
|
29
|
-
};
|
|
11
|
+
import { doubleRangeFacetSchemaEnhancer } from './utils.js';
|
|
30
12
|
|
|
31
13
|
const DoubleRangeFacet = (props) => {
|
|
32
14
|
const { facet, choices, onChange, value } = props;
|
|
33
15
|
const facetValue = value;
|
|
34
|
-
var [open, setOpen] = React.useState(false);
|
|
16
|
+
var [open, setOpen] = React.useState(facet.expandedByDefault ?? false);
|
|
35
17
|
|
|
36
18
|
const convertToRange = (values) => {
|
|
37
19
|
return {
|
|
@@ -111,6 +93,7 @@ const DoubleRangeFacet = (props) => {
|
|
|
111
93
|
};
|
|
112
94
|
|
|
113
95
|
DoubleRangeFacet.schemaEnhancer = doubleRangeFacetSchemaEnhancer;
|
|
96
|
+
|
|
114
97
|
DoubleRangeFacet.stateToValue = selectFacetStateToValue;
|
|
115
98
|
DoubleRangeFacet.valueToQuery = selectFacetValueToQuery;
|
|
116
99
|
export default DoubleRangeFacet;
|
|
@@ -8,25 +8,7 @@ import {
|
|
|
8
8
|
selectFacetStateToValue,
|
|
9
9
|
selectFacetValueToQuery,
|
|
10
10
|
} from '@plone/volto/components/manage/Blocks/Search/components/base';
|
|
11
|
-
|
|
12
|
-
const doubleRangeSpatialFacetSchemaEnhancer = ({ schema, formData }) => {
|
|
13
|
-
// adds (enables) the 'multiple' field after the 'type' dropdown
|
|
14
|
-
let { fields } = schema.fieldsets[0];
|
|
15
|
-
const pos = fields.indexOf('type') + 1;
|
|
16
|
-
fields = [
|
|
17
|
-
...fields.slice(0, pos),
|
|
18
|
-
'step',
|
|
19
|
-
'multiple',
|
|
20
|
-
...fields.slice(pos, fields.length),
|
|
21
|
-
];
|
|
22
|
-
|
|
23
|
-
schema.properties = {
|
|
24
|
-
...schema.properties,
|
|
25
|
-
step: { title: 'Step', type: 'number', default: 1 },
|
|
26
|
-
};
|
|
27
|
-
schema.fieldsets[0].fields = fields;
|
|
28
|
-
return schema;
|
|
29
|
-
};
|
|
11
|
+
import { doubleRangeFacetSchemaEnhancer } from './utils.js';
|
|
30
12
|
|
|
31
13
|
const mToKm = (m) => {
|
|
32
14
|
return m >= 1000 ? Math.round((m / 1000) * 10) / 10 + 'km' : m + 'm';
|
|
@@ -82,7 +64,7 @@ const convertToRange = (values, step) => {
|
|
|
82
64
|
const DoubleRangeSpatialFacet = (props) => {
|
|
83
65
|
const { facet, choices, onChange, value } = props;
|
|
84
66
|
const facetValue = value;
|
|
85
|
-
var [open, setOpen] = React.useState(false);
|
|
67
|
+
var [open, setOpen] = React.useState(facet.expandedByDefault ?? false);
|
|
86
68
|
|
|
87
69
|
const startingValues = convertToRange(choices, facet?.step);
|
|
88
70
|
|
|
@@ -156,7 +138,7 @@ const DoubleRangeSpatialFacet = (props) => {
|
|
|
156
138
|
);
|
|
157
139
|
};
|
|
158
140
|
|
|
159
|
-
DoubleRangeSpatialFacet.schemaEnhancer =
|
|
141
|
+
DoubleRangeSpatialFacet.schemaEnhancer = doubleRangeFacetSchemaEnhancer;
|
|
160
142
|
DoubleRangeSpatialFacet.stateToValue = selectFacetStateToValue;
|
|
161
143
|
DoubleRangeSpatialFacet.valueToQuery = selectFacetValueToQuery;
|
|
162
144
|
export default DoubleRangeSpatialFacet;
|
|
@@ -21,3 +21,39 @@ export const uncheckOptionAndChildren = (value, option) => {
|
|
|
21
21
|
}
|
|
22
22
|
});
|
|
23
23
|
};
|
|
24
|
+
|
|
25
|
+
export const enhanceExpandedByDefault = ({ schema, formData }) => {
|
|
26
|
+
return expandedByDefault(schema);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const doubleRangeFacetSchemaEnhancer = ({ schema, formData }) => {
|
|
30
|
+
// adds (enables) the 'multiple' field after the 'type' dropdown
|
|
31
|
+
let { fields } = schema.fieldsets[0];
|
|
32
|
+
const pos = fields.indexOf('type') + 1;
|
|
33
|
+
fields = [
|
|
34
|
+
...fields.slice(0, pos),
|
|
35
|
+
'step',
|
|
36
|
+
'multiple',
|
|
37
|
+
...fields.slice(pos, fields.length),
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
schema.properties = {
|
|
41
|
+
...schema.properties,
|
|
42
|
+
step: { title: 'Step', type: 'number', default: 1 },
|
|
43
|
+
};
|
|
44
|
+
schema.fieldsets[0].fields = fields;
|
|
45
|
+
return expandedByDefault(schema);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const expandedByDefault = (schema) => {
|
|
49
|
+
schema.properties = {
|
|
50
|
+
...schema.properties,
|
|
51
|
+
expandedByDefault: {
|
|
52
|
+
title: 'Expanded by default',
|
|
53
|
+
type: 'boolean',
|
|
54
|
+
default: false,
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
schema.fieldsets[0].fields.push('expandedByDefault');
|
|
58
|
+
return schema;
|
|
59
|
+
};
|
|
@@ -46,7 +46,7 @@ const messages = defineMessages({
|
|
|
46
46
|
description: {
|
|
47
47
|
id: 'descripton',
|
|
48
48
|
defaultMessage:
|
|
49
|
-
'API tokens are used for machine-to-machine communication with the CLMS
|
|
49
|
+
'API tokens are used for machine-to-machine communication with the CLMS website API. You need to create a new API token for each application in wich you need to use the CLMS website API. In this screen you can create a new API token, check the list of your tokens and also delete those tokens.',
|
|
50
50
|
},
|
|
51
51
|
createTitle: {
|
|
52
52
|
id: 'create title',
|
|
@@ -24,7 +24,7 @@ class CLMSNewsletterSubscriberView extends Component {
|
|
|
24
24
|
this.handleClick = this.handleClick.bind(this);
|
|
25
25
|
this.state = {
|
|
26
26
|
download: false,
|
|
27
|
-
headers: [{ label: '
|
|
27
|
+
headers: [{ label: 'CLMS updates subscribers', key: 'email' }],
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
componentDidMount() {
|
|
@@ -45,10 +45,10 @@ class CLMSNewsletterSubscriberView extends Component {
|
|
|
45
45
|
<Container>
|
|
46
46
|
<div>
|
|
47
47
|
<h1 className="page-title">
|
|
48
|
-
Download a list of
|
|
48
|
+
Download a list of CLMS updates subscribers
|
|
49
49
|
</h1>
|
|
50
50
|
<p>
|
|
51
|
-
Click in the button below to download a list of all the
|
|
51
|
+
Click in the button below to download a list of all the CLMS updates
|
|
52
52
|
subscribers.
|
|
53
53
|
</p>
|
|
54
54
|
<CclButton mode={'filled'} onClick={this.handleClick}>
|
|
@@ -69,7 +69,7 @@ class CLMSProfileView extends Component {
|
|
|
69
69
|
</div>
|
|
70
70
|
{(this.props.roles?.includes('Manager') ||
|
|
71
71
|
this.props.roles?.includes('Site Administrator')) && (
|
|
72
|
-
<div tabTitle="
|
|
72
|
+
<div tabTitle="CLMS updates subscribers">
|
|
73
73
|
<CLMSNewsletterSubscriberView />
|
|
74
74
|
</div>
|
|
75
75
|
)}
|
|
@@ -275,7 +275,7 @@ class SubscriptionView extends Component {
|
|
|
275
275
|
this.props.type === 'events'
|
|
276
276
|
? 'event'
|
|
277
277
|
: this.props.type === 'newsletter'
|
|
278
|
-
? 'the
|
|
278
|
+
? 'the CLMS updates'
|
|
279
279
|
: this.props.type,
|
|
280
280
|
})}
|
|
281
281
|
{this.props.isUnsubscribe === false &&
|
|
@@ -284,7 +284,7 @@ class SubscriptionView extends Component {
|
|
|
284
284
|
this.props.type === 'events'
|
|
285
285
|
? 'event'
|
|
286
286
|
: this.props.type === 'newsletter'
|
|
287
|
-
? 'the
|
|
287
|
+
? 'the CLMS updates'
|
|
288
288
|
: this.props.type,
|
|
289
289
|
})}{' '}
|
|
290
290
|
{this.props.type !== 'newsletter' &&
|
|
@@ -320,7 +320,7 @@ class SubscriptionView extends Component {
|
|
|
320
320
|
{
|
|
321
321
|
type:
|
|
322
322
|
this.props.type === 'newsletter'
|
|
323
|
-
? 'the
|
|
323
|
+
? 'the CLMS updates'
|
|
324
324
|
: this.props.type === 'events'
|
|
325
325
|
? 'event'
|
|
326
326
|
: this.props.type,
|
|
@@ -420,7 +420,11 @@ class SubscriptionView extends Component {
|
|
|
420
420
|
}}
|
|
421
421
|
>
|
|
422
422
|
{'Click here if you would like to unsubscribe from our'}{' '}
|
|
423
|
-
{this.props.type === 'events'
|
|
423
|
+
{this.props.type === 'events'
|
|
424
|
+
? 'event'
|
|
425
|
+
: this.props.type === 'newsletter'
|
|
426
|
+
? 'CLMS updates'
|
|
427
|
+
: this.props.type}
|
|
424
428
|
{this.props.type === 'newsletter' ? '' : ' notifications'}
|
|
425
429
|
</Link>
|
|
426
430
|
</>
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { Icon } from '@plone/volto/components';
|
|
3
|
+
import { useDispatch, useSelector } from 'react-redux';
|
|
4
|
+
import { Popup, Segment, Divider, Message } from 'semantic-ui-react';
|
|
5
|
+
import CclButton from '@eeacms/volto-clms-theme/components/CclButton/CclButton';
|
|
6
|
+
import clearSVG from '@plone/volto/icons/clear.svg';
|
|
7
|
+
import {
|
|
8
|
+
getDatasetTimeseries,
|
|
9
|
+
getNutsNames,
|
|
10
|
+
getDatasetsByUid,
|
|
11
|
+
} from '@eeacms/volto-clms-theme/actions';
|
|
12
|
+
import { getCartItems } from '@eeacms/volto-clms-utils/actions';
|
|
13
|
+
import { Link } from 'react-router-dom';
|
|
14
|
+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
15
|
+
|
|
16
|
+
export const onlyInLeft = (left, right, compareFunction) =>
|
|
17
|
+
left.filter(
|
|
18
|
+
(leftValue) =>
|
|
19
|
+
!right.some((rightValue) => compareFunction(leftValue, rightValue)),
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
export const CartIconCounter = () => {
|
|
23
|
+
const datasetTimeseries = useSelector((state) => state.datasetTimeseries);
|
|
24
|
+
const nutsnames = useSelector((state) => state.nutsnames);
|
|
25
|
+
const datasetsByUid = useSelector((state) => state.datasetsByUid);
|
|
26
|
+
const cartState = useSelector((state) => state.cart_items);
|
|
27
|
+
const cartState_ref = useRef(cartState);
|
|
28
|
+
const cart_icon_ref = React.useRef();
|
|
29
|
+
const intl = useSelector((state) => state.intl);
|
|
30
|
+
const user_id = useSelector((state) => state.users.user.id);
|
|
31
|
+
const [showPopup, setshowPopup] = useState(false);
|
|
32
|
+
const [cartDiff, setCartDiff] = useState(0);
|
|
33
|
+
const [cartDiffItems, setCartDiffItems] = useState([]);
|
|
34
|
+
const [hasTimeseries, setHasTimeseries] = useState(false);
|
|
35
|
+
const dispatch = useDispatch();
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
dispatch(getCartItems(user_id));
|
|
38
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
39
|
+
}, [user_id]);
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
cartDiffItems.forEach((newItem) => {
|
|
43
|
+
if (
|
|
44
|
+
!datasetTimeseries.loading &&
|
|
45
|
+
datasetTimeseries?.datasets[newItem.UID] === undefined
|
|
46
|
+
) {
|
|
47
|
+
dispatch(getDatasetTimeseries(newItem.UID));
|
|
48
|
+
}
|
|
49
|
+
if (newItem.area?.type) {
|
|
50
|
+
dispatch(getNutsNames(newItem.area?.value));
|
|
51
|
+
dispatch(getDatasetsByUid(newItem.UID));
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
return () => {};
|
|
55
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
56
|
+
}, [cartDiffItems]);
|
|
57
|
+
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
let hasTS = false;
|
|
60
|
+
if (datasetTimeseries.datasets) {
|
|
61
|
+
cartDiffItems.forEach((diffItem) => {
|
|
62
|
+
if (
|
|
63
|
+
datasetTimeseries.datasets[diffItem.UID] &&
|
|
64
|
+
datasetTimeseries.datasets[diffItem.UID].start
|
|
65
|
+
) {
|
|
66
|
+
hasTS = true;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
setHasTimeseries(hasTS);
|
|
71
|
+
|
|
72
|
+
return () => {};
|
|
73
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
74
|
+
}, [datasetTimeseries.datasets, datasetTimeseries.loaded, cartDiffItems]);
|
|
75
|
+
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
if (
|
|
78
|
+
cartState_ref.current.set.loading &&
|
|
79
|
+
cartState.set.loaded &&
|
|
80
|
+
cartState.items.length >= cartState_ref.current.items.length
|
|
81
|
+
) {
|
|
82
|
+
setCartDiff(cartState.items.length - cartState_ref.current.items.length);
|
|
83
|
+
setCartDiffItems(
|
|
84
|
+
onlyInLeft(
|
|
85
|
+
cartState.items,
|
|
86
|
+
cartState_ref.current.items,
|
|
87
|
+
(l, r) => l.unique_id === r.unique_id,
|
|
88
|
+
),
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
window.scrollTo({
|
|
92
|
+
top: 0,
|
|
93
|
+
left: 0,
|
|
94
|
+
behavior: 'smooth',
|
|
95
|
+
});
|
|
96
|
+
!showPopup && setTimeout(() => setshowPopup(true), 900);
|
|
97
|
+
setTimeout(() => {
|
|
98
|
+
setshowPopup(false);
|
|
99
|
+
}, 11000);
|
|
100
|
+
}
|
|
101
|
+
cartState_ref.current = cartState;
|
|
102
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
103
|
+
}, [cartState]);
|
|
104
|
+
return (
|
|
105
|
+
cartState.items && (
|
|
106
|
+
<>
|
|
107
|
+
<Popup
|
|
108
|
+
context={cart_icon_ref}
|
|
109
|
+
open={showPopup}
|
|
110
|
+
position="bottom center"
|
|
111
|
+
flowing
|
|
112
|
+
>
|
|
113
|
+
<Segment
|
|
114
|
+
attached="top"
|
|
115
|
+
style={{ padding: 0, display: 'flex', justifyContent: 'flex-end' }}
|
|
116
|
+
>
|
|
117
|
+
<Icon
|
|
118
|
+
onClick={() => setshowPopup(false)}
|
|
119
|
+
name={clearSVG}
|
|
120
|
+
size={20}
|
|
121
|
+
style={{ cursor: 'pointer' }}
|
|
122
|
+
/>
|
|
123
|
+
</Segment>
|
|
124
|
+
<Divider horizontal style={{ margin: 0 }}>
|
|
125
|
+
My cart
|
|
126
|
+
</Divider>
|
|
127
|
+
{cartDiff > 0 ? (
|
|
128
|
+
<Message positive>
|
|
129
|
+
{cartDiffItems.some((cdi) => cdi.area === '') && (
|
|
130
|
+
<p>
|
|
131
|
+
You added{' '}
|
|
132
|
+
<strong>
|
|
133
|
+
{cartDiff} new prepackaged item{cartDiff > 1 ? 's' : ''}
|
|
134
|
+
</strong>{' '}
|
|
135
|
+
to the cart.
|
|
136
|
+
</p>
|
|
137
|
+
)}
|
|
138
|
+
{datasetsByUid.loaded &&
|
|
139
|
+
nutsnames.loaded &&
|
|
140
|
+
cartDiffItems.map((cdi, key) => {
|
|
141
|
+
const ddata = datasetsByUid.loaded
|
|
142
|
+
? datasetsByUid?.datasets?.items.find(
|
|
143
|
+
(d) => d.UID === cdi.UID,
|
|
144
|
+
)
|
|
145
|
+
: {};
|
|
146
|
+
return (
|
|
147
|
+
<p key={key}>
|
|
148
|
+
<strong>Name:</strong> {ddata?.title} <br />
|
|
149
|
+
<strong>Area:</strong>{' '}
|
|
150
|
+
{nutsnames?.nutsnames?.[cdi?.area?.value]}
|
|
151
|
+
</p>
|
|
152
|
+
);
|
|
153
|
+
})}
|
|
154
|
+
{hasTimeseries && (
|
|
155
|
+
<>
|
|
156
|
+
<br />
|
|
157
|
+
Click on Go to cart to select time interval.
|
|
158
|
+
</>
|
|
159
|
+
)}
|
|
160
|
+
</Message>
|
|
161
|
+
) : (
|
|
162
|
+
<Message warning>
|
|
163
|
+
The items you tried to add were already added
|
|
164
|
+
</Message>
|
|
165
|
+
)}
|
|
166
|
+
<CclButton
|
|
167
|
+
mode="filled"
|
|
168
|
+
to={`/${intl.locale}/cart`}
|
|
169
|
+
style={{ width: '100%' }}
|
|
170
|
+
>
|
|
171
|
+
Go to cart
|
|
172
|
+
</CclButton>
|
|
173
|
+
</Popup>
|
|
174
|
+
<Link
|
|
175
|
+
to={`/${intl.locale}/cart`}
|
|
176
|
+
className="header-login-link"
|
|
177
|
+
ref={cart_icon_ref}
|
|
178
|
+
>
|
|
179
|
+
<FontAwesomeIcon
|
|
180
|
+
icon={['fas', 'shopping-cart']}
|
|
181
|
+
style={{ marginRight: '0.25rem', maxWidth: '1.5rem' }}
|
|
182
|
+
/>
|
|
183
|
+
<strong>{cartState?.items?.length}</strong>
|
|
184
|
+
</Link>
|
|
185
|
+
</>
|
|
186
|
+
)
|
|
187
|
+
);
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
export default CartIconCounter;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { onlyInLeft, CartIconCounter } from './CartIconCounter';
|
|
2
|
+
import Enzyme from 'enzyme';
|
|
3
|
+
import Adapter from 'enzyme-adapter-react-16';
|
|
4
|
+
import renderer from 'react-test-renderer';
|
|
5
|
+
import { MemoryRouter } from 'react-router-dom';
|
|
6
|
+
import { Provider } from 'react-intl-redux';
|
|
7
|
+
import configureStore from 'redux-mock-store';
|
|
8
|
+
Enzyme.configure({ adapter: new Adapter() });
|
|
9
|
+
|
|
10
|
+
describe('onlyInLeft', () => {
|
|
11
|
+
// Returns an array with items that are only in the left array, based on a compare function
|
|
12
|
+
it('should return an array with items that are only in the left array', () => {
|
|
13
|
+
const left = [
|
|
14
|
+
{ id: 1, name: 'Item 1' },
|
|
15
|
+
{ id: 2, name: 'Item 2' },
|
|
16
|
+
{ id: 3, name: 'Item 3' },
|
|
17
|
+
];
|
|
18
|
+
const right = [
|
|
19
|
+
{ id: 2, name: 'Item 2' },
|
|
20
|
+
{ id: 4, name: 'Item 4' },
|
|
21
|
+
];
|
|
22
|
+
const compareFunction = (leftValue, rightValue) =>
|
|
23
|
+
leftValue.id === rightValue.id;
|
|
24
|
+
|
|
25
|
+
const result = onlyInLeft(left, right, compareFunction);
|
|
26
|
+
|
|
27
|
+
expect(result).toEqual([
|
|
28
|
+
{ id: 1, name: 'Item 1' },
|
|
29
|
+
{ id: 3, name: 'Item 3' },
|
|
30
|
+
]);
|
|
31
|
+
});
|
|
32
|
+
// Should return an array with items that are only in the left array, based on a compare function
|
|
33
|
+
it('should return an array with items that are only in the left array', () => {
|
|
34
|
+
const left = [
|
|
35
|
+
{ id: 1, name: 'Item 1' },
|
|
36
|
+
{ id: 2, name: 'Item 2' },
|
|
37
|
+
{ id: 3, name: 'Item 3' },
|
|
38
|
+
];
|
|
39
|
+
const right = [
|
|
40
|
+
{ id: 2, name: 'Item 2' },
|
|
41
|
+
{ id: 4, name: 'Item 4' },
|
|
42
|
+
];
|
|
43
|
+
const compareFunction = (leftValue, rightValue) =>
|
|
44
|
+
leftValue.id === rightValue.id;
|
|
45
|
+
|
|
46
|
+
const result = onlyInLeft(left, right, compareFunction);
|
|
47
|
+
|
|
48
|
+
expect(result).toEqual([
|
|
49
|
+
{ id: 1, name: 'Item 1' },
|
|
50
|
+
{ id: 3, name: 'Item 3' },
|
|
51
|
+
]);
|
|
52
|
+
});
|
|
53
|
+
// should return an empty array if left array is empty
|
|
54
|
+
it('should return an empty array when left array is empty', () => {
|
|
55
|
+
const left = [];
|
|
56
|
+
const right = [
|
|
57
|
+
{ id: 2, name: 'Item 2' },
|
|
58
|
+
{ id: 4, name: 'Item 4' },
|
|
59
|
+
];
|
|
60
|
+
const compareFunction = (leftValue, rightValue) =>
|
|
61
|
+
leftValue.id === rightValue.id;
|
|
62
|
+
|
|
63
|
+
const result = onlyInLeft(left, right, compareFunction);
|
|
64
|
+
|
|
65
|
+
expect(result).toEqual([]);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// should return the left array if right array is empty
|
|
69
|
+
it('should return the left array when the right array is empty', () => {
|
|
70
|
+
const left = [
|
|
71
|
+
{ id: 1, name: 'Item 1' },
|
|
72
|
+
{ id: 2, name: 'Item 2' },
|
|
73
|
+
{ id: 3, name: 'Item 3' },
|
|
74
|
+
];
|
|
75
|
+
const right = [];
|
|
76
|
+
const compareFunction = (leftValue, rightValue) =>
|
|
77
|
+
leftValue.id === rightValue.id;
|
|
78
|
+
|
|
79
|
+
const result = onlyInLeft(left, right, compareFunction);
|
|
80
|
+
|
|
81
|
+
expect(result).toEqual(left);
|
|
82
|
+
});
|
|
83
|
+
// should return an array with items that are only in the left array
|
|
84
|
+
it('should return an array with items that are only in the left array', () => {
|
|
85
|
+
const left = [
|
|
86
|
+
{ id: 1, name: 'Item 1' },
|
|
87
|
+
{ id: 2, name: 'Item 2' },
|
|
88
|
+
{ id: 3, name: 'Item 3' },
|
|
89
|
+
];
|
|
90
|
+
const right = [
|
|
91
|
+
{ id: 2, name: 'Item 2' },
|
|
92
|
+
{ id: 4, name: 'Item 4' },
|
|
93
|
+
];
|
|
94
|
+
const compareFunction = (leftValue, rightValue) =>
|
|
95
|
+
leftValue.id === rightValue.id;
|
|
96
|
+
|
|
97
|
+
const result = onlyInLeft(left, right, compareFunction);
|
|
98
|
+
|
|
99
|
+
expect(result).toEqual([
|
|
100
|
+
{ id: 1, name: 'Item 1' },
|
|
101
|
+
{ id: 3, name: 'Item 3' },
|
|
102
|
+
]);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const mockStore = configureStore();
|
|
107
|
+
describe('CartIconCounter', () => {
|
|
108
|
+
// Cart icon displays number of items in cart
|
|
109
|
+
it('should display the correct number of items in the cart', () => {
|
|
110
|
+
const store = mockStore({
|
|
111
|
+
cart_items: {
|
|
112
|
+
items: ['one', 'two', 'three'],
|
|
113
|
+
},
|
|
114
|
+
users: {
|
|
115
|
+
user: {
|
|
116
|
+
id: 'g678gfd678',
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
datasetTimeseries: {
|
|
120
|
+
datasets: [],
|
|
121
|
+
},
|
|
122
|
+
intl: {
|
|
123
|
+
locale: 'en',
|
|
124
|
+
messages: {},
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
// Render the component
|
|
128
|
+
const component = renderer.create(
|
|
129
|
+
<Provider store={store}>
|
|
130
|
+
<MemoryRouter>
|
|
131
|
+
<CartIconCounter />
|
|
132
|
+
</MemoryRouter>
|
|
133
|
+
</Provider>,
|
|
134
|
+
);
|
|
135
|
+
// const component = mount(<CartIconCounter />);
|
|
136
|
+
|
|
137
|
+
// Expect the component toBeDefined
|
|
138
|
+
expect(component).toBeDefined();
|
|
139
|
+
expect(component.toJSON().children[0].children[0]).toEqual('3');
|
|
140
|
+
});
|
|
141
|
+
});
|
|
@@ -2,28 +2,29 @@
|
|
|
2
2
|
* Footer component.
|
|
3
3
|
* @module components/theme/Footer/Footer
|
|
4
4
|
*/
|
|
5
|
-
|
|
6
5
|
import React, { Component } from 'react';
|
|
7
6
|
import { defineMessages, injectIntl } from 'react-intl';
|
|
7
|
+
import { connect } from 'react-redux';
|
|
8
|
+
import { Link } from 'react-router-dom';
|
|
9
|
+
import { ReactSVG } from 'react-svg';
|
|
10
|
+
import { toast } from 'react-toastify';
|
|
11
|
+
import { compose } from 'redux';
|
|
8
12
|
|
|
13
|
+
import { Toast } from '@plone/volto/components';
|
|
9
14
|
import AtmosphereImage from '@eeacms/volto-clms-theme/../theme/clms/img/ccl-icon-atmosphere.svg';
|
|
10
|
-
import CclFooterColumn from '@eeacms/volto-clms-theme/components/CclFooterColumn/CclFooterColumn';
|
|
11
|
-
import CclFooterMenu from '@eeacms/volto-clms-theme/components/CclTopMainMenu/CclFooterMenu';
|
|
12
15
|
import ClimateImage from '@eeacms/volto-clms-theme/../theme/clms/img/ccl-icon-climate.svg';
|
|
13
|
-
import CopernicusImage from '@eeacms/volto-clms-theme/../theme/clms/img/copernicus_eu_logo_white.svg';
|
|
14
|
-
import ECImage from '@eeacms/volto-clms-theme/../theme/clms/img/eea-logo.svg';
|
|
15
|
-
import EEAImage from '@eeacms/volto-clms-theme/../theme/clms/img/ec-logo-white.svg';
|
|
16
16
|
import EmergencyImage from '@eeacms/volto-clms-theme/../theme/clms/img/ccl-icon-emergency.svg';
|
|
17
17
|
import LandImage from '@eeacms/volto-clms-theme/../theme/clms/img/ccl-icon-land.svg';
|
|
18
|
-
import { Link } from 'react-router-dom';
|
|
19
18
|
import MarineImage from '@eeacms/volto-clms-theme/../theme/clms/img/ccl-icon-marine.svg';
|
|
20
|
-
import { ReactSVG } from 'react-svg';
|
|
21
19
|
import SecurityImage from '@eeacms/volto-clms-theme/../theme/clms/img/ccl-icon-security.svg';
|
|
22
|
-
import
|
|
23
|
-
import
|
|
24
|
-
import
|
|
20
|
+
import CopernicusImage from '@eeacms/volto-clms-theme/../theme/clms/img/copernicus_eu_logo_white.svg';
|
|
21
|
+
import EEAImage from '@eeacms/volto-clms-theme/../theme/clms/img/ec-logo-white.svg';
|
|
22
|
+
import ECImage from '@eeacms/volto-clms-theme/../theme/clms/img/eea-logo.svg';
|
|
23
|
+
import CclFooterColumn from '@eeacms/volto-clms-theme/components/CclFooterColumn/CclFooterColumn';
|
|
24
|
+
import CclFooterMenu from '@eeacms/volto-clms-theme/components/CclTopMainMenu/CclFooterMenu';
|
|
25
|
+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
25
26
|
import { subscribeTo } from '../../../../../actions';
|
|
26
|
-
import {
|
|
27
|
+
import { faLinkedin } from '@fortawesome/free-brands-svg-icons';
|
|
27
28
|
import validator from 'validator';
|
|
28
29
|
|
|
29
30
|
const messages = defineMessages({
|
|
@@ -370,7 +371,7 @@ class Footer extends Component {
|
|
|
370
371
|
</CclFooterColumn>
|
|
371
372
|
<div className="ccl-footer-col">
|
|
372
373
|
<div className="ccl-footer-form-title">
|
|
373
|
-
Sign up to
|
|
374
|
+
Sign up to CLMS updates
|
|
374
375
|
</div>
|
|
375
376
|
<form action="" className="ccl-footer-form">
|
|
376
377
|
<div className="ccl-footer-newsletter">
|
|
@@ -435,7 +436,7 @@ class Footer extends Component {
|
|
|
435
436
|
</div>
|
|
436
437
|
<div className="ccl-footer-social">
|
|
437
438
|
<a
|
|
438
|
-
href="https://www.youtube.com/
|
|
439
|
+
href="https://www.youtube.com/playlist?list=PL1_QSyumTz7CZQEZ-1foOTeTOelKDQmxu"
|
|
439
440
|
target="_blank"
|
|
440
441
|
rel="noreferrer"
|
|
441
442
|
aria-label="Youtube"
|
|
@@ -458,8 +459,17 @@ class Footer extends Component {
|
|
|
458
459
|
>
|
|
459
460
|
<span className="ccl-icon-facebook"></span>
|
|
460
461
|
</a>
|
|
462
|
+
<a
|
|
463
|
+
href="https://www.linkedin.com/showcase/copernicus-eea"
|
|
464
|
+
target="_blank"
|
|
465
|
+
rel="noreferrer"
|
|
466
|
+
aria-label="Linkedin"
|
|
467
|
+
>
|
|
468
|
+
<FontAwesomeIcon icon={faLinkedin} />
|
|
469
|
+
</a>
|
|
461
470
|
</div>
|
|
462
471
|
</div>
|
|
472
|
+
|
|
463
473
|
<CclFooterColumn title={'Implemented by'}>
|
|
464
474
|
<div className="footer-implemented footer-implemented-eea">
|
|
465
475
|
<a
|
|
@@ -497,9 +507,17 @@ class Footer extends Component {
|
|
|
497
507
|
wrapper="div"
|
|
498
508
|
/>
|
|
499
509
|
</a>
|
|
500
|
-
<
|
|
501
|
-
|
|
502
|
-
|
|
510
|
+
<a
|
|
511
|
+
href="https://joint-research-centre.ec.europa.eu/index_en"
|
|
512
|
+
target="_blank"
|
|
513
|
+
rel="noreferrer"
|
|
514
|
+
>
|
|
515
|
+
<span>
|
|
516
|
+
{this.props.intl.formatMessage(
|
|
517
|
+
messages.jointResearchCenter,
|
|
518
|
+
)}
|
|
519
|
+
</span>
|
|
520
|
+
</a>
|
|
503
521
|
</div>
|
|
504
522
|
{/* <div className="ccl-footer-col-title">
|
|
505
523
|
{intl.formatMessage(messages.expertSupportProvidedBy)}
|
|
@@ -6,12 +6,9 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { FormattedMessage, injectIntl } from 'react-intl';
|
|
9
|
-
import { Logo, Navigation, SearchWidget
|
|
10
|
-
import React, { Component
|
|
11
|
-
import { connect,
|
|
12
|
-
import { Popup, Segment, Divider, Message } from 'semantic-ui-react';
|
|
13
|
-
import CclButton from '@eeacms/volto-clms-theme/components/CclButton/CclButton';
|
|
14
|
-
import clearSVG from '@plone/volto/icons/clear.svg';
|
|
9
|
+
import { Logo, Navigation, SearchWidget } from '@plone/volto/components';
|
|
10
|
+
import React, { Component } from 'react';
|
|
11
|
+
import { connect, useSelector } from 'react-redux';
|
|
15
12
|
|
|
16
13
|
import { BodyClass } from '@plone/volto/helpers';
|
|
17
14
|
// IMPORT isnt nedded until translations are created
|
|
@@ -26,91 +23,7 @@ import { getCartItems } from '@eeacms/volto-clms-utils/actions';
|
|
|
26
23
|
import { getUser } from '@plone/volto/actions';
|
|
27
24
|
import jwtDecode from 'jwt-decode';
|
|
28
25
|
|
|
29
|
-
|
|
30
|
-
const cartState = useSelector((state) => state.cart_items);
|
|
31
|
-
const cartState_ref = useRef(cartState);
|
|
32
|
-
const cart_icon_ref = React.useRef();
|
|
33
|
-
const intl = useSelector((state) => state.intl);
|
|
34
|
-
const user_id = useSelector((state) => state.users.user.id);
|
|
35
|
-
const [showPopup, setshowPopup] = useState(false);
|
|
36
|
-
const [cartDiff, setCartDiff] = useState(0);
|
|
37
|
-
const dispatch = useDispatch();
|
|
38
|
-
useEffect(() => {
|
|
39
|
-
dispatch(getCartItems(user_id));
|
|
40
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
41
|
-
}, [user_id]);
|
|
42
|
-
useEffect(() => {
|
|
43
|
-
if (
|
|
44
|
-
cartState_ref.current.set.loading &&
|
|
45
|
-
cartState.set.loaded &&
|
|
46
|
-
cartState.items.length >= cartState_ref.current.items.length
|
|
47
|
-
) {
|
|
48
|
-
setCartDiff(cartState.items.length - cartState_ref.current.items.length);
|
|
49
|
-
window.scrollTo({
|
|
50
|
-
top: 0,
|
|
51
|
-
left: 0,
|
|
52
|
-
behavior: 'smooth',
|
|
53
|
-
});
|
|
54
|
-
!showPopup && setTimeout(() => setshowPopup(true), 900);
|
|
55
|
-
setTimeout(() => setshowPopup(false), 11000);
|
|
56
|
-
}
|
|
57
|
-
cartState_ref.current = cartState;
|
|
58
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
59
|
-
}, [cartState]);
|
|
60
|
-
return (
|
|
61
|
-
cartState.items && (
|
|
62
|
-
<>
|
|
63
|
-
<Popup
|
|
64
|
-
context={cart_icon_ref}
|
|
65
|
-
open={showPopup}
|
|
66
|
-
position="bottom center"
|
|
67
|
-
>
|
|
68
|
-
<Segment
|
|
69
|
-
attached="top"
|
|
70
|
-
style={{ padding: 0, display: 'flex', justifyContent: 'flex-end' }}
|
|
71
|
-
>
|
|
72
|
-
<Icon
|
|
73
|
-
onClick={() => setshowPopup(false)}
|
|
74
|
-
name={clearSVG}
|
|
75
|
-
size={20}
|
|
76
|
-
style={{ cursor: 'pointer' }}
|
|
77
|
-
/>
|
|
78
|
-
</Segment>
|
|
79
|
-
<Divider horizontal style={{ margin: 0 }}>
|
|
80
|
-
My cart
|
|
81
|
-
</Divider>
|
|
82
|
-
{cartDiff > 0 ? (
|
|
83
|
-
<Message positive>
|
|
84
|
-
You added <strong>{cartDiff} new items</strong> to the cart
|
|
85
|
-
</Message>
|
|
86
|
-
) : (
|
|
87
|
-
<Message warning>
|
|
88
|
-
The items you tried to add were already added
|
|
89
|
-
</Message>
|
|
90
|
-
)}
|
|
91
|
-
<CclButton
|
|
92
|
-
mode="filled"
|
|
93
|
-
to={`/${intl.locale}/cart`}
|
|
94
|
-
style={{ width: '100%' }}
|
|
95
|
-
>
|
|
96
|
-
Go to cart
|
|
97
|
-
</CclButton>
|
|
98
|
-
</Popup>
|
|
99
|
-
<Link
|
|
100
|
-
to={`/${intl.locale}/cart`}
|
|
101
|
-
className="header-login-link"
|
|
102
|
-
ref={cart_icon_ref}
|
|
103
|
-
>
|
|
104
|
-
<FontAwesomeIcon
|
|
105
|
-
icon={['fas', 'shopping-cart']}
|
|
106
|
-
style={{ marginRight: '0.25rem', maxWidth: '1.5rem' }}
|
|
107
|
-
/>
|
|
108
|
-
<strong>{cartState?.items?.length}</strong>
|
|
109
|
-
</Link>
|
|
110
|
-
</>
|
|
111
|
-
)
|
|
112
|
-
);
|
|
113
|
-
};
|
|
26
|
+
import CartIconCounter from '@eeacms/volto-clms-theme/components/CartIconCounter/CartIconCounter';
|
|
114
27
|
|
|
115
28
|
const HeaderDropdown = ({ user }) => {
|
|
116
29
|
const intl = useSelector((state) => state.intl);
|
|
@@ -27,18 +27,18 @@ hr {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
hr
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
+ body:not(.contenttype-lrf:not(.section-cart):not(.section-profile):not(.section-newsletter-notification-subscription):not(.section-newsletter-notification-unsubscription):not(.section-logout))
|
|
31
|
+
main {
|
|
32
32
|
margin-bottom: 2rem;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
body:not(.contenttype-lrf:not(.section-cart):not(.section-profile):not(.section-newsletter-notification-subscription):not(.section-newsletter-notification-unsubscription):not(.section-logout))
|
|
36
|
-
|
|
36
|
+
main:not(.map-main) {
|
|
37
37
|
min-height: 70vh;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
.map-body:not(.contenttype-lrf:not(.section-cart):not(.section-profile):not(.section-newsletter-notification-subscription):not(.section-newsletter-notification-unsubscription):not(.section-logout))
|
|
41
|
-
|
|
41
|
+
main {
|
|
42
42
|
margin-bottom: 0;
|
|
43
43
|
}
|
|
44
44
|
|
|
@@ -169,77 +169,77 @@ body.contenttype-lrf:not(.section-cart):not(.profilecart) main .ccl-container {
|
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
body:not(.contenttype-lrf:not(.section-cart):not(.section-profile))
|
|
172
|
-
|
|
173
|
-
|
|
172
|
+
main
|
|
173
|
+
.ccl-container {
|
|
174
174
|
max-width: 100%;
|
|
175
175
|
padding: 0;
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
body:not(.contenttype-lrf:not(.section-cart):not(.section-profile))
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
179
|
+
main
|
|
180
|
+
.ccl-container
|
|
181
|
+
.cont-w-25 {
|
|
182
182
|
max-width: 100%;
|
|
183
183
|
flex: 0 0 100%;
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
body:not(.contenttype-lrf:not(.section-cart):not(.section-profile))
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
187
|
+
main
|
|
188
|
+
.ccl-container
|
|
189
|
+
.cont-w-50 {
|
|
190
190
|
max-width: 100%;
|
|
191
191
|
flex: 0 0 100%;
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
body:not(.contenttype-lrf:not(.section-cart):not(.section-profile))
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
195
|
+
main
|
|
196
|
+
.ccl-container
|
|
197
|
+
.cont-w-75 {
|
|
198
198
|
max-width: 100%;
|
|
199
199
|
flex: 0 0 100%;
|
|
200
200
|
}
|
|
201
201
|
@media (min-width: 576px) {
|
|
202
202
|
body:not(.contenttype-lrf:not(.section-cart):not(.section-profile))
|
|
203
|
-
|
|
204
|
-
|
|
203
|
+
main
|
|
204
|
+
.ccl-container {
|
|
205
205
|
max-width: 540px;
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
208
|
@media (min-width: 768px) {
|
|
209
209
|
body:not(.contenttype-lrf:not(.section-cart):not(.section-profile))
|
|
210
|
-
|
|
211
|
-
|
|
210
|
+
main
|
|
211
|
+
.ccl-container {
|
|
212
212
|
max-width: 720px;
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
body:not(.contenttype-lrf:not(.section-cart):not(.section-profile))
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
216
|
+
main
|
|
217
|
+
.ccl-container
|
|
218
|
+
.cont-w-25 {
|
|
219
219
|
max-width: 30%;
|
|
220
220
|
flex: 0 0 30%;
|
|
221
221
|
}
|
|
222
222
|
|
|
223
223
|
body:not(.contenttype-lrf:not(.section-cart):not(.section-profile))
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
224
|
+
main
|
|
225
|
+
.ccl-container
|
|
226
|
+
.cont-w-50 {
|
|
227
227
|
max-width: 50%;
|
|
228
228
|
flex: 0 0 50%;
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
body:not(.contenttype-lrf:not(.section-cart):not(.section-profile))
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
232
|
+
main
|
|
233
|
+
.ccl-container
|
|
234
|
+
.cont-w-75 {
|
|
235
235
|
max-width: 70%;
|
|
236
236
|
flex: 0 0 70%;
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
body:not(.contenttype-lrf:not(.section-cart):not(.section-profile))
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
240
|
+
main
|
|
241
|
+
.ccl-container
|
|
242
|
+
.cont-o-1 {
|
|
243
243
|
order: 1;
|
|
244
244
|
}
|
|
245
245
|
|
|
@@ -253,30 +253,30 @@ body:not(.contenttype-lrf:not(.section-cart):not(.section-profile))
|
|
|
253
253
|
}
|
|
254
254
|
@media (min-width: 992px) {
|
|
255
255
|
body:not(.contenttype-lrf:not(.section-cart):not(.section-profile))
|
|
256
|
-
|
|
257
|
-
|
|
256
|
+
main
|
|
257
|
+
.ccl-container {
|
|
258
258
|
max-width: 960px;
|
|
259
259
|
}
|
|
260
260
|
}
|
|
261
261
|
@media (min-width: 1200px) {
|
|
262
262
|
body:not(.contenttype-lrf:not(.section-cart):not(.section-profile))
|
|
263
|
-
|
|
264
|
-
|
|
263
|
+
main
|
|
264
|
+
.ccl-container {
|
|
265
265
|
max-width: 1127px;
|
|
266
266
|
}
|
|
267
267
|
|
|
268
268
|
body:not(.contenttype-lrf:not(.section-cart):not(.section-profile))
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
269
|
+
main
|
|
270
|
+
.ccl-container
|
|
271
|
+
.cont-w-25 {
|
|
272
272
|
max-width: 25%;
|
|
273
273
|
flex: 0 0 25%;
|
|
274
274
|
}
|
|
275
275
|
|
|
276
276
|
body:not(.contenttype-lrf:not(.section-cart):not(.section-profile))
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
277
|
+
main
|
|
278
|
+
.ccl-container
|
|
279
|
+
.cont-w-75 {
|
|
280
280
|
max-width: 75%;
|
|
281
281
|
flex: 0 0 75%;
|
|
282
282
|
}
|
|
@@ -705,6 +705,23 @@ body:not(.contenttype-lrf:not(.section-cart):not(.section-profile))
|
|
|
705
705
|
margin: 0 auto !important;
|
|
706
706
|
}
|
|
707
707
|
|
|
708
|
+
.ui.fluid.card.u-card .extra.content a.ui.button {
|
|
709
|
+
display: inline-block;
|
|
710
|
+
padding: 0.3rem 1rem;
|
|
711
|
+
margin: 0;
|
|
712
|
+
appearance: none;
|
|
713
|
+
background: none;
|
|
714
|
+
color: @clmsGreen;
|
|
715
|
+
color: #a0b128;
|
|
716
|
+
cursor: pointer;
|
|
717
|
+
font-size: 0.85rem;
|
|
718
|
+
line-height: 1.5;
|
|
719
|
+
text-align: center;
|
|
720
|
+
text-decoration: none;
|
|
721
|
+
transition: all 0.3s ease-out;
|
|
722
|
+
vertical-align: middle;
|
|
723
|
+
}
|
|
724
|
+
|
|
708
725
|
/* Search */
|
|
709
726
|
.search-results {
|
|
710
727
|
margin-bottom: 2rem;
|