@eeacms/volto-marine-policy 2.0.7 → 2.0.9
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 +36 -0
- package/package.json +3 -2
- package/src/components/Blocks/DemoSitesExplorer/DemoSitesExplorerView.js +49 -15
- package/src/components/Blocks/DemoSitesExplorer/DemoSitesFilters.jsx +51 -34
- package/src/components/Blocks/DemoSitesExplorer/DemoSitesMap.jsx +23 -5
- package/src/components/Blocks/DemoSitesExplorer/FeatureDisplay.jsx +22 -18
- package/src/components/Blocks/DemoSitesExplorer/ObjectivesChart.jsx +224 -0
- package/src/components/Blocks/DemoSitesExplorer/styles.less +99 -3
- package/src/components/Blocks/DemoSitesExplorer/utils.js +85 -12
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,42 @@ 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.9](https://github.com/eea/volto-marine-policy/compare/2.0.8...2.0.9) - 7 May 2025
|
|
8
|
+
|
|
9
|
+
#### :rocket: Dependency updates
|
|
10
|
+
|
|
11
|
+
- Release @eeacms/volto-searchlib@2.0.16 [EEA Jenkins - [`9d425b4`](https://github.com/eea/volto-marine-policy/commit/9d425b44fa813cf7d08d95d36af6c0f01d0e52ce)]
|
|
12
|
+
|
|
13
|
+
#### :house: Internal changes
|
|
14
|
+
|
|
15
|
+
- style: Automated code fix [eea-jenkins - [`1c3cbdc`](https://github.com/eea/volto-marine-policy/commit/1c3cbdcc74127d45e46db9ae4fd1ac3b237ad32c)]
|
|
16
|
+
- style: Automated code fix [eea-jenkins - [`b00b251`](https://github.com/eea/volto-marine-policy/commit/b00b2515e54b8ffdbc51bf301f0b0bb232b83efe)]
|
|
17
|
+
- style: Automated code fix [eea-jenkins - [`e02c515`](https://github.com/eea/volto-marine-policy/commit/e02c5156ac34e4cf2fef61a27dfe64d60aad97af)]
|
|
18
|
+
- style: Automated code fix [eea-jenkins - [`678b9bf`](https://github.com/eea/volto-marine-policy/commit/678b9bf2b8f83f9b3a6facaa42f5851a013462ad)]
|
|
19
|
+
- style: Automated code fix [eea-jenkins - [`b1cccfb`](https://github.com/eea/volto-marine-policy/commit/b1cccfbd674890392948ad5227bfe334f55a2721)]
|
|
20
|
+
|
|
21
|
+
#### :hammer_and_wrench: Others
|
|
22
|
+
|
|
23
|
+
- small fixes demmo sites map [laszlocseh - [`f83d070`](https://github.com/eea/volto-marine-policy/commit/f83d070d0b1c7a5bdf1136eb542782477c9266b0)]
|
|
24
|
+
- update objectives [laszlocseh - [`0c09eb1`](https://github.com/eea/volto-marine-policy/commit/0c09eb1b093ffba34deafa079127175629f3f2b9)]
|
|
25
|
+
- fix eslint [laszlocseh - [`128fbb0`](https://github.com/eea/volto-marine-policy/commit/128fbb04277cc11bc6406df8cc815ea4a41291d0)]
|
|
26
|
+
- fix eslint [laszlocseh - [`68e7168`](https://github.com/eea/volto-marine-policy/commit/68e7168d6322cb07fe7df7c2376a26e297cb015f)]
|
|
27
|
+
- fix eslint [laszlocseh - [`1be214a`](https://github.com/eea/volto-marine-policy/commit/1be214accdac3657f156691f09c149f9f678d344)]
|
|
28
|
+
- fix eslint [laszlocseh - [`fbed39c`](https://github.com/eea/volto-marine-policy/commit/fbed39c59d7841234cd8ffe699a9e84766a501bc)]
|
|
29
|
+
- fix eslint [laszlocseh - [`ed3a2d2`](https://github.com/eea/volto-marine-policy/commit/ed3a2d26194d18aed50b1781e2d84a3d1482b0ab)]
|
|
30
|
+
- fix eslint [laszlocseh - [`15805ce`](https://github.com/eea/volto-marine-policy/commit/15805ce9adb0ca66a99368a432d54d74fcbc8077)]
|
|
31
|
+
- WiP demo sites map [laszlocseh - [`ca84585`](https://github.com/eea/volto-marine-policy/commit/ca84585f692b03c7011d46393fa5b937fa985e4d)]
|
|
32
|
+
- WiP demo sites map [laszlocseh - [`b51ca84`](https://github.com/eea/volto-marine-policy/commit/b51ca842dba4fe0700100b5a6328d1bd4b962acb)]
|
|
33
|
+
### [2.0.8](https://github.com/eea/volto-marine-policy/compare/2.0.7...2.0.8) - 16 April 2025
|
|
34
|
+
|
|
35
|
+
#### :house: Internal changes
|
|
36
|
+
|
|
37
|
+
- style: Automated code fix [eea-jenkins - [`c400842`](https://github.com/eea/volto-marine-policy/commit/c4008422b3480259c2a1101994c544b44e5b9002)]
|
|
38
|
+
|
|
39
|
+
#### :hammer_and_wrench: Others
|
|
40
|
+
|
|
41
|
+
- eslint [laszlocseh - [`ebdba1e`](https://github.com/eea/volto-marine-policy/commit/ebdba1e06ed02f3bf03442dfd578f2c1db5b1241)]
|
|
42
|
+
- demo sites map viewer zoom improvements [laszlocseh - [`1680f64`](https://github.com/eea/volto-marine-policy/commit/1680f644f00e1bf62e1a5471aaf25a95d152ba05)]
|
|
7
43
|
### [2.0.7](https://github.com/eea/volto-marine-policy/compare/2.0.6...2.0.7) - 15 April 2025
|
|
8
44
|
|
|
9
45
|
#### :house: Internal changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eeacms/volto-marine-policy",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.9",
|
|
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",
|
|
@@ -36,13 +36,14 @@
|
|
|
36
36
|
"@eeacms/volto-embed": "*",
|
|
37
37
|
"@eeacms/volto-globalsearch": "2.0.11",
|
|
38
38
|
"@eeacms/volto-openlayers-map": "0.3.2",
|
|
39
|
-
"@eeacms/volto-searchlib": "2.0.
|
|
39
|
+
"@eeacms/volto-searchlib": "2.0.16",
|
|
40
40
|
"@eeacms/volto-tabs-block": "*",
|
|
41
41
|
"axios": "0.30.0",
|
|
42
42
|
"d3-array": "^2.12.1",
|
|
43
43
|
"jquery": "3.6.0",
|
|
44
44
|
"razzle-plugin-scss": "^4.2.18",
|
|
45
45
|
"react-lazy-load-image-component": "^1.4.0",
|
|
46
|
+
"react-plotly.js": "2.6.0",
|
|
46
47
|
"react-slick": "^0.24.0",
|
|
47
48
|
"slick-carousel": "^1.8.1"
|
|
48
49
|
},
|
|
@@ -2,9 +2,11 @@ import React from 'react';
|
|
|
2
2
|
import { Grid } from 'semantic-ui-react'; // Dropdown,
|
|
3
3
|
import { addAppURL } from '@plone/volto/helpers';
|
|
4
4
|
import config from '@plone/volto/registry';
|
|
5
|
+
import { VisibilitySensor } from '@eeacms/volto-datablocks/components';
|
|
6
|
+
|
|
5
7
|
import DemoSitesMap from './DemoSitesMap';
|
|
6
8
|
import { ActiveFilters, DemoSitesFilters } from './DemoSitesFilters';
|
|
7
|
-
|
|
9
|
+
import ObjectivesChart from './ObjectivesChart';
|
|
8
10
|
import { filterCases, getFilters } from './utils';
|
|
9
11
|
import { useCases } from './hooks';
|
|
10
12
|
|
|
@@ -23,6 +25,7 @@ export default function DemoSitesExplorerView(props) {
|
|
|
23
25
|
|
|
24
26
|
const [activeFilters, setActiveFilters] = React.useState({
|
|
25
27
|
objective_filter: [],
|
|
28
|
+
target_filter: [],
|
|
26
29
|
indicator_filter: [],
|
|
27
30
|
project_filter: [],
|
|
28
31
|
country_filter: [],
|
|
@@ -31,6 +34,7 @@ export default function DemoSitesExplorerView(props) {
|
|
|
31
34
|
const [activeItems, setActiveItems] = React.useState(cases);
|
|
32
35
|
const [filters, setFilters] = React.useState([]);
|
|
33
36
|
const [map, setMap] = React.useState();
|
|
37
|
+
const [highlightedIndex, setHighlightedIndex] = React.useState(-1);
|
|
34
38
|
|
|
35
39
|
React.useEffect(() => {
|
|
36
40
|
const _filters = getFilters(cases, indicatorOnly);
|
|
@@ -38,6 +42,7 @@ export default function DemoSitesExplorerView(props) {
|
|
|
38
42
|
}, [
|
|
39
43
|
cases,
|
|
40
44
|
activeFilters.objective_filter,
|
|
45
|
+
activeFilters.target_filter,
|
|
41
46
|
activeFilters.indicator_filter,
|
|
42
47
|
activeFilters.project_filter,
|
|
43
48
|
activeFilters.country_filter,
|
|
@@ -74,33 +79,62 @@ export default function DemoSitesExplorerView(props) {
|
|
|
74
79
|
<Grid.Row>
|
|
75
80
|
{cases.length ? (
|
|
76
81
|
<Grid columns={12}>
|
|
77
|
-
<Grid.Column mobile={
|
|
82
|
+
<Grid.Column mobile={8} tablet={8} computer={8}>
|
|
78
83
|
<DemoSitesMap
|
|
79
84
|
items={cases}
|
|
80
85
|
activeItems={activeItems}
|
|
86
|
+
setActiveFilters={setActiveFilters}
|
|
81
87
|
hideFilters={hideFilters}
|
|
82
88
|
selectedCase={selectedCase}
|
|
83
89
|
onSelectedCase={onSelectedCase}
|
|
84
90
|
map={map}
|
|
85
91
|
setMap={setMap}
|
|
92
|
+
highlightedIndex={highlightedIndex}
|
|
93
|
+
setHighlightedIndex={setHighlightedIndex}
|
|
86
94
|
/>
|
|
87
95
|
</Grid.Column>
|
|
88
|
-
<Grid.Column
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
96
|
+
<Grid.Column
|
|
97
|
+
mobile={4}
|
|
98
|
+
tablet={4}
|
|
99
|
+
computer={4}
|
|
100
|
+
className="right-side-filters"
|
|
101
|
+
>
|
|
102
|
+
<Grid.Row>
|
|
103
|
+
{!hideFilters ? (
|
|
104
|
+
<VisibilitySensor>
|
|
105
|
+
<ObjectivesChart
|
|
106
|
+
items={cases}
|
|
107
|
+
activeItems={activeItems}
|
|
108
|
+
filters={filters}
|
|
109
|
+
activeFilters={activeFilters}
|
|
110
|
+
hideFilters={hideFilters}
|
|
111
|
+
setActiveFilters={setActiveFilters}
|
|
112
|
+
map={map}
|
|
113
|
+
highlightedIndex={highlightedIndex}
|
|
114
|
+
setHighlightedIndex={setHighlightedIndex}
|
|
115
|
+
/>
|
|
116
|
+
</VisibilitySensor>
|
|
117
|
+
) : (
|
|
118
|
+
''
|
|
119
|
+
)}
|
|
120
|
+
</Grid.Row>
|
|
121
|
+
<Grid.Row>
|
|
122
|
+
<div className="legend">
|
|
123
|
+
{/* <div className="legend-row legend-subtitle">Legend</div> */}
|
|
124
|
+
<div className="legend-row">
|
|
125
|
+
<div className="circle">
|
|
126
|
+
<div className="dot-demosite"></div>
|
|
127
|
+
</div>
|
|
128
|
+
<div>Demo site</div>
|
|
94
129
|
</div>
|
|
95
|
-
<div
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
<div
|
|
130
|
+
<div className="legend-row">
|
|
131
|
+
<div className="circle">
|
|
132
|
+
<div className="dot-region"></div>
|
|
133
|
+
</div>
|
|
134
|
+
<div>Associated region</div>
|
|
100
135
|
</div>
|
|
101
|
-
<div>Associated region</div>
|
|
102
136
|
</div>
|
|
103
|
-
</
|
|
137
|
+
</Grid.Row>
|
|
104
138
|
</Grid.Column>
|
|
105
139
|
</Grid>
|
|
106
140
|
) : null}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
|
-
import { centerAndResetMapZoom } from './utils';
|
|
3
|
+
import { centerAndResetMapZoom, clearFilters } from './utils';
|
|
4
4
|
|
|
5
5
|
const normalizeSearchInput = (searchInput) => {
|
|
6
6
|
let normInput = searchInput
|
|
@@ -132,7 +132,7 @@ export function DemoSitesFilters(props) {
|
|
|
132
132
|
|
|
133
133
|
return (
|
|
134
134
|
<>
|
|
135
|
-
{!hideFilters ? (
|
|
135
|
+
{/* {!hideFilters ? (
|
|
136
136
|
<DemoSitesFilter
|
|
137
137
|
filterTitle="Objective/Enabler"
|
|
138
138
|
filterName="objective_filter"
|
|
@@ -140,13 +140,19 @@ export function DemoSitesFilters(props) {
|
|
|
140
140
|
activeFilters={activeFilters}
|
|
141
141
|
setActiveFilters={setActiveFilters}
|
|
142
142
|
map={map}
|
|
143
|
-
customOrder={
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
143
|
+
customOrder={objectivesCustomOrder}
|
|
144
|
+
/>
|
|
145
|
+
) : (
|
|
146
|
+
''
|
|
147
|
+
)} */}
|
|
148
|
+
{!hideFilters ? (
|
|
149
|
+
<DemoSitesFilter
|
|
150
|
+
filterTitle="Target"
|
|
151
|
+
filterName="target_filter"
|
|
152
|
+
filters={filters}
|
|
153
|
+
activeFilters={activeFilters}
|
|
154
|
+
setActiveFilters={setActiveFilters}
|
|
155
|
+
map={map}
|
|
150
156
|
/>
|
|
151
157
|
) : (
|
|
152
158
|
''
|
|
@@ -266,30 +272,16 @@ export function SearchBox(props) {
|
|
|
266
272
|
|
|
267
273
|
export function ActiveFilters(props) {
|
|
268
274
|
const { filters, activeFilters, setActiveFilters } = props;
|
|
269
|
-
const hasActiveFilters =
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
const filterInputs = document.querySelectorAll(
|
|
280
|
-
'#cse-filter .filter-input input',
|
|
281
|
-
);
|
|
282
|
-
for (let i = 0; i < filterInputs.length; i++) {
|
|
283
|
-
filterInputs[i].checked = false;
|
|
284
|
-
}
|
|
285
|
-
setActiveFilters({
|
|
286
|
-
objective_filter: [],
|
|
287
|
-
indicator_filter: [],
|
|
288
|
-
project_filter: [],
|
|
289
|
-
country_filter: [],
|
|
290
|
-
});
|
|
291
|
-
// scrollToElement('search-input');
|
|
292
|
-
};
|
|
275
|
+
const hasActiveFilters = false;
|
|
276
|
+
// const hasActiveFilters = Object.entries(activeFilters).some(
|
|
277
|
+
// ([filterName, filterList]) => {
|
|
278
|
+
// return false;
|
|
279
|
+
// if (filterList.length > 0) {
|
|
280
|
+
// return true;
|
|
281
|
+
// }
|
|
282
|
+
// return false;
|
|
283
|
+
// },
|
|
284
|
+
// );
|
|
293
285
|
|
|
294
286
|
const removeFilter = (filterName, filterCode) => {
|
|
295
287
|
const temp = JSON.parse(JSON.stringify(activeFilters));
|
|
@@ -316,7 +308,7 @@ export function ActiveFilters(props) {
|
|
|
316
308
|
<div className="filter-list-header">
|
|
317
309
|
<h4 className="filter-list-title">Active filters</h4>
|
|
318
310
|
<button
|
|
319
|
-
onClick={clearFilters}
|
|
311
|
+
onClick={() => clearFilters(setActiveFilters)}
|
|
320
312
|
className="ui mini basic compact button clear-btn"
|
|
321
313
|
>
|
|
322
314
|
clear all
|
|
@@ -349,6 +341,31 @@ export function ActiveFilters(props) {
|
|
|
349
341
|
) : (
|
|
350
342
|
''
|
|
351
343
|
)}
|
|
344
|
+
{activeFilters.target_filter.length > 0 ? (
|
|
345
|
+
<div className="filter-wrapper">
|
|
346
|
+
<div className="filter-label">Target:</div>
|
|
347
|
+
{activeFilters.target_filter.map((filterCode) => {
|
|
348
|
+
const filterLabel = filters.target_filter[filterCode];
|
|
349
|
+
return (
|
|
350
|
+
<div className="ui basic label filter-value">
|
|
351
|
+
<span>{filterLabel}</span>
|
|
352
|
+
<i
|
|
353
|
+
tabIndex="0"
|
|
354
|
+
onKeyPress={() => {}}
|
|
355
|
+
onClick={() => {
|
|
356
|
+
removeFilter('target_filter', filterCode);
|
|
357
|
+
// scrollToElement('search-input');
|
|
358
|
+
}}
|
|
359
|
+
role="button"
|
|
360
|
+
className="close icon"
|
|
361
|
+
></i>
|
|
362
|
+
</div>
|
|
363
|
+
);
|
|
364
|
+
})}
|
|
365
|
+
</div>
|
|
366
|
+
) : (
|
|
367
|
+
''
|
|
368
|
+
)}
|
|
352
369
|
{activeFilters.indicator_filter.length > 0 ? (
|
|
353
370
|
<div className="filter-wrapper">
|
|
354
371
|
<div className="filter-label">Indicator:</div>
|
|
@@ -9,7 +9,12 @@ import InfoOverlay from './InfoOverlay';
|
|
|
9
9
|
import FeatureInteraction from './FeatureInteraction';
|
|
10
10
|
import { useMapContext } from '@eeacms/volto-openlayers-map/api';
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
// centerAndResetMapZoom,
|
|
14
|
+
clearFilters,
|
|
15
|
+
getFeatures,
|
|
16
|
+
zoomMapToFeatures,
|
|
17
|
+
} from './utils';
|
|
13
18
|
|
|
14
19
|
const styleCache = {};
|
|
15
20
|
const MapContextGateway = ({ setMap }) => {
|
|
@@ -24,11 +29,13 @@ export default function DemoSitesMap(props) {
|
|
|
24
29
|
const {
|
|
25
30
|
items,
|
|
26
31
|
activeItems,
|
|
32
|
+
setActiveFilters,
|
|
27
33
|
hideFilters,
|
|
28
34
|
selectedCase,
|
|
29
35
|
onSelectedCase,
|
|
30
36
|
map,
|
|
31
37
|
setMap,
|
|
38
|
+
setHighlightedIndex,
|
|
32
39
|
} = props;
|
|
33
40
|
const features = getFeatures(items);
|
|
34
41
|
const [resetMapButtonClass, setResetMapButtonClass] =
|
|
@@ -60,11 +67,15 @@ export default function DemoSitesMap(props) {
|
|
|
60
67
|
);
|
|
61
68
|
|
|
62
69
|
React.useEffect(() => {
|
|
70
|
+
if (!map) return null;
|
|
71
|
+
|
|
63
72
|
if (activeItems) {
|
|
64
73
|
pointsSource.clear();
|
|
65
74
|
pointsSource.addFeatures(getFeatures(activeItems));
|
|
75
|
+
// hideFilters && zoomMapToFeatures(map, getFeatures(activeItems));
|
|
76
|
+
zoomMapToFeatures(map, getFeatures(activeItems));
|
|
66
77
|
}
|
|
67
|
-
}, [activeItems, pointsSource]);
|
|
78
|
+
}, [map, activeItems, pointsSource, hideFilters]);
|
|
68
79
|
|
|
69
80
|
React.useEffect(() => {
|
|
70
81
|
if (!map) return null;
|
|
@@ -119,7 +130,7 @@ export default function DemoSitesMap(props) {
|
|
|
119
130
|
view={{
|
|
120
131
|
center: ol.proj.fromLonLat([10, 54]),
|
|
121
132
|
showFullExtent: true,
|
|
122
|
-
zoom:
|
|
133
|
+
zoom: 3,
|
|
123
134
|
}}
|
|
124
135
|
pixelRatio={1}
|
|
125
136
|
// controls={ol.control.defaults({ attribution: false })}
|
|
@@ -134,11 +145,18 @@ export default function DemoSitesMap(props) {
|
|
|
134
145
|
onClick={() => {
|
|
135
146
|
// scrollToElement('search-input');
|
|
136
147
|
onSelectedCase(null);
|
|
137
|
-
|
|
148
|
+
clearFilters(setActiveFilters);
|
|
149
|
+
setHighlightedIndex(5);
|
|
150
|
+
if (hideFilters) {
|
|
151
|
+
zoomMapToFeatures(map, getFeatures(activeItems));
|
|
152
|
+
} else {
|
|
153
|
+
// centerAndResetMapZoom(map);
|
|
154
|
+
zoomMapToFeatures(map, getFeatures(activeItems));
|
|
155
|
+
}
|
|
138
156
|
map.getInteractions().array_[9].getFeatures().clear();
|
|
139
157
|
}}
|
|
140
158
|
>
|
|
141
|
-
<span className="result-info-title">Reset
|
|
159
|
+
<span className="result-info-title">Reset filters</span>
|
|
142
160
|
<i className="icon ri-map-2-line"></i>
|
|
143
161
|
</button>
|
|
144
162
|
<InfoOverlay
|
|
@@ -78,24 +78,28 @@ export default function FeatureDisplay({ feature }) {
|
|
|
78
78
|
''
|
|
79
79
|
)} */}
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
<
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
81
|
+
{feature.indicators.length > 0 ? (
|
|
82
|
+
<div>
|
|
83
|
+
<span className="popup-title blue">Indicators</span>
|
|
84
|
+
<ul>
|
|
85
|
+
{feature.indicators.map((item, index) => {
|
|
86
|
+
return (
|
|
87
|
+
<li key={index}>
|
|
88
|
+
<span
|
|
89
|
+
// target="_blank"
|
|
90
|
+
// rel="noopener noreferrer"
|
|
91
|
+
// href={item['path']}
|
|
92
|
+
>
|
|
93
|
+
{item['title']}
|
|
94
|
+
</span>
|
|
95
|
+
</li>
|
|
96
|
+
);
|
|
97
|
+
})}
|
|
98
|
+
</ul>
|
|
99
|
+
</div>
|
|
100
|
+
) : (
|
|
101
|
+
''
|
|
102
|
+
)}
|
|
99
103
|
{/* <div>
|
|
100
104
|
<h4>Indicators</h4>
|
|
101
105
|
<ul>
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import React, { useRef } from 'react';
|
|
2
|
+
import cx from 'classnames';
|
|
3
|
+
import loadable from '@loadable/component';
|
|
4
|
+
import { Grid } from 'semantic-ui-react'; // Dropdown,
|
|
5
|
+
|
|
6
|
+
import { objectivesCustomOrder } from './utils';
|
|
7
|
+
|
|
8
|
+
const Plot = loadable(() => import('react-plotly.js'));
|
|
9
|
+
|
|
10
|
+
const ObjectivesChart = ({
|
|
11
|
+
items,
|
|
12
|
+
// activeItems,
|
|
13
|
+
// filters,
|
|
14
|
+
activeFilters,
|
|
15
|
+
setActiveFilters,
|
|
16
|
+
// hideFilters,
|
|
17
|
+
// map,
|
|
18
|
+
highlightedIndex,
|
|
19
|
+
setHighlightedIndex,
|
|
20
|
+
}) => {
|
|
21
|
+
const initialized = useRef(false);
|
|
22
|
+
const chartRef = useRef(null);
|
|
23
|
+
const objectiveCounts = {};
|
|
24
|
+
|
|
25
|
+
for (const item of items) {
|
|
26
|
+
item.properties.objective.map((obj) => {
|
|
27
|
+
objectiveCounts[obj] = (objectiveCounts[obj] || 0) + 1;
|
|
28
|
+
return [];
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
const _sorted = Object.entries(objectiveCounts).sort(
|
|
32
|
+
(a, b) =>
|
|
33
|
+
objectivesCustomOrder.indexOf(a[0]) - objectivesCustomOrder.indexOf(b[0]),
|
|
34
|
+
);
|
|
35
|
+
const objectives = Object.fromEntries(_sorted);
|
|
36
|
+
const objectivesLength = Object.keys(objectives).length;
|
|
37
|
+
|
|
38
|
+
React.useEffect(() => {
|
|
39
|
+
// set the objectives and the count
|
|
40
|
+
if (!items) return;
|
|
41
|
+
if (initialized.current) return;
|
|
42
|
+
|
|
43
|
+
const interval = setInterval(() => {
|
|
44
|
+
setHighlightedIndex((prevIndex) => {
|
|
45
|
+
if (prevIndex + 1 >= objectivesLength + 1) {
|
|
46
|
+
clearInterval(interval); // Stop after last item
|
|
47
|
+
initialized.current = true;
|
|
48
|
+
return prevIndex;
|
|
49
|
+
}
|
|
50
|
+
return prevIndex + 1;
|
|
51
|
+
});
|
|
52
|
+
}, 2000);
|
|
53
|
+
// console.log('init');
|
|
54
|
+
return () => clearInterval(interval); // Cleanup on unmount
|
|
55
|
+
}, [items, objectivesLength, highlightedIndex, setHighlightedIndex]);
|
|
56
|
+
|
|
57
|
+
React.useEffect(() => {
|
|
58
|
+
// if (!objectives) return;
|
|
59
|
+
if (highlightedIndex === -1) {
|
|
60
|
+
setActiveFilters({
|
|
61
|
+
objective_filter: [undefined],
|
|
62
|
+
target_filter: [],
|
|
63
|
+
indicator_filter: [],
|
|
64
|
+
project_filter: [],
|
|
65
|
+
country_filter: [],
|
|
66
|
+
});
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const currentObjective = Object.keys(objectives)[highlightedIndex];
|
|
71
|
+
const filterKey = 'objective_filter';
|
|
72
|
+
const newValue = currentObjective ? [currentObjective] : [];
|
|
73
|
+
|
|
74
|
+
setActiveFilters((prev) => {
|
|
75
|
+
const currentValues = prev[filterKey];
|
|
76
|
+
// Don't update if no change
|
|
77
|
+
const isSame =
|
|
78
|
+
currentValues.length === newValue.length &&
|
|
79
|
+
currentValues.every((v, i) => v === newValue[i]);
|
|
80
|
+
|
|
81
|
+
if (isSame) return prev;
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
...prev,
|
|
85
|
+
[filterKey]: newValue,
|
|
86
|
+
};
|
|
87
|
+
});
|
|
88
|
+
// console.log(highlightedIndex, activeFilters);
|
|
89
|
+
}, [objectives, activeFilters, setActiveFilters, highlightedIndex]);
|
|
90
|
+
|
|
91
|
+
const handleClick = (event) => {
|
|
92
|
+
const point = event.points[0];
|
|
93
|
+
const label = point.label;
|
|
94
|
+
// const value = point.value;
|
|
95
|
+
|
|
96
|
+
const tempFilters = JSON.parse(JSON.stringify(activeFilters));
|
|
97
|
+
if (tempFilters['objective_filter'].includes(label)) {
|
|
98
|
+
tempFilters['objective_filter'] = tempFilters['objective_filter'].filter(
|
|
99
|
+
(i) => i !== label,
|
|
100
|
+
);
|
|
101
|
+
setHighlightedIndex(5);
|
|
102
|
+
} else {
|
|
103
|
+
tempFilters['objective_filter'] = [];
|
|
104
|
+
tempFilters['objective_filter'].push(label);
|
|
105
|
+
setHighlightedIndex(Object.keys(objectives).indexOf(label));
|
|
106
|
+
}
|
|
107
|
+
setActiveFilters(tempFilters);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const handleHover = () => {
|
|
111
|
+
if (chartRef.current) {
|
|
112
|
+
chartRef.current.style.cursor = 'pointer'; // Change cursor on hover
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const handleUnhover = () => {
|
|
117
|
+
if (chartRef.current) {
|
|
118
|
+
chartRef.current.style.cursor = 'default'; // Reset cursor
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const labels = Object.keys(objectives);
|
|
123
|
+
const values = Object.values(objectives);
|
|
124
|
+
const totalCount = values.reduce((acc, curr) => acc + curr, 0);
|
|
125
|
+
const customColors = ['#007b6c', '#fdaf20', '#004b7f', '#f9eb8a', '#9e83b6']; // Adjust colors as needed
|
|
126
|
+
const grayColor = '#d3d3d3';
|
|
127
|
+
// const pull = labels.map((_, i) => (i === highlightedIndex ? 0.1 : 0));
|
|
128
|
+
const inactiveColors = labels.map((_, i) =>
|
|
129
|
+
i === highlightedIndex ? customColors[i % customColors.length] : grayColor,
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
return highlightedIndex >= -1 ? (
|
|
133
|
+
<div className="objectives-chart fade-in">
|
|
134
|
+
<Grid.Row className="chart-title">
|
|
135
|
+
Demo sites and Associated regions
|
|
136
|
+
</Grid.Row>
|
|
137
|
+
<Grid.Row className="chart-title">Objective/Enabler</Grid.Row>
|
|
138
|
+
<Grid.Row className="chart-container">
|
|
139
|
+
<div ref={chartRef}>
|
|
140
|
+
<Plot
|
|
141
|
+
useResizeHandler
|
|
142
|
+
data={[
|
|
143
|
+
{
|
|
144
|
+
type: 'pie',
|
|
145
|
+
labels: labels,
|
|
146
|
+
values: values,
|
|
147
|
+
textinfo: 'none',
|
|
148
|
+
// textinfo: highlightedIndex === 5 ? 'value' : 'none',
|
|
149
|
+
hole: 0.4,
|
|
150
|
+
insidetextorientation: 'radial',
|
|
151
|
+
hoverinfo: 'label+value',
|
|
152
|
+
marker: {
|
|
153
|
+
colors:
|
|
154
|
+
highlightedIndex === 5 ? customColors : inactiveColors, // Apply custom colors here
|
|
155
|
+
},
|
|
156
|
+
direction: 'clockwise',
|
|
157
|
+
// pull,
|
|
158
|
+
},
|
|
159
|
+
]}
|
|
160
|
+
layout={{
|
|
161
|
+
width: 250,
|
|
162
|
+
height: 250,
|
|
163
|
+
// title: 'Objectives Distribution',
|
|
164
|
+
showlegend: false,
|
|
165
|
+
margin: {
|
|
166
|
+
t: 20, // Top margin
|
|
167
|
+
b: 20, // Bottom margin
|
|
168
|
+
l: 0, // Left margin
|
|
169
|
+
r: 0, // Right margin
|
|
170
|
+
},
|
|
171
|
+
annotations: [
|
|
172
|
+
{
|
|
173
|
+
text:
|
|
174
|
+
highlightedIndex === 5
|
|
175
|
+
? `${totalCount}`
|
|
176
|
+
: values[highlightedIndex] || '', // Display total count in the center
|
|
177
|
+
font: {
|
|
178
|
+
size: 24, // Adjust font size as needed
|
|
179
|
+
weight: 'bold',
|
|
180
|
+
},
|
|
181
|
+
showarrow: false, // No arrow pointing to the center
|
|
182
|
+
x: 0.5, // Position in the center (horizontal)
|
|
183
|
+
y: 0.5, // Position in the center (vertical)
|
|
184
|
+
align: 'center',
|
|
185
|
+
valign: 'middle',
|
|
186
|
+
bgcolor: 'rgba(255, 255, 255, 0.6)', // Optional background color
|
|
187
|
+
borderpad: 10, // Padding around the text
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
}}
|
|
191
|
+
config={{
|
|
192
|
+
responsive: true,
|
|
193
|
+
displayModeBar: true,
|
|
194
|
+
modeBarButtonsToRemove: ['toImage'], // Removes download button
|
|
195
|
+
displaylogo: false, // Removes "Produced with Plotly.js"
|
|
196
|
+
}}
|
|
197
|
+
onClick={handleClick}
|
|
198
|
+
onHover={handleHover}
|
|
199
|
+
onUnhover={handleUnhover}
|
|
200
|
+
/>
|
|
201
|
+
</div>
|
|
202
|
+
</Grid.Row>
|
|
203
|
+
<Grid.Row>
|
|
204
|
+
<ul>
|
|
205
|
+
{Object.entries(objectives).map(([item, count], index) => (
|
|
206
|
+
<li
|
|
207
|
+
key={item}
|
|
208
|
+
className={cx(
|
|
209
|
+
index === highlightedIndex ? 'active' : '',
|
|
210
|
+
customColors[index].replace('#', 'C'),
|
|
211
|
+
)}
|
|
212
|
+
>
|
|
213
|
+
{item}
|
|
214
|
+
</li>
|
|
215
|
+
))}
|
|
216
|
+
</ul>
|
|
217
|
+
</Grid.Row>
|
|
218
|
+
</div>
|
|
219
|
+
) : (
|
|
220
|
+
''
|
|
221
|
+
);
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
export default ObjectivesChart;
|
|
@@ -388,12 +388,12 @@
|
|
|
388
388
|
}
|
|
389
389
|
|
|
390
390
|
.legend {
|
|
391
|
-
position: absolute;
|
|
391
|
+
// position: absolute;
|
|
392
392
|
bottom: 2em;
|
|
393
393
|
display: flex;
|
|
394
|
-
flex-direction:
|
|
394
|
+
flex-direction: row;
|
|
395
395
|
align-items: flex-start;
|
|
396
|
-
gap:
|
|
396
|
+
gap: 1em;
|
|
397
397
|
|
|
398
398
|
.legend-row {
|
|
399
399
|
display: flex;
|
|
@@ -439,3 +439,99 @@
|
|
|
439
439
|
transform: translate(-50%, -50%);
|
|
440
440
|
}
|
|
441
441
|
}
|
|
442
|
+
|
|
443
|
+
.right-side-filters {
|
|
444
|
+
display: flex !important;
|
|
445
|
+
flex-direction: column;
|
|
446
|
+
align-items: center;
|
|
447
|
+
justify-content: space-around;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
.objectives-chart {
|
|
451
|
+
&.fade-in {
|
|
452
|
+
animation: fadeIn 1s ease-in forwards;
|
|
453
|
+
opacity: 0;
|
|
454
|
+
/* Start invisible */
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
@keyframes fadeIn {
|
|
458
|
+
to {
|
|
459
|
+
opacity: 1;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
.main-svg {
|
|
464
|
+
overflow: visible !important;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
.hoverlayer {
|
|
468
|
+
overflow: visible !important;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
.chart-title {
|
|
472
|
+
font-size: 1em;
|
|
473
|
+
font-weight: 500;
|
|
474
|
+
text-align: center;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
.chart-container {
|
|
478
|
+
text-align: center;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
ul {
|
|
482
|
+
display: flex;
|
|
483
|
+
flex-direction: column;
|
|
484
|
+
padding-left: 1.3em;
|
|
485
|
+
gap: 0.5em;
|
|
486
|
+
list-style: none;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
li {
|
|
490
|
+
display: flex;
|
|
491
|
+
flex-direction: row;
|
|
492
|
+
align-items: center;
|
|
493
|
+
font-size: 0.8em;
|
|
494
|
+
line-height: 1.5em;
|
|
495
|
+
|
|
496
|
+
&.C007b6c::before {
|
|
497
|
+
background-color: #007b6c;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
&.Cfdaf20::before {
|
|
501
|
+
background-color: #fdaf20;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
&.C004b7f::before {
|
|
505
|
+
background-color: #004b7f;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
&.Cf9eb8a::before {
|
|
509
|
+
background-color: #f9eb8a;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
&.C9e83b6::before {
|
|
513
|
+
background-color: #9e83b6;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
&::before {
|
|
517
|
+
width: 0.7em;
|
|
518
|
+
height: 0.7em;
|
|
519
|
+
flex-shrink: 0;
|
|
520
|
+
border-radius: 2px;
|
|
521
|
+
/* change as needed */
|
|
522
|
+
margin-right: 8px;
|
|
523
|
+
content: '';
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
&.active {
|
|
527
|
+
font-size: 1em;
|
|
528
|
+
// color: red;
|
|
529
|
+
font-weight: 500;
|
|
530
|
+
|
|
531
|
+
&::before {
|
|
532
|
+
width: 1em;
|
|
533
|
+
height: 1em;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
import { openlayers as ol } from '@eeacms/volto-openlayers-map';
|
|
2
2
|
|
|
3
|
+
export const objectivesCustomOrder = [
|
|
4
|
+
'Objective 1: Protect and restore marine and freshwater ecosystems and biodiversity',
|
|
5
|
+
'Objective 2: Prevent and eliminate pollution of our oceans, seas and waters',
|
|
6
|
+
'Objective 3: Make the sustainable blue economy carbon-neutral and circular',
|
|
7
|
+
'Enabler 1: Digital twin of the ocean',
|
|
8
|
+
'Enabler 2: Public mobilisation and engagement',
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
export const clearFilters = (setActiveFilters) => {
|
|
12
|
+
const filterInputs = document.querySelectorAll(
|
|
13
|
+
'#cse-filter .filter-input input',
|
|
14
|
+
);
|
|
15
|
+
for (let i = 0; i < filterInputs.length; i++) {
|
|
16
|
+
filterInputs[i].checked = false;
|
|
17
|
+
}
|
|
18
|
+
setActiveFilters({
|
|
19
|
+
objective_filter: [],
|
|
20
|
+
target_filter: [],
|
|
21
|
+
indicator_filter: [],
|
|
22
|
+
project_filter: [],
|
|
23
|
+
country_filter: [],
|
|
24
|
+
});
|
|
25
|
+
// scrollToElement('search-input');
|
|
26
|
+
};
|
|
27
|
+
|
|
3
28
|
export const truncateText = (str, max = 50) => {
|
|
4
29
|
if (str.length <= max) {
|
|
5
30
|
return str;
|
|
@@ -39,20 +64,42 @@ export function getExtentOfFeatures(features) {
|
|
|
39
64
|
|
|
40
65
|
export function zoomMapToFeatures(map, features, threshold = 500) {
|
|
41
66
|
const extent = getExtentOfFeatures(features);
|
|
42
|
-
|
|
67
|
+
|
|
68
|
+
// let extentBuffer = (extent[3] - extent[1] + extent[2] - extent[0]) / 4;
|
|
69
|
+
// extentBuffer = extentBuffer < threshold ? threshold : extentBuffer;
|
|
70
|
+
// const paddedExtent = ol.extent.buffer(extent, extentBuffer);
|
|
71
|
+
|
|
72
|
+
const width = extent[2] - extent[0];
|
|
73
|
+
const height = extent[3] - extent[1];
|
|
74
|
+
const bufferFactor = 0.05; // 5% buffer
|
|
75
|
+
|
|
76
|
+
let extentBuffer = Math.max(width, height) * bufferFactor;
|
|
43
77
|
extentBuffer = extentBuffer < threshold ? threshold : extentBuffer;
|
|
44
78
|
const paddedExtent = ol.extent.buffer(extent, extentBuffer);
|
|
45
|
-
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
// map.getView().fit(paddedExtent, { ...map.getSize(), duration: 1000 });
|
|
82
|
+
map.getView().fit(paddedExtent, {
|
|
83
|
+
size: map.getSize(), // pass size explicitly
|
|
84
|
+
// padding: [0, 0, 0, 0], // top, right, bottom, left
|
|
85
|
+
duration: 1000,
|
|
86
|
+
});
|
|
87
|
+
} catch (error) {
|
|
88
|
+
// no features available, zooming fails
|
|
89
|
+
}
|
|
46
90
|
}
|
|
47
91
|
|
|
48
92
|
export function getFeatures(cases) {
|
|
49
93
|
const Feature = ol.ol.Feature;
|
|
50
94
|
const colors = {
|
|
51
|
-
'
|
|
52
|
-
|
|
53
|
-
'Prevent and eliminate pollution of waters':
|
|
54
|
-
|
|
55
|
-
'
|
|
95
|
+
'Objective 1: Protect and restore marine and freshwater ecosystems and biodiversity':
|
|
96
|
+
'#007b6c',
|
|
97
|
+
'Objective 2: Prevent and eliminate pollution of our oceans, seas and waters':
|
|
98
|
+
'#fdaf20',
|
|
99
|
+
'Objective 3: Make the sustainable blue economy carbon-neutral and circular':
|
|
100
|
+
'#004b7f',
|
|
101
|
+
'Enabler 1: Digital twin of the ocean': '#f9eb8a',
|
|
102
|
+
'Enabler 2: Public mobilisation and engagement': '#9e83b6',
|
|
56
103
|
};
|
|
57
104
|
const width = {
|
|
58
105
|
'Demo site': 6,
|
|
@@ -85,10 +132,11 @@ export function getFeatures(cases) {
|
|
|
85
132
|
info: c.properties.info,
|
|
86
133
|
website: c.properties.website,
|
|
87
134
|
objective: c.properties.objective,
|
|
135
|
+
target: c.properties.target,
|
|
88
136
|
description: c.properties.description,
|
|
89
137
|
index: index,
|
|
90
138
|
path: c.properties.path,
|
|
91
|
-
color: colors[c.properties.objective] || '#B83230',
|
|
139
|
+
color: colors[c.properties.objective[0]] || '#B83230',
|
|
92
140
|
width: width[c.properties.type_is_region],
|
|
93
141
|
radius: radius[c.properties.type_is_region],
|
|
94
142
|
},
|
|
@@ -101,6 +149,7 @@ export function getFeatures(cases) {
|
|
|
101
149
|
export function filterCases(cases, activeFilters, indicatorOnly) {
|
|
102
150
|
const data = cases.filter((_case) => {
|
|
103
151
|
let flag_objective = false;
|
|
152
|
+
let flag_target = false;
|
|
104
153
|
let flag_indicator = false;
|
|
105
154
|
let flag_project = false;
|
|
106
155
|
let flag_country = false;
|
|
@@ -122,7 +171,17 @@ export function filterCases(cases, activeFilters, indicatorOnly) {
|
|
|
122
171
|
let objective = _case.properties.objective;
|
|
123
172
|
|
|
124
173
|
activeFilters.objective_filter.forEach((filter) => {
|
|
125
|
-
if (objective
|
|
174
|
+
if (objective?.includes(filter)) flag_objective = true;
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (!activeFilters.target_filter.length) {
|
|
179
|
+
flag_target = true;
|
|
180
|
+
} else {
|
|
181
|
+
let target = _case.properties.target;
|
|
182
|
+
|
|
183
|
+
activeFilters.target_filter.forEach((filter) => {
|
|
184
|
+
if (target?.includes(filter)) flag_target = true;
|
|
126
185
|
});
|
|
127
186
|
}
|
|
128
187
|
|
|
@@ -162,6 +221,7 @@ export function filterCases(cases, activeFilters, indicatorOnly) {
|
|
|
162
221
|
|
|
163
222
|
return flag_indicatorOnly &&
|
|
164
223
|
flag_objective &&
|
|
224
|
+
flag_target &&
|
|
165
225
|
flag_indicator &&
|
|
166
226
|
flag_country &&
|
|
167
227
|
flag_project
|
|
@@ -175,6 +235,7 @@ export function filterCases(cases, activeFilters, indicatorOnly) {
|
|
|
175
235
|
export function getFilters(cases, indicatorOnly) {
|
|
176
236
|
let _filters = {
|
|
177
237
|
objective_filter: {},
|
|
238
|
+
target_filter: {},
|
|
178
239
|
indicator_filter: {},
|
|
179
240
|
project_filter: {},
|
|
180
241
|
country_filter: {},
|
|
@@ -188,6 +249,7 @@ export function getFilters(cases, indicatorOnly) {
|
|
|
188
249
|
indicators.map((item) => {
|
|
189
250
|
if (
|
|
190
251
|
item['title'] &&
|
|
252
|
+
item['title'] !== '0' &&
|
|
191
253
|
!_filters.indicator_filter.hasOwnProperty('_' + item['id'])
|
|
192
254
|
) {
|
|
193
255
|
_filters.indicator_filter['_' + item['id']] = item['title'];
|
|
@@ -196,9 +258,20 @@ export function getFilters(cases, indicatorOnly) {
|
|
|
196
258
|
});
|
|
197
259
|
|
|
198
260
|
let objective = _case.properties.objective;
|
|
199
|
-
|
|
200
|
-
_filters.objective_filter
|
|
201
|
-
|
|
261
|
+
objective.map((item) => {
|
|
262
|
+
if (item && !_filters.objective_filter.hasOwnProperty(item)) {
|
|
263
|
+
_filters.objective_filter[item] = item;
|
|
264
|
+
}
|
|
265
|
+
return [];
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
let target = _case.properties.target;
|
|
269
|
+
target.map((item) => {
|
|
270
|
+
if (item && !_filters.target_filter.hasOwnProperty(item)) {
|
|
271
|
+
_filters.target_filter[item] = item;
|
|
272
|
+
}
|
|
273
|
+
return [];
|
|
274
|
+
});
|
|
202
275
|
|
|
203
276
|
let project = _case.properties.project;
|
|
204
277
|
if (project && !_filters.project_filter.hasOwnProperty(project)) {
|