@eeacms/volto-clms-theme 1.0.48 → 1.0.52

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.
Files changed (32) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +86 -0
  3. package/package.json +4 -2
  4. package/src/actions/datasets_by_uid/datasets_by_uid.js +20 -0
  5. package/src/actions/downloadtool/delete_downloadtool.js +3 -3
  6. package/src/actions/downloadtool/get_downloadtool.js +2 -2
  7. package/src/actions/downloadtool/post_downloadtool.js +3 -1
  8. package/src/actions/format_conversion_table/get_format_conversion_table.js +20 -0
  9. package/src/actions/index.js +12 -0
  10. package/src/actions/projections/get_projections.js +20 -0
  11. package/src/components/Blocks/CclTextLinkCarouselBlock/CclTextLinkCarouselView.jsx +3 -3
  12. package/src/components/Blocks/CustomTemplates/VoltoTabsBlock/CclCarouselView.jsx +10 -9
  13. package/src/components/CLMSDatasetDetailView/CLMSDatasetDetailView.jsx +1 -1
  14. package/src/components/CLMSDatasetDetailView/MetadataContent.jsx +57 -19
  15. package/src/components/CLMSDownloadCartView/CLMSCartContent.jsx +329 -0
  16. package/src/components/CLMSDownloadCartView/CLMSDownloadCartView.jsx +11 -235
  17. package/src/components/CLMSDownloadCartView/CLMSTasksInProgress.jsx +81 -61
  18. package/src/components/CLMSDownloadCartView/cartUtils.js +75 -0
  19. package/src/components/CLMSDownloadCartView/conversion.js +28 -0
  20. package/src/components/CclButton/CclButton.jsx +13 -6
  21. package/src/components/CclCard/CclCard.jsx +1 -1
  22. package/src/components/CclDownloadTable/CclDownloadTable.jsx +14 -7
  23. package/src/components/CclTab/CclTab.jsx +6 -3
  24. package/src/components/CclTab/CclTabs.jsx +17 -11
  25. package/src/components/Widgets/DownloadableFilesWidget.jsx +32 -5
  26. package/src/customizations/volto/components/theme/Header/Header.jsx +15 -15
  27. package/src/customizations/volto/components/theme/Navigation/Navigation.jsx +5 -5
  28. package/src/reducers/datasets_by_uid/datasets_by_uid.js +43 -0
  29. package/src/reducers/downloadtool/downloadtool_reducer.js +60 -2
  30. package/src/reducers/index.js +2 -0
  31. package/theme/clms/css/header.css +3 -1
  32. package/theme/clms/css/styles.less +9 -0
