datastake-daf 0.6.816 → 0.6.818

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 (37) hide show
  1. package/dist/components/index.js +1236 -1281
  2. package/dist/pages/index.js +1326 -426
  3. package/dist/services/index.js +202 -0
  4. package/dist/utils/index.js +28 -0
  5. package/package.json +1 -1
  6. package/public/Vegetation/damage-from-insects-default.svg +1 -0
  7. package/public/Vegetation/dry-or-dead-default.svg +1 -0
  8. package/public/Vegetation/healthy-default.svg +1 -0
  9. package/public/Vegetation/yellowing.svg +1 -0
  10. package/src/@daf/core/components/Charts/RadarChart/index.jsx +51 -9
  11. package/src/@daf/core/components/Charts/style.js +0 -1
  12. package/src/@daf/core/components/Dashboard/Map/ChainIcon/index.js +123 -104
  13. package/src/@daf/core/components/Table/index.jsx +11 -6
  14. package/src/@daf/pages/Events/Activities/columns.js +15 -11
  15. package/src/@daf/pages/Events/Incidents/columns.js +15 -11
  16. package/src/@daf/pages/Events/Testimonials/columns.js +173 -0
  17. package/src/@daf/pages/Events/Testimonials/config.js +175 -0
  18. package/src/@daf/pages/Events/columns.js +7 -3
  19. package/src/@daf/pages/Locations/ConflictAreas/columns.js +140 -0
  20. package/src/@daf/pages/Locations/ConflictAreas/config.js +41 -0
  21. package/src/@daf/pages/Locations/MineSite/columns.js +21 -12
  22. package/src/@daf/pages/Locations/MineSite/config.js +2 -1
  23. package/src/@daf/pages/Locations/columns.js +7 -3
  24. package/src/@daf/pages/Stakeholders/ArmedGroups/columns.js +110 -0
  25. package/src/@daf/pages/Stakeholders/ArmedGroups/config.js +41 -0
  26. package/src/@daf/pages/Stakeholders/Operators/columns.js +30 -14
  27. package/src/@daf/pages/Stakeholders/Workers/columns.js +23 -13
  28. package/src/@daf/pages/Stakeholders/columns.js +8 -4
  29. package/src/@daf/pages/Summary/Activities/MonitoringCampaign/components/BiodiversityHabitat/index.jsx +4 -2
  30. package/src/@daf/pages/TablePage/config.js +1 -1
  31. package/src/@daf/pages/TablePage/helper.js +45 -0
  32. package/src/@daf/services/EventsService.js +115 -0
  33. package/src/@daf/services/LinkedSubjects.js +1 -0
  34. package/src/@daf/services/WorkersService.js +80 -0
  35. package/src/helpers/errorHandling.js +142 -74
  36. package/src/services.js +3 -1
  37. package/src/utils.js +1 -1
@@ -1,4 +1,5 @@
1
1
  import { useMemo, useEffect, useRef, useState } from "react";
