@eeacms/volto-marine-policy 2.0.27 → 2.0.29

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,6 +4,25 @@ 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
+ ### [2.0.29](https://github.com/eea/volto-marine-policy/compare/2.0.28...2.0.29) - 22 September 2025
8
+
9
+ #### :hammer_and_wrench: Others
10
+
11
+ - cleanup [laszlocseh - [`2d9cd7c`](https://github.com/eea/volto-marine-policy/commit/2d9cd7c4e37f36c92e0ecc604812cd7ee9703ec7)]
12
+ - add nonce to script-src [laszlocseh - [`a35b6f5`](https://github.com/eea/volto-marine-policy/commit/a35b6f5f31d9cdc7039ef43c52b0efb37397b679)]
13
+ ### [2.0.28](https://github.com/eea/volto-marine-policy/compare/2.0.27...2.0.28) - 12 September 2025
14
+
15
+ #### :house: Internal changes
16
+
17
+ - style: Automated code fix [eea-jenkins - [`cbf9e77`](https://github.com/eea/volto-marine-policy/commit/cbf9e77a6fe1d745288792ad83f463ef3c9bec41)]
18
+
19
+ #### :hammer_and_wrench: Others
20
+
21
+ - fix eslint [Laszlo Cseh - [`04b17b2`](https://github.com/eea/volto-marine-policy/commit/04b17b25c5dbbd9d8afd1caf74132e7fca642f39)]
22
+ - NISlistingView fix assign to total count [Laszlo Cseh - [`ef82759`](https://github.com/eea/volto-marine-policy/commit/ef827593308f779e420c4cc3b3b8cae025c1616f)]
23
+ - NISlistingView download button location [Laszlo Cseh - [`22153f4`](https://github.com/eea/volto-marine-policy/commit/22153f422e06f7fbaa7adfa45817cd8ce7199c5a)]
24
+ - NISlistingView fix paths [Laszlo Cseh - [`ffa946e`](https://github.com/eea/volto-marine-policy/commit/ffa946eb83d144ca461af43787faa48f71d545ee)]
25
+ - WiP Nis improvements [Laszlo Cseh - [`c66a5f6`](https://github.com/eea/volto-marine-policy/commit/c66a5f68d3c3d1bc8aa968c98aa12f409e931adc)]
7
26
  ### [2.0.27](https://github.com/eea/volto-marine-policy/compare/2.0.26...2.0.27) - 5 September 2025
8
27
 
9
28
  #### :hammer_and_wrench: Others
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-marine-policy",
3
- "version": "2.0.27",
3
+ "version": "2.0.29",
4
4
  "description": "@eeacms/volto-marine-policy: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: IDM2 A-Team",
@@ -1,49 +1,115 @@
1
- // import ProgressWorkflow from '@eeacms/volto-workflow-progress/ProgressWorkflow';
2
1
  import ProgressWorkflow from '@eeacms/volto-marine-policy/components/theme/ProgressWorkflow/ProgressWorkflow';
3
2
 
4
- // import { Link } from 'react-router-dom';
3
+ import qs from 'query-string';
5
4
  import PropTypes from 'prop-types';
6
5
  import './style.less';
7
6
  import { useState, useEffect } from 'react';
8
7
  import { useSelector } from 'react-redux';
9
8
  import { Checkbox } from 'semantic-ui-react';
10
- import { Button, Select } from 'semantic-ui-react';
9
+ import { Button, Select, Dimmer, Loader } from 'semantic-ui-react';
10
+
11
+ function normalizeQueryOperators(query) {
12
+ return query.map((q) => {
13
+ // if the operator starts with "paqo", replace with full
14
+ if (q.o && q.o.startsWith('paqo.')) {
15
+ const op = q.o.replace('paqo.', 'plone.app.querystring.operation.');
16
+ return { ...q, o: op };
17
+ }
18
+ return q;
19
+ });
20
+ }
21
+
22
+ async function getCurrentSearchItems() {
23
+ // parse querystring into an object
24
+ const parsed = qs.parse(window.location.search);
25
+
26
+ // decode the `query` param, which is itself a JSON string
27
+ let query = [];
28
+ if (parsed.query) {
29
+ try {
30
+ query = normalizeQueryOperators(JSON.parse(parsed.query));
31
+ } catch (e) {
32
+ // console.error('Invalid query param JSON', e);
33
+ }
34
+ }
35
+
36
+ // build payload
37
+ const payload = {
38
+ metadata_fields: '_all',
39
+ b_size: parsed.b_size ? parseInt(parsed.b_size, 10) : 25,
40
+ limit: parsed.limit ? parseInt(parsed.limit, 10) : 3000,
41
+ query,
42
+ sort_on: parsed.sort_on || 'effective',
43
+ sort_order: parsed.sort_order || 'ascending',
44
+ b_start: parsed.b_start ? parseInt(parsed.b_start, 10) : 0,
45
+ };
46
+
47
+ // call Plone
48
+ try {
49
+ const response = await fetch('/marine/++api++/@querystring-search', {
50
+ method: 'POST',
51
+ headers: {
52
+ 'Content-Type': 'application/json',
53
+ },
54
+ body: JSON.stringify(payload),
55
+ });
56
+ return response;
57
+ } catch (err) {
58
+ // console.error('Querystring search failed:', err);
59
+ }
60
+ }
11
61
 
12
62
  const NISListingView = ({ items, isEditMode }) => {
63
+ const [isLoading, setIsLoading] = useState(false);
13
64
  const [selectedItems, setSelectedItems] = useState([]);
65
+ const [itemsTotal, setItemsTotal] = useState(0);
14
66
  const [users, setUsers] = useState([]);
15
67
  const [assignee, setAssignee] = useState(null);
16
68
  const actions = useSelector((state) => state.actions.actions);
17
69
  const canEditPage = actions?.object?.some((action) => action.id === 'edit');
18
- // console.log('actions', actions);
19
70
 
20
71
  const toggleSelection = (id) => {
72
+ setItemsTotal(0);
21
73
  setSelectedItems((prev) =>
22
- prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id],
74
+ prev.includes(id)
75
+ ? prev.filter((x) => x !== id && x !== 'All')
76
+ : [...prev.filter((x) => x !== 'All'), id],
23
77
  );
24
78
  };
25
79
 
26
80
  const handleBulkAssign = () => {
81
+ setItemsTotal(0);
27
82
  onBulkAssign(selectedItems, assignee);
28
83
  setSelectedItems([]);
29
84
  setAssignee(null);
30
85
  };
31
86
 
87
+ const handleBulkAssignAll = async () => {
88
+ const items = await getCurrentSearchItems();
89
+ const itemsTotal = await items.json();
90
+ setItemsTotal(itemsTotal.items_total);
91
+ setSelectedItems(['All']);
92
+ };
93
+
32
94
  const onBulkAssign = async (ids, assignee) => {
33
- await fetch(`${window.location.origin}/marine/++api++/@bulk-assign`, {
34
- method: 'POST',
35
- headers: {
36
- 'Content-Type': 'application/json',
37
- Accept: 'application/json',
95
+ setIsLoading(true);
96
+ await fetch(
97
+ `${window.location.origin}/marine/++api++/@bulk-assign${window.location.search}`,
98
+ {
99
+ method: 'POST',
100
+ headers: {
101
+ 'Content-Type': 'application/json',
102
+ Accept: 'application/json',
103
+ },
104
+ credentials: 'include',
105
+ body: JSON.stringify({
106
+ items: ids,
107
+ assigned_to: assignee,
108
+ search: window.location.search,
109
+ }),
38
110
  },
39
- credentials: 'include',
40
- body: JSON.stringify({
41
- items: ids,
42
- assigned_to: assignee,
43
- }),
44
- });
111
+ );
45
112
 
46
- // const result = await res.json();
47
113
  window.location.reload();
48
114
  };
49
115
 
@@ -74,6 +140,37 @@ const NISListingView = ({ items, isEditMode }) => {
74
140
 
75
141
  return (
76
142
  <>
143
+ {isLoading && (
144
+ <Dimmer active inverted>
145
+ <Loader>Assigning...</Loader>
146
+ </Dimmer>
147
+ )}
148
+ {canEditPage && (
149
+ <div className="download-button-wrapper">
150
+ <Button
151
+ className="primary"
152
+ size="small"
153
+ onClick={handleBulkAssignAll}
154
+ >
155
+ <i className="ri-user-add-line"></i>Assign search results
156
+ </Button>
157
+ <div>
158
+ <a
159
+ href={`/marine/++api++${window.location.pathname.replace(
160
+ '/marine',
161
+ '',
162
+ )}/nis-export${window.location.search}`}
163
+ title="Download"
164
+ target="_blank"
165
+ rel="noopener"
166
+ className="ui button primary download-as-xls"
167
+ >
168
+ <i className="ri-file-download-line"></i>
169
+ Download search results
170
+ </a>
171
+ </div>
172
+ </div>
173
+ )}
77
174
  <table className="ui table">
78
175
  <thead>
79
176
  <tr>
@@ -82,28 +179,11 @@ const NISListingView = ({ items, isEditMode }) => {
82
179
  <th>Scientific name accepted</th>
83
180
  <th>Region</th>
84
181
  <th>Subregion</th>
182
+ <th>Country</th>
85
183
  <th>Status</th>
86
184
  <th>Group</th>
87
185
  <th>Assigned to</th>
88
- <th>
89
- {canEditPage && (
90
- <div>
91
- <a
92
- href={`/marine/++api++${window.location.pathname.replace(
93
- '/marine',
94
- '',
95
- )}/nis-export${window.location.search}`}
96
- title="Download"
97
- target="_blank"
98
- rel="noopener"
99
- className="ui button primary download-as-xls"
100
- >
101
- <i className="ri-file-download-line"></i>
102
- Download
103
- </a>
104
- </div>
105
- )}
106
- </th>
186
+ <th></th>
107
187
  </tr>
108
188
  </thead>
109
189
  <tbody>
@@ -114,6 +194,7 @@ const NISListingView = ({ items, isEditMode }) => {
114
194
  <td>{item.nis_scientificname_accepted}</td>
115
195
  <td>{item.nis_region}</td>
116
196
  <td>{item.nis_subregion}</td>
197
+ <td>{item.nis_country && item.nis_country.join(', ')}</td>
117
198
  <td>{item.nis_status}</td>
118
199
  <td>{item.nis_group}</td>
119
200
  <td>
@@ -163,8 +244,13 @@ const NISListingView = ({ items, isEditMode }) => {
163
244
  {selectedItems.length > 0 && (
164
245
  <div className="users-assign-container">
165
246
  <h4>
166
- Assign {selectedItems.length} item
167
- {selectedItems.length > 1 ? 's' : ''}
247
+ {itemsTotal > 0 &&
248
+ selectedItems[0] === 'All' &&
249
+ `Assign ${itemsTotal} search result items`}
250
+ {itemsTotal === 0 &&
251
+ `Assign ${selectedItems.length} selected item${
252
+ selectedItems.length > 1 ? 's' : ''
253
+ }`}
168
254
  </h4>
169
255
  <Select
170
256
  placeholder="Select expert"
@@ -11,6 +11,10 @@
11
11
  font-size: 0.9em;
12
12
  }
13
13
 
14
+ .ui.text.loader {
15
+ position: fixed;
16
+ }
17
+
14
18
  .ui.table tr {
15
19
  td,
16
20
  th {
@@ -145,3 +149,16 @@
145
149
  .download-as-xls {
146
150
  padding: 0.6em 0.5em !important;
147
151
  }
152
+
153
+ .download-button-wrapper {
154
+ display: flex;
155
+
156
+ button {
157
+ padding: 0.6em 0.5em !important;
158
+ font-size: 1em !important;
159
+
160
+ i {
161
+ margin-right: 5px;
162
+ }
163
+ }
164
+ }
package/src/index.js CHANGED
@@ -98,6 +98,14 @@ const applyConfig = (config) => {
98
98
  if (__SERVER__) {
99
99
  const installExpressMiddleware = require('./express-middleware').default;
100
100
  config = installExpressMiddleware(config);
101
+
102
+ // CSP Header
103
+ const devsource = __DEVELOPMENT__
104
+ ? ` http://localhost:${parseInt(process.env.PORT || '3000') + 1}`
105
+ : '';
106
+ config.settings.serverConfig.csp = {
107
+ 'script-src': `'self' {nonce}${devsource}`,
108
+ };
101
109
  }
102
110
 
103
111
  config.widgets.widget.text_align = TextAlignWidget;