@@ -0,0 +1,329 @@
1
+ /* eslint-disable react-hooks/exhaustive-deps */
2
+ /**
3
+ * CLMSCartContent container.
4
+ * @module components/CLMSDownloadCartView/CLMSCartContent
5
+ */
6
+
7
+ import React, { useEffect, useState } from 'react';
8
+ import {
9
+ getCartObjectFromMapviewer,
10
+ getCartObjectFromPrepackaged,
11
+ getDownloadToolPostBody,
12
+ } from './cartUtils';
13
+ import {
14
+ getDatasetsByUid,
15
+ getDownloadtool,
16
+ getFormatConversionTable,
17
+ getProjections,
18
+ postDownloadtool,
19
+ } from '../../actions';
20
+ import { useDispatch, useSelector } from 'react-redux';
21
+
22
+ import { CART_SESSION_KEY } from '@eeacms/volto-clms-utils/cart/useCartState';
23
+ import CclButton from '@eeacms/volto-clms-theme/components/CclButton/CclButton';
24
+ import { Checkbox } from 'semantic-ui-react';
25
+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
26
+ import { Select } from 'semantic-ui-react';
27
+ import { cleanDuplicatesEntries } from '@eeacms/volto-clms-utils/utils';
28
+ import { getAvailableConversion } from './conversion';
29
+ import useCartState from '@eeacms/volto-clms-utils/cart/useCartState';
30
+
31
+ const CLMSCartContent = (props) => {
32
+ const dispatch = useDispatch();
33
+ const { cart, removeCartItem } = useCartState();
34
+ const [cartSelection, setCartSelection] = useState([]);
35
+ const post_download_in_progress = useSelector(
36
+ (state) => state.downloadtool.post_download_in_progress,
37
+ );
38
+ const user_id = useSelector((state) => state.users.user.id);
39
+ const datasets = useSelector((state) => state.datasetsByUid.datasets.items);
40
+ const formatConversionTable = useSelector(
41
+ (state) => state.downloadtool.format_conversion_table_in_progress,
42
+ );
43
+ const projections = useSelector(
44
+ (state) => state.downloadtool.projections_in_progress,
45
+ );
46
+
47
+ const [cartItems, setCartItems] = useState([]);
48
+ const [localSessionCart, setLocalSessionCart] = useState([]);
49
+
50
+ useEffect(() => {
51
+ dispatch(getProjections());
52
+ dispatch(getFormatConversionTable());
53
+ }, [dispatch]);
54
+
55
+ useEffect(() => {
56
+ const CART_SESSION_USER_KEY = CART_SESSION_KEY.concat(`_${user_id}`);
57
+ setLocalSessionCart(
58
+ JSON.parse(localStorage.getItem(CART_SESSION_USER_KEY)) || [],
59
+ );
60
+ }, [user_id]);
61
+
62
+ useEffect(() => {
63
+ if (localSessionCart?.length !== 0) {
64
+ const uidsList = [
65
+ ...new Set(localSessionCart.map((item) => item.UID || item.id)),
66
+ ];
67
+ dispatch(getDatasetsByUid(uidsList));
68
+ }
69
+ }, [localSessionCart, dispatch]);
70
+
71
+ useEffect(() => {
72
+ if (datasets?.length > 0) {
73
+ concatRequestedCartItem();
74
+ }
75
+ }, [datasets]);
76
+
77
+ function concatRequestedCartItem() {
78
+ localSessionCart.forEach((localItem) => {
79
+ const requestedItem = datasets.find(
80
+ (requestedItem) => requestedItem.UID === localItem.UID,
81
+ );
82
+ if (requestedItem) {
83
+ const file_data = requestedItem?.downloadable_files?.items.find(
84
+ (item) => item['@id'] === localItem.file_id,
85
+ );
86
+ if (file_data) {
87
+ cartItems.push(
88
+ getCartObjectFromPrepackaged(file_data, requestedItem),
89
+ );
90
+ setCartItems(cleanDuplicatesEntries(cartItems));
91
+ } else if (localItem.area) {
92
+ cartItems.push(getCartObjectFromMapviewer(localItem, requestedItem));
93
+ setCartItems(cleanDuplicatesEntries(cartItems));
94
+ }
95
+ }
96
+ });
97
+ }
98
+
99
+ const selectAllCart = (checked) => {
100
+ if (checked) {
101
+ setCartSelection(
102
+ cartItems
103
+ .filter((item) => item.task_in_progress === false)
104
+ .map((item, key) => item.unique_id),
105
+ );
106
+ } else {
107
+ setCartSelection([]);
108
+ }
109
+ };
110
+
111
+ const selectCart = (id, checked) => {
112
+ if (checked) setCartSelection(cartSelection.concat(id));
113
+ else setCartSelection(cartSelection.filter((arr_id) => arr_id !== id));
114
+ };
115
+
116
+ const getSelectedCartItems = () => {
117
+ return cartItems.filter(
118
+ (item) => cartSelection.indexOf(item.unique_id) > -1,
119
+ );
120
+ };
121
+
122
+ // eslint-disable-next-line react-hooks/exhaustive-deps
123
+ const setCartItemInProgress = (in_progress_unique_ids) => {
124
+ let started_processing_items = cartItems.filter((r) =>
125
+ in_progress_unique_ids.includes(r['unique_id']),
126
+ );
127
+ started_processing_items.forEach((item) => {
128
+ if (item['unique_id']) {
129
+ removeCartItem(item['unique_id'], user_id);
130
+ dispatch(getFormatConversionTable());
131
+ dispatch(getDownloadtool());
132
+ }
133
+ });
134
+ };
135
+
136
+ useEffect(() => {
137
+ setCartItemInProgress(post_download_in_progress['unique_ids']);
138
+ // eslint-disable-next-line react-hooks/exhaustive-deps
139
+ }, [post_download_in_progress]);
140
+
141
+ useEffect(() => {
142
+ const array_ids = cart?.map((item) => item.unique_id);
143
+ const newCart = cartItems.filter((item) =>
144
+ array_ids.includes(item.unique_id),
145
+ );
146
+ setCartItems(newCart);
147
+ }, [cart]);
148
+
149
+ function startDownloading() {
150
+ let selectedItems = getSelectedCartItems();
151
+ const body = getDownloadToolPostBody(selectedItems);
152
+ const unique_ids = selectedItems.map((item) => item.unique_id);
153
+ dispatch(postDownloadtool(body, unique_ids));
154
+ }
155
+ const setProjectionValue = (unique_id, value) => {
156
+ const objIndex = cartItems.findIndex((obj) => obj.unique_id === unique_id);
157
+ cartItems[objIndex].projection = value;
158
+ setCartItems([...cartItems]);
159
+ };
160
+ return (
161
+ <>
162
+ {localSessionCart?.length !== 0 ? (
163
+ <div className="custom-table cart-table">
164
+ <h2>My cart</h2>
165
+ <table>
166
+ <thead>
167
+ <tr>
168
+ <th className="table-th-warning"></th>
169
+ <th className="table-th-checkbox">
170
+ <div className="ccl-form">
171
+ <div className="ccl-form-group">
172
+ <Checkbox
173
+ onChange={(e, data) => selectAllCart(data.checked)}
174
+ checked={
175
+ cartItems
176
+ ? cartItems
177
+ .filter(
178
+ (item) => item.task_in_progress === false,
179
+ )
180
+ .map((item, key) => item.unique_id)
181
+ .every(function (val) {
182
+ return cartSelection.indexOf(val) !== -1;
183
+ })
184
+ : false
185
+ }
186
+ />
187
+ </div>
188
+ </div>
189
+ </th>
190
+ <th>Name</th>
191
+ <th>Source</th>
192
+ <th>Area</th>
193
+ {/* <th>Year</th>
194
+ <th>Resolution</th> */}
195
+ <th>Type</th>
196
+ <th>Format</th>
197
+ <th>Projection</th>
198
+ <th>Version</th>
199
+ <th>Size</th>
200
+ <th></th>
201
+ </tr>
202
+ </thead>
203
+ <tbody>
204
+ {cartItems &&
205
+ cartItems.map((item, key) => (
206
+ <tr
207
+ key={key}
208
+ style={
209
+ item.task_in_progress
210
+ ? { opacity: 0.5, backgroundColor: '#f5f5f5' }
211
+ : {}
212
+ }
213
+ >
214
+ <td className="table-td-warning hidden-warning">
215
+ {!!item.warning && (
216
+ <span
217
+ className="info-icon"
218
+ tooltip={item.warning}
219
+ direction="up"
220
+ >
221
+ <FontAwesomeIcon
222
+ icon={['fas', 'exclamation-triangle']}
223
+ />
224
+ </span>
225
+ )}
226
+ </td>
227
+ <td className="table-td-checkbox">
228
+ <div className="ccl-form">
229
+ <div className="ccl-form-group">
230
+ <Checkbox
231
+ onChange={(e, data) =>
232
+ selectCart(item.unique_id, data.checked)
233
+ }
234
+ checked={cartSelection.includes(item.unique_id)}
235
+ disabled={item.task_in_progress}
236
+ />
237
+ </div>
238
+ </div>
239
+ </td>
240
+ <td>{item.name || '-'}</td>
241
+ <td>{item.source || '-'}</td>
242
+ <td>{item.area.type || '-'}</td>
243
+ {/* <td>{item.year || '-'}</td>
244
+ <td>{item.resolution || '-'}</td> */}
245
+ <td>
246
+ <span className={'tag tag-' + item?.type?.toLowerCase()}>
247
+ {item.type || '-'}
248
+ </span>
249
+ </td>
250
+ <td className="table-td-format">
251
+ {!item.file_id ? (
252
+ <Select
253
+ placeholder="Select format"
254
+ value={item.format?.token || item.format}
255
+ options={getAvailableConversion(
256
+ formatConversionTable,
257
+ item.format?.token || item.format,
258
+ )}
259
+ onChange={(e, data) => {
260
+ const objIndex = cartItems.findIndex(
261
+ (obj) => obj.unique_id === item.unique_id,
262
+ );
263
+ cartItems[objIndex].format = data.value;
264
+ setCartItems([...cartItems]);
265
+ }}
266
+ />
267
+ ) : (
268
+ item.format?.token || item.format
269
+ )}
270
+ </td>
271
+ <td className="table-td-projections">
272
+ {!item.file_id ? (
273
+ <Select
274
+ placeholder="Select projection"
275
+ value={
276
+ item.projection ||
277
+ setProjectionValue(item.unique_id, projections[0])
278
+ }
279
+ options={projections.map((item) => {
280
+ return {
281
+ key: item,
282
+ value: item,
283
+ text: item,
284
+ };
285
+ })}
286
+ onChange={(e, data) => {
287
+ setProjectionValue(item.unique_id, data.value);
288
+ }}
289
+ />
290
+ ) : (
291
+ item.projection
292
+ )}
293
+ </td>
294
+ <td>{item.version}</td>
295
+ <td>{item.size}</td>
296
+ <td>
297
+ {item.task_in_progress ? (
298
+ <FontAwesomeIcon icon="spinner" spin />
299
+ ) : (
300
+ <FontAwesomeIcon
301
+ icon={['fas', 'trash']}
302
+ style={{ cursor: 'pointer' }}
303
+ onClick={() => {
304
+ removeCartItem(item.unique_id);
305
+ }}
306
+ />
307
+ )}
308
+ </td>
309
+ </tr>
310
+ ))}
311
+ </tbody>
312
+ </table>
313
+ </div>
314
+ ) : (
315
+ <h2 style={{ textAlign: 'center' }}>Empty cart</h2>
316
+ )}
317
+ {localSessionCart?.length !== 0 && (
318
+ <CclButton
319
+ onClick={() => startDownloading()}
320
+ disabled={cartSelection.length === 0}
321
+ >
322
+ Start downloading
323
+ </CclButton>
324
+ )}
325
+ </>
326
+ );
327
+ };
328
+
329
+ export default CLMSCartContent;
@@ -3,44 +3,22 @@
3
3
  * @module components/CLMSDownloadCartView/CLMSDownloadCartView