2
+ import { createPortal } from "react-dom";
2
3
  import {
3
4
  isMineSite,
4
5
  getStakeholderPosition,
@@ -10,7 +11,6 @@ import {
10
11
  import MineSiteMarker from "./Markers/SVG/MinesiteMarker.js";
11
12
  import VillageMarker from "./Markers/SVG/VillageMarker.js";
12
13
  import StakeholderIcon from "./Markers/StakeholderMarker.js";
13
- import { createRoot } from "react-dom/client";
14
14
  import * as L from "leaflet";
15
15
  import { Popover } from "antd";
16
16
  import { renderTooltipJsx } from "../../../../../utils/tooltip.js";
@@ -32,8 +32,8 @@ export default function LocationIcon({
32
32
  activeMarker,
33
33
  setActiveMarker,
34
34
  }) {
35
- const rootsMapRef = useRef(new Map());
36
- const markersRef = useRef([])
35
+ const markersRef = useRef([]);
36
+ const [portalContainers, setPortalContainers] = useState([]);
37
37
  const isSelected = selectedMarkersId.includes(data.datastakeId);
38
38
  const Marker = useMemo(() => {
39
39
  if (isMineSite(data.type)) {
@@ -114,25 +114,31 @@ export default function LocationIcon({
114
114
  }, [JSON.stringify(allData), JSON.stringify(data.links), JSON.stringify(data.stakeholders), zoom]);
115
115
 
116
116
  const stakeholdersOfLocation = useMemo(() => {
117
- return data?.stakeholders || [];
118
- }, [data.stakeholders, zoom]);
117
+ return (data?.stakeholders || []).filter(stakeholder => {
118
+ if (!stakeholder.links || stakeholder.links.length === 0) {
119
+ return true;
120
+ }
121
+
122
+ const locationsWithThisStakeholder = allData
123
+ .filter(loc =>
124
+ loc.stakeholders?.some(s => s.datastakeId === stakeholder.datastakeId)
125
+ )
126
+ .map(loc => loc.datastakeId);
127
+
128
+ const primaryLocation = locationsWithThisStakeholder.sort()[0];
129
+ return data.datastakeId === primaryLocation;
130
+ });
131
+ }, [data.stakeholders, data.datastakeId, allData, zoom]);
119
132
 
120
133
  useEffect(() => {
121
- const currentRoots = rootsMapRef.current;
122
- const currentMarkers = markersRef.current;
123
-
124
- currentMarkers.forEach(marker => {
125
- if (mapRef.hasLayer(marker)) {
134
+ markersRef.current.forEach(marker => {
135
+ if (mapRef && mapRef.hasLayer(marker)) {
126
136
  mapRef.removeLayer(marker);
127
137
  }
128
138
  });
129
- currentRoots.forEach(root => {
130
- root.unmount();
131
- });
132
- currentRoots.clear();
133
139
  markersRef.current = [];
140
+ setPortalContainers([]);
134
141
 
135
- // Only create stakeholder markers if this location or any of its stakeholders are selected
136
142
  const shouldShowStakeholders = isSelected || stakeholdersOfLocation.some(stk =>
137
143
  selectedMarkersId.includes(stk.datastakeId)
138
144
  );
@@ -141,9 +147,11 @@ export default function LocationIcon({
141
147
  return;
142
148
  }
143
149
 
144
- // Create new markers only when selected
150
+ // Create markers and store their container references
151
+ const containers = [];
152
+
145
153
  stakeholdersOfLocation.forEach((stakeholder, index) => {
146
- const markerId = `${stakeholder.datastakeId}`;
154
+ const markerId = `${data.datastakeId}-${stakeholder.datastakeId}`;
147
155
  const { x, y, radius, center } = getStakeholderPosition({
148
156
  zoom,
149
157
  totalMarkers: stakeholdersOfLocation.length,
@@ -162,46 +170,30 @@ export default function LocationIcon({
162
170
  const isForceOpen = activeMarker?.datastakeId === data.datastakeId;
163
171
  const iconSize = isSmallMarker(zoom) || isExtraSmallMarker(zoom) ? [11, 11] : [25, 25];
164
172
 
173
+ // Create container div
174
+ const containerDiv = document.createElement('div');
175
+ containerDiv.id = markerId;
176
+
165
177
  const marker = L.marker(stakeholderLatLng, {
166
178
  icon: L.divIcon({
167
- html: `<div id="${markerId}"></div>`,
179
+ html: containerDiv.outerHTML,
168
180
  className: "marker-chain",
169
181
  iconSize: iconSize,
170
182
  }),
171
183
  }).addTo(mapRef);
172
184
 
173
185
  markersRef.current.push(marker);
174
-
175
- setTimeout(() => {
176
- const div = document.getElementById(markerId);
177
- if (div && !rootsMapRef.current.has(markerId)) {
178
- const root = createRoot(div);
179
- rootsMapRef.current.set(markerId, root);
180
-
181
- root.render(
182
- <StakeholderIcon
183
- data={stakeholder}
184
- zoom={zoom}
185
- allData={allData}
186
- link={link}
187
- parentId={data.datastakeId}
188
- renderTooltip={renderTooltip}
189
- onClickLink={onClickLink}
190
- selectedMarkersId={selectedMarkersId}
191
- handleSelectMarker={handleSelectMarker}
192
- mapRef={mapRef}
193
- radius={radius}
194
- index={index}
195
- x={x}
196
- y={y}
197
- openPopupIdRef={openPopupIdRef}
198
- polylinesRef={polylinesRef}
199
- isForceOpen={isForceOpen}
200
- activeMarker={activeMarker}
201
- />,
202
- );
203
- }
204
- }, 0);
186
+
187
+ // Store container info for portal rendering
188
+ containers.push({
189
+ markerId,
190
+ stakeholder,
191
+ x,
192
+ y,
193
+ radius,
194
+ index,
195
+ isForceOpen,
196
+ });
205
197
 
206
198
  setMapMarkers((prev) => {
207
199
  const array = [
@@ -236,21 +228,23 @@ export default function LocationIcon({
236
228
  isForceOpen,
237
229
  listOfPolylines: polylinesRef.current,
238
230
  stakeholderType: stakeholder.type,
239
- animated: true,
231
+ animated: true,
240
232
  });
241
233
  });
242
234
 
235
+ // Update portal containers after markers are created
236
+ setTimeout(() => {
237
+ setPortalContainers(containers);
238
+ }, 0);
239
+
243
240
  return () => {
244
241
  markersRef.current.forEach(marker => {
245
- if (mapRef.hasLayer(marker)) {
242
+ if (mapRef && mapRef.hasLayer(marker)) {
246
243
  mapRef.removeLayer(marker);
247
244
  }
248
245
  });
249
- rootsMapRef.current.forEach(root => {
250
- root.unmount();
251
- });
252
- rootsMapRef.current.clear();
253
246
  markersRef.current = [];
247
+ setPortalContainers([]);
254
248
  };
255
249
  }, [stakeholdersOfLocation, selectedMarkersId, activeMarker, zoom]);
256
250
 
@@ -344,56 +338,81 @@ useEffect(() => {
344
338
  }, [linkedNodesData, selectedMarkersId, zoom, stakeholdersOfLocation, isSelected]);
345
339
 
346
340
  return (
347
- <Popover
348
- content={renderTooltipJsx({
349
- title: data.name,
350
- subTitle: data.subTitle,
351
- total: data.sources,
352
- className: "pt-0 pb-0",
353
- items: renderTooltip(data),
354
- link,
355
- onClickLink: () => onClickLink(data),
356
- isNewTab: true,
357
- })}
358
- // open={
359
- // (!openPopupIdRef.current || openPopupIdRef.current === data.datastakeId) &&
360
- // isHovering
361
- // }
362
- getPopupContainer={(triggerNode) => {
363
- const mapElement = document.getElementById("map");
364
- return mapElement || triggerNode.parentElement || document.body;
365
- }}
366
- >
367
- <div style={{ position: "relative", display: "inline-block" }}>
368
- {(isSelected || selectedMarkersId.length === 0) && (
369
- <div
370
- style={{
371
- position: "absolute",
372
- bottom: "0",
373
- left: "50%",
374
- transform: "translateX(-50%)",
375
- width: "12px",
376
- height: "6px",
377
- background: "rgba(0,0,0,0.15)",
378
- borderRadius: "50%",
379
- filter: "blur(3px)",
341
+ <>
342
+ <Popover
343
+ content={renderTooltipJsx({
344
+ title: data.name,
345
+ subTitle: data.subTitle,
346
+ total: data.sources,
347
+ className: "pt-0 pb-0",
348
+ items: renderTooltip(data),
349
+ link,
350
+ onClickLink: () => onClickLink(data),
351
+ isNewTab: true,
352
+ })}
353
+ getPopupContainer={(triggerNode) => {
354
+ const mapElement = document.getElementById("map");
355
+ return mapElement || triggerNode.parentElement || document.body;
356
+ }}
357
+ >
358
+ <div style={{ position: "relative", display: "inline-block" }}>
359
+ {(isSelected || selectedMarkersId.length === 0) && (
360
+ <div
361
+ style={{
362
+ position: "absolute",
363
+ bottom: "0",
364
+ left: "50%",
365
+ transform: "translateX(-50%)",
366
+ width: "12px",
367
+ height: "6px",
368
+ background: "rgba(0,0,0,0.15)",
369
+ borderRadius: "50%",
370
+ filter: "blur(3px)",
371
+ }}
372
+ />
373
+ )}
374
+ <Marker
375
+ isSelected={isSelected}
376
+ onClick={() => {
377
+ handleSelectMarker(data);
378
+ setActiveMarker(isSelected ? null : data);
380
379
  }}
380
+ zoom={zoom}
381
+ selectedMarkersId={selectedMarkersId}
381
382
  />
382
- )}
383
- <Marker
384
- isSelected={isSelected}
385
- onClick={() => {
386
- handleSelectMarker(data);
387
- setActiveMarker(isSelected ? null : data);
388
- }}
389
- zoom={zoom}
390
- onMouseEnter={() => setIsHovering(true)}
391
- onMouseLeave={() => {
392
- setIsHovering(false);
393
- }}
394
- selectedMarkersId={selectedMarkersId}
395
- />
396
- </div>
397
- </Popover>
383
+ </div>
384
+ </Popover>
385
+
386
+ {/* Render stakeholder icons via portals */}
387
+ {portalContainers.map(({markerId, stakeholder, x, y, radius, index, isForceOpen}) => {
388
+ const container = document.getElementById(markerId);
389
+ if (!container) return null;
390
+
391
+ return createPortal(
392
+ <StakeholderIcon
393
+ key={markerId}
394
+ data={stakeholder}
395
+ zoom={zoom}
396
+ allData={allData}
397
+ link={link}
398
+ parentId={data.datastakeId}
399
+ renderTooltip={renderTooltip}
400
+ onClickLink={onClickLink}
401
+ selectedMarkersId={selectedMarkersId}
402
+ handleSelectMarker={handleSelectMarker}
403
+ mapRef={mapRef}
404
+ radius={radius}
405
+ index={index}
406
+ x={x}
407
+ y={y}
408
+ openPopupIdRef={openPopupIdRef}
409
+ polylinesRef={polylinesRef}
410
+ isForceOpen={isForceOpen}
411
+ activeMarker={activeMarker}
412
+ />,
413
+ container
414
+ );
415
+ })}
416
+ </>
398
417
  );
399
418
  }
@@ -36,7 +36,12 @@ export default function DAFTable({
36
36
  doEmptyRows,
37
37
  ...rest
38
38
  }) {
39
- const [source, setSource] = useState([]);
39
+ const source = useMemo(() => {
40
+ if (data && Array.isArray(data)) {
41
+ return data;
42
+ }
43
+ return [];
44
+ }, [data]);
40
45
  const projectData = (projects || []).find(p => p.id === selectedProject);
41
46
  const [filtersInit, setFiltersInit] = useState(!loading);
42
47
 
@@ -85,11 +90,11 @@ export default function DAFTable({
85
90
  } : filtersConfig;
86
91
  }, [sourcesKey, sources, filtersConfig, t]);
87
92
 
88
- useEffect(() => {
89
- if (data && Array.isArray(data)) {
90
- setSource(data);
91
- }
92
- }, [data, data.length]);
93
+ // useEffect(() => {
94
+ // if (data && Array.isArray(data)) {
95
+ // setSource(data);
96
+ // }
97
+ // }, [data, data.length]);
93
98
 
94
99
  const paginationPageSize = pagination?.pageSize;
95
100
  const dataSource = useMemo(() => {
@@ -117,19 +117,23 @@ export const getColumns = ({t, goTo, user, options, activeTab, getRedirectLink,
117
117
  },
118
118
  },
119
119
  {
120
- title: t("Sources"),
121
- dataIndex: "sources",
122
- key: "sources",
123
- show: activeTab !== "own",
124
- render: (val, all) => {
125
- if (all.empty) {
126
- return <div className="daf-default-cell" />;
127
- }
120
+ title: t("Sources"),
121
+ dataIndex: "sources",
122
+ key: "sources",
123
+ show: activeTab !== "own",
124
+ render: (val, all) => {
125
+ if (all.empty) {
126
+ return <div className="daf-default-cell" />;
127
+ }
128
128
 
129
- const sources = sourceAvatarConfig(val, user, applications);
129
+ if (!val || val?.length === 0) {
130
+ return "-";
131
+ }
130
132
 
131
- return <AvatarGroup items={sources}></AvatarGroup>;
132
- },
133
+ const sources = sourceAvatarConfig(val, user, applications);
134
+
135
+ return <AvatarGroup items={sources} />;
136
+ },
133
137
  },
134
138
  {
135
139
  title: t("Status"),
@@ -117,19 +117,23 @@ export const getColumns = ({t, goTo, user, options, activeTab, getRedirectLink,
117
117
  },
118
118
  },
119
119
  {
120
- title: t("Sources"),
121
- dataIndex: "sources",
122
- key: "sources",
123
- show: activeTab !== "own",
124
- render: (val, all) => {
125
- if (all.empty) {
126
- return <div className="daf-default-cell" />;
127
- }
120
+ title: t("Sources"),
121
+ dataIndex: "sources",
122
+ key: "sources",
123
+ show: activeTab !== "own",
124
+ render: (val, all) => {
125
+ if (all.empty) {
126
+ return <div className="daf-default-cell" />;
127
+ }
128
128
 
129
- const sources = sourceAvatarConfig(val, user, applications);
129
+ if (!val || val?.length === 0) {
130
+ return "-";
131
+ }
130
132
 
131
- return <AvatarGroup items={sources}></AvatarGroup>;
132
- },
133
+ const sources = sourceAvatarConfig(val, user, applications);
134
+
135
+ return <AvatarGroup items={sources} />;
136
+ },
133
137
  },
134
138
  {
135
139
  title: t("Status"),
@@ -0,0 +1,173 @@
1
+ import React from 'react';
2
+ import { Tooltip, Tag } from 'antd';
3
+ import { findOptions, getLinkValue } from '../../../../helpers/StringHelper.js';
4
+ import { renderDateFormatted } from '../../../../helpers/Forms.js';
5
+ import AvatarGroup from '../../../core/components/AvatarGroup/index.jsx';
6
+ import sourceAvatarConfig from '../../../../helpers/sourceAvatarConfig.js';
7
+ import { getEventCategoryBySubject } from '../helper.js';
8
+ import { renderStatusTag } from '../../../utils/tags.js';
9
+ import NavigationAction from '../../../core/components/Table/NavigationAction/index.jsx';
10
+
11
+ export const getColumns = ({t, goTo, user, options, activeTab, getRedirectLink, theme, subject, data, applications}) => [
12
+ {
13
+ dataIndex: 'datastakeId',
14
+ title: t('ID'),
15
+ ellipsis: true,
16
+ show: true,
17
+ key: "datastakeId",
18
+ sorter: () => 0 + 0,
19
+ render: (v, all) => {
20
+ if (all.empty) {
21
+ return <div className="daf-default-cell" />
22
+ }
23
+
24
+ return <Tooltip title={v}>{v}</Tooltip>;
25
+ },
26
+ },
27
+ {
28
+ dataIndex: 'name',
29
+ title: t('Title'),
30
+ ellipsis: true,
31
+ show: true,
32
+ key: "name",
33
+ sorter: () => 0 + 0,
34
+ render: (v, all) => {
35
+ if (all.empty) {
36
+ return <div className="daf-default-cell" />
37
+ }
38
+
39
+ return <Tooltip title={v}>{v}</Tooltip>;
40
+ },
41
+ },
42
+ {
43
+ title: t("type"),
44
+ dataIndex: "typeOfTestimonials",
45
+ key: "typeOfTestimonials",
46
+ sorter: () => 0 + 0,
47
+ show: true,
48
+ render: (title, all) => {
49
+ if (all.empty) {
50
+ return <div className="daf-default-cell" />;
51
+ }
52
+
53
+ const type = findOptions(title, data?.options?.testimonialsType);
54
+
55
+ return type ? <Tooltip title={type}>{type}</Tooltip> : '-';
56
+ },
57
+ ellipsis: true,
58
+ },
59
+ {
60
+ title: t("Date"),
61
+ dataIndex: "date",
62
+ key: "date",
63
+ sorter: () => 0 + 0,
64
+ render: (date, all) => {
65
+ if (all.empty) {
66
+ return <div className="daf-default-cell" />;
67
+ }
68
+
69
+ const _date = date ? renderDateFormatted(date, "DD MMM YYYY", user?.language || 'en') : "-";
70
+ return <Tooltip title={_date}>{_date}</Tooltip>;
71
+ },
72
+ ellipsis: true,
73
+ },
74
+ {
75
+ dataIndex: 'mineSite',
76
+ title: t('Location'),
77
+ ellipsis: true,
78
+ show: true,
79
+ render: (v, all) => {
80
+ if (all.empty) {
81
+ return <div className="daf-default-cell" />
82
+ }
83
+
84
+ // const country = findOptions(v, data?.options?.positionSupplyChainOptions);
85
+ const mineSite = all?.location?.name
86
+
87
+ return mineSite ? <Tooltip title={mineSite}>{mineSite}</Tooltip> : '-';
88
+ },
89
+ },
90
+ {
91
+ dataIndex: 'eventCategory',
92
+ title: t('Category'),
93
+ ellipsis: true,
94
+ show: true,
95
+ render: (v, all) => {
96
+ if (all.empty) {
97
+ return <div className="daf-default-cell" />
98
+ }
99
+
100
+ const eventCategory = findOptions(v, data?.options?.eventCategoryOptions || data?.options?.categoryOptions);
101
+ const categoryValue = getEventCategoryBySubject(eventCategory, subject);
102
+
103
+ return categoryValue ? <Tooltip title={categoryValue}>{categoryValue}</Tooltip> : '-';
104
+ },
105
+ },
106
+ {
107
+ title: t("Sources"),
108
+ dataIndex: "sources",
109
+ key: "sources",
110
+ show: activeTab !== "own",
111
+ render: (val, all) => {
112
+ if (all.empty) {
113
+ return <div className="daf-default-cell" />;
114
+ }
115
+
116
+ if (!val || val?.length === 0) {
117
+ return "-";
118
+ }
119
+
120
+ const sources = sourceAvatarConfig(val, user, applications);
121
+
122
+ return <AvatarGroup items={sources} />;
123
+ },
124
+ },
125
+ {
126
+ title: t("Status"),
127
+ dataIndex: "status",
128
+ key: "status",
129
+ show: activeTab === "own",
130
+ render: (val, all) => {
131
+ if (all.empty) {
132
+ return <div className="daf-default-cell" />
133
+ }
134
+
135
+ const _val = all?.published || all?.status === "submitted" ? "submitted" : val;
136
+
137
+ return renderStatusTag({ value: _val, t });
138
+ },
139
+ },
140
+ {
141
+ title: t("Last Update"),
142
+ dataIndex: "updatedAt",
143
+ key: "updatedAt",
144
+ render: (date, all) => {
145
+ if (all.empty) {
146
+ return <div className="daf-default-cell" />;
147
+ }
148
+
149
+ const _date = date ? renderDateFormatted(date, "DD MMM YYYY", user?.language || 'en') : "-";
150
+ return <Tooltip title={_date}>{_date}</Tooltip>;
151
+ },
152
+ ellipsis: true,
153
+ },
154
+ {
155
+ id: 'actions',
156
+ title: "",
157
+ width: 60,
158
+ render: (_, all) => {
159
+ if (all.empty) {
160
+ return <div className="daf-default-cell" />;
161
+ }
162
+ const onClick = () => {
163
+ let link = `/app/view/${subject}/${all.datastakeId}`;
164
+ if (activeTab === "shared") {
165
+ link += `?sourceId=${all?.authorId?.id}`;
166
+ }
167
+ goTo(getRedirectLink(link));
168
+ };
169
+
170
+ return <NavigationAction onClick={onClick} theme={theme} />;
171
+ }
172
+ }
173
+ ].filter((column) => column.show !== false);