4
4
  */
5
5
 
6
- import React, { useEffect, useState } from 'react';
7
- import { Helmet } from '@plone/volto/helpers';
6
+ import { Forbidden, Unauthorized } from '@plone/volto/components';
7
+ import React, { useEffect } from 'react';
8
8
  import { defineMessages, useIntl } from 'react-intl';
9
- import { useDispatch, useSelector } from 'react-redux';
9
+
10
+ import CLMSCartContent from './CLMSCartContent';
11
+ import CLMSTasksInProgress from './CLMSTasksInProgress';
12
+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
13
+ import { FormattedMessage } from 'react-intl';
14
+ import { Helmet } from '@plone/volto/helpers';
10
15
  import { getExtraBreadcrumbItems } from '../../actions';
11
16
  import useCartState from '@eeacms/volto-clms-utils/cart/useCartState';
12
- import { FormattedMessage } from 'react-intl';
13
- import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
14
- import { Forbidden, Unauthorized } from '@plone/volto/components';
15
- import { Checkbox } from 'semantic-ui-react';
16
- import CclButton from '@eeacms/volto-clms-theme/components/CclButton/CclButton';
17
- import { postDownloadtool, getDownloadtool } from '../../actions';
18
- import CLMSTasksInProgress from './CLMSTasksInProgress';
17
+ import { useDispatch } from 'react-redux';
19
18
 
20
19
  const CLMSDownloadCartView = (props) => {
21
20
  const dispatch = useDispatch();
22
- const { cart, removeCartItem, isLoggedIn } = useCartState();
23
- const [cartSelection, setCartSelection] = useState([]);
24
- const post_download_in_progress = useSelector(
25
- (state) => state.downloadtool.post_download_in_progress,
26
- );
27
- const user_id = useSelector((state) => state.users.user.id);
28
- const selectCart = (id, checked) => {
29
- if (checked) setCartSelection(cartSelection.concat(id));
30
- else setCartSelection(cartSelection.filter((arr_id) => arr_id !== id));
31
- };
32
-
33
- const selectAllCart = (checked) => {
34
- if (checked) {
35
- setCartSelection(
36
- cart
37
- .filter((item) => item.task_in_progress === false)
38
- .map((item, key) => item.unique_id),
39
- );
40
- } else {
41
- setCartSelection([]);
42
- }
43
- };
21
+ const { isLoggedIn } = useCartState();
44
22
 
45
23
  const { formatMessage } = useIntl();
46
24
  const messages = defineMessages({
@@ -66,31 +44,6 @@ const CLMSDownloadCartView = (props) => {
66
44
  };
67
45
  }, [dispatch, formatMessage, messages.Cart, props.location.pathname]);
68
46
 
69
- const getSelectedCartItems = () => {
70
- return cart.filter((item) => cartSelection.indexOf(item.unique_id) > -1);
71
- };
72
-
73
- // eslint-disable-next-line react-hooks/exhaustive-deps
74
- const setCartItemInProgress = (in_progress_dataset_id) => {
75
- let started_processing_item = cart.filter(
76
- (r) => r['unique_id'] === in_progress_dataset_id,
77
- )[0];
78
- if (started_processing_item['unique_id']) {
79
- removeCartItem(started_processing_item['unique_id']);
80
- dispatch(getDownloadtool(user_id));
81
- }
82
- };
83
-
84
- useEffect(() => {
85
- let progress_keys = Object.keys(post_download_in_progress);
86
- progress_keys.forEach((progress_key) =>
87
- setCartItemInProgress(
88
- post_download_in_progress[progress_key]['DatasetID'],
89
- ),
90
- );
91
- // eslint-disable-next-line react-hooks/exhaustive-deps
92
- }, [post_download_in_progress]);
93
-
94
47
  return (
95
48
  <>
96
49
  <Helmet title={formatMessage(messages.Cart)} />
@@ -141,184 +94,7 @@ const CLMSDownloadCartView = (props) => {
141
94
  </div>
142
95
  </div>
143
96
  <CLMSTasksInProgress />
144
- <div className="custom-table cart-table">
145
- <h2>My cart</h2>
146
- <table>
147
- <thead>
148
- <tr>
149
- <th className="table-th-warning"></th>
150
- <th className="table-th-checkbox">
151
- <div className="ccl-form">
152
- <div className="ccl-form-group">
153
- <Checkbox
154
- onChange={(e, data) =>
155
- selectAllCart(data.checked)
156
- }
157
- checked={
158
- cart
159
- ? cart
160
- .filter(
161
- (item) =>
162
- item.task_in_progress === false,
163
- )
164
- .map((item, key) => item.unique_id)
165
- .every(function (val) {
166
- return (
167
- cartSelection.indexOf(val) !== -1
168
- );
169
- })
170
- : false
171
- }
172
- />
173
- </div>
174
- </div>
175
- </th>
176
- <th>Name</th>
177
- <th>Source</th>
178
- <th>Area</th>
179
- <th>Year</th>
180
- <th>Resolution</th>
181
- <th>Type</th>
182
- <th>Format</th>
183
- <th>Version</th>
184
- <th>Size</th>
185
- <th></th>
186
- </tr>
187
- </thead>
188
- <tbody>
189
- {cart &&
190
- cart.map((item, key) => (
191
- <tr
192
- key={key}
193
- style={
194
- item.task_in_progress
195
- ? { opacity: 0.5, backgroundColor: '#f5f5f5' }
196
- : {}
197
- }
198
- >
199
- <td className="table-td-warning hidden-warning">
200
- {!!item.warning && (
201
- <span
202
- className="info-icon"
203
- tooltip={item.warning}
204
- direction="up"
205
- >
206
- <FontAwesomeIcon
207
- icon={['fas', 'exclamation-triangle']}
208
- />
209
- </span>
210
- )}
211
- </td>
212
- <td className="table-td-checkbox">
213
- <div className="ccl-form">
214
- <div className="ccl-form-group">
215
- <Checkbox
216
- onChange={(e, data) =>
217
- selectCart(item.unique_id, data.checked)
218
- }
219
- checked={cartSelection.includes(
220
- item.unique_id,
221
- )}
222
- disabled={item.task_in_progress}
223
- />
224
- </div>
225
- </div>
226
- </td>
227
- <td>{item.name}</td>
228
- <td>{item.source}</td>
229
- <td>{item.area}</td>
230
- <td>{item.year}</td>
231
- <td>{item.resolution}</td>
232
- <td>
233
- <span
234
- className={'tag tag-' + item?.type?.toLowerCase()}
235
- >
236
- {item.type}
237
- </span>
238
- </td>
239
- <td className="table-td-format">
240
- {item.format}
241
- {/* <div className="ccl-form">
242
- <div className="ccl-form-group">
243
- <select
244
- className="ccl-select"
245
- id="select-ID-1"
246
- name="select-name-1"
247
- defaultValue={item.format}
248
- >
249
- <option value="GeoTiff" >GeoTiff</option>
250
- <option value="ESRI Geodatabase" >ESRI Geodatabase</option>
251
- <option value="SQLite Geodatabase" >SQLite Geodatabase</option>
252
- </select>
253
- </div>
254
- </div> */}
255
- </td>
256
- <td>{item.version}</td>
257
- <td>{item.size}</td>
258
- <td>
259
- {item.task_in_progress ? (
260
- <FontAwesomeIcon icon="spinner" spin />
261
- ) : (
262
- <FontAwesomeIcon
263
- icon={['fas', 'trash']}
264
- style={{ cursor: 'pointer' }}
265
- onClick={() => {
266
- removeCartItem(item.unique_id);
267
- }}
268
- />
269
- )}
270
- </td>
271
- </tr>
272
- ))}
273
- {cart?.length === 0 && (
274
- <>
275
- <tr>
276
- <td
277
- colSpan={11}
278
- style={{
279
- textAlign: 'center',
280
- color: '#adb0b8',
281
- opacity: 0.5,
282
- }}
283
- >
284
- Empty cart
285
- </td>
286
- </tr>
287
- </>
288
- )}
289
- </tbody>
290
- </table>
291
- </div>
292
- {cart?.length !== 0 && (
293
- <CclButton
294
- onClick={() => {
295
- let selectedItems = getSelectedCartItems();
296
- selectedItems.forEach((selected) => {
297
- const item = {
298
- UserID: user_id,
299
- /*,
300
- UID: dataset unique id;
301
- DatasetID: dataset id;
302
- Format: Dataset format
303
- selected['unique_id']
304
- selected['UID']
305
- */
306
- DatasetID: selected['unique_id'] || selected['UID'], // provisional, We need to send and return unique_id to can mark this element as a pending task selected['UID'],
307
- //DatasetFormat: selected['type'], // Formats: "Shapefile"|"GDB"|"GPKG"|"Geojson"|"Geotiff"|"Netcdf"|"GML"|"WFS"
308
- DatasetFormat: 'Shapefile',
309
- // OutputFormat:selected['format'],
310
- // DatasetPath: selected['path'],
311
-
312
- OutputFormat: 'GDB',
313
- };
314
- dispatch(postDownloadtool(item));
315
- });
316
- }}
317
- disabled={cartSelection.length === 0}
318
- >
319
- Start downloading
320
- </CclButton>
321
- )}
97
+ <CLMSCartContent />
322
98
  </div>
323
99
  </>
324
100
  )}