datastake-daf 0.6.781 → 0.6.783
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/dist/components/index.js +383 -317
- package/dist/pages/index.js +2577 -258
- package/dist/utils/index.js +13 -0
- package/package.json +1 -1
- package/src/@daf/core/components/Dashboard/Map/ChainIcon/Markers/StakeholderMarker.js +9 -76
- package/src/@daf/core/components/Dashboard/Map/ChainIcon/index.js +116 -8
- package/src/@daf/core/components/Dashboard/Map/ChainIcon/utils.js +73 -17
- package/src/@daf/core/components/Dashboard/Map/helper.js +1 -0
- package/src/@daf/core/components/Dashboard/Map/hook.js +64 -29
- package/src/@daf/core/components/Dashboard/Map/style.js +20 -5
- package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleOutcomes/index.jsx +1 -1
- package/src/@daf/pages/Summary/Activities/PlantingCycle/components/PlantingLocations/index.jsx +2 -2
- package/src/@daf/pages/Template/components/LinkingTemplate/columns.js +95 -0
- package/src/@daf/pages/Template/components/LinkingTemplate/config.js +75 -0
- package/src/@daf/pages/Template/components/LinkingTemplate/index.jsx +119 -0
- package/src/@daf/pages/Template/index.jsx +19 -0
- package/src/@daf/pages/View/hooks/useCallToGetData.js +73 -0
- package/src/@daf/pages/View/hooks/usePrepareForm.js +86 -0
- package/src/@daf/pages/View/hooks/useSubmitSubject.js +40 -0
- package/src/@daf/pages/View/hooks/useViewActions.js +83 -0
- package/src/@daf/pages/View/hooks/useViewPermissions.js +74 -0
- package/src/@daf/pages/View/hooks/useViewUrlParams.js +93 -0
- package/src/@daf/pages/View/index.jsx +325 -0
- package/src/@daf/utils/object.js +3 -1
- package/src/pages.js +4 -1
- package/src/utils.js +1 -1
- package/dist/style/datastake/mapbox-gl.css +0 -330
- package/src/@daf/hooks/useViewFormUrlParams.js +0 -84
package/dist/utils/index.js
CHANGED
|
@@ -7211,6 +7211,17 @@ function convertUndefinedToNull(obj) {
|
|
|
7211
7211
|
}
|
|
7212
7212
|
return obj;
|
|
7213
7213
|
}
|
|
7214
|
+
const removeKeysFromObject = (obj = {}, keys = []) => {
|
|
7215
|
+
if (typeof obj !== 'object' || obj === null) return obj;
|
|
7216
|
+
const result = {};
|
|
7217
|
+
for (const key of Object.keys(obj)) {
|
|
7218
|
+
if (!keys.includes(key)) {
|
|
7219
|
+
result[key] = obj[key];
|
|
7220
|
+
}
|
|
7221
|
+
}
|
|
7222
|
+
return result;
|
|
7223
|
+
};
|
|
7224
|
+
const hasKeyInObject = (obj, key) => Object.keys(obj || {}).includes(key);
|
|
7214
7225
|
|
|
7215
7226
|
({
|
|
7216
7227
|
t: PropTypes__default["default"].func,
|
|
@@ -14935,6 +14946,7 @@ exports.handleError = handleError;
|
|
|
14935
14946
|
exports.handleInfo = handleInfo;
|
|
14936
14947
|
exports.handleSuccess = handleSuccess;
|
|
14937
14948
|
exports.handleWarning = handleWarning;
|
|
14949
|
+
exports.hasKeyInObject = hasKeyInObject;
|
|
14938
14950
|
exports.hasNotChanged = hasNotChanged;
|
|
14939
14951
|
exports.isEmptyOrSpaces = isEmptyOrSpaces;
|
|
14940
14952
|
exports.isModuleApproved = isModuleApproved;
|
|
@@ -14950,6 +14962,7 @@ exports.modules = modules;
|
|
|
14950
14962
|
exports.nowToIso = nowToIso;
|
|
14951
14963
|
exports.processConfig = processConfig;
|
|
14952
14964
|
exports.propHasValue = propHasValue;
|
|
14965
|
+
exports.removeKeysFromObject = removeKeysFromObject;
|
|
14953
14966
|
exports.renderBreadCrumbs = renderBreadCrumbs;
|
|
14954
14967
|
exports.renderDateFormatted = renderDateFormatted;
|
|
14955
14968
|
exports.renderNumber = renderNumber;
|
package/package.json
CHANGED
|
@@ -88,6 +88,10 @@ export default function StakeholderIcon({
|
|
|
88
88
|
}, [parentId, allData]);
|
|
89
89
|
|
|
90
90
|
useEffect(() => {
|
|
91
|
+
if (selectedMarkersId.length === 0 || !isSelected) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
91
95
|
linkNodesData.map((node) => {
|
|
92
96
|
const isConnectingToStakeholder = node.isStakeholder;
|
|
93
97
|
const id = `${data.datastakeId}-${node.stakeholderId || node.datastakeId}`;
|
|
@@ -101,13 +105,6 @@ export default function StakeholderIcon({
|
|
|
101
105
|
const stakeholderLatLng = mapRef.layerPointToLatLng(stakeholderPoint);
|
|
102
106
|
let endLatLng = L.latLng(node.gps.latitude, node.gps.longitude);
|
|
103
107
|
|
|
104
|
-
const areNextToEachOther =
|
|
105
|
-
targetMarkerIndex === index + 1 ||
|
|
106
|
-
targetMarkerIndex === index - 1 ||
|
|
107
|
-
(index === 0 && targetMarkerIndex === node.totalStakeholders - 1) ||
|
|
108
|
-
(targetMarkerIndex === 0 && index === node.totalStakeholders - 1);
|
|
109
|
-
const areOnlyTwoSiblings = node.totalStakeholders === 2;
|
|
110
|
-
|
|
111
108
|
if (isExtraSmallMarker(zoom) && !isForceOpen) {
|
|
112
109
|
createPolyline({
|
|
113
110
|
L,
|
|
@@ -118,6 +115,7 @@ export default function StakeholderIcon({
|
|
|
118
115
|
isSelected,
|
|
119
116
|
id,
|
|
120
117
|
listOfPolylines: polylinesRef.current,
|
|
118
|
+
animated: true,
|
|
121
119
|
});
|
|
122
120
|
return;
|
|
123
121
|
}
|
|
@@ -137,75 +135,8 @@ export default function StakeholderIcon({
|
|
|
137
135
|
y + nodePoint.y + center.top,
|
|
138
136
|
);
|
|
139
137
|
endLatLng = mapRef.layerPointToLatLng(endPoint);
|
|
140
|
-
|
|
141
|
-
if (isSibling && (!areNextToEachOther || areOnlyTwoSiblings)) {
|
|
142
|
-
if (areOnlyTwoSiblings) {
|
|
143
|
-
buildCurveWIthTwoSiblings({
|
|
144
|
-
mapRef,
|
|
145
|
-
startLatLng: stakeholderLatLng,
|
|
146
|
-
endLatLng,
|
|
147
|
-
zoom,
|
|
148
|
-
isSelected,
|
|
149
|
-
id,
|
|
150
|
-
});
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const total = node.totalStakeholders;
|
|
155
|
-
let from = index;
|
|
156
|
-
let to = targetMarkerIndex;
|
|
157
|
-
let flip = false;
|
|
158
|
-
|
|
159
|
-
const forwardDistance = (to - from + total) % total;
|
|
160
|
-
const backwardDistance = (from - to + total) % total;
|
|
161
|
-
|
|
162
|
-
if (backwardDistance < forwardDistance) {
|
|
163
|
-
[from, to] = [to, from];
|
|
164
|
-
flip = true;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const intermediateIndices = [];
|
|
168
|
-
for (let i = 1; i < (to - from + total) % total; i++) {
|
|
169
|
-
intermediateIndices.push((from + i) % total);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const indices = [from, ...intermediateIndices, to];
|
|
173
|
-
|
|
174
|
-
const intermediatePoints = [];
|
|
175
|
-
|
|
176
|
-
for (const i of indices) {
|
|
177
|
-
const { x, y } = createCurvePath({
|
|
178
|
-
zoom,
|
|
179
|
-
totalMarkers: node.totalStakeholders,
|
|
180
|
-
markerIndex: i,
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
const point = centerPoint.add(L.point(x, y));
|
|
184
|
-
const latlng = mapRef.layerPointToLatLng(point);
|
|
185
|
-
intermediatePoints.push(latlng);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
const latlngs = flip
|
|
189
|
-
? [endLatLng, ...intermediatePoints, stakeholderLatLng]
|
|
190
|
-
: [stakeholderLatLng, ...intermediatePoints, endLatLng];
|
|
191
|
-
|
|
192
|
-
const layerPoints = latlngs.map((latlng) => mapRef.latLngToLayerPoint(latlng));
|
|
193
|
-
|
|
194
|
-
const path = buildSmoothCurve(layerPoints, mapRef, flip);
|
|
195
|
-
|
|
196
|
-
const curve = L?.curve?.(path, {
|
|
197
|
-
color: "var(--base-gray-70)",
|
|
198
|
-
weight: isExtraSmallMarker(zoom) ? 0 : 1,
|
|
199
|
-
opacity: isSelected ? 1 : 0.5,
|
|
200
|
-
smoothFactor: 1,
|
|
201
|
-
id,
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
mapRef.addLayer(curve);
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
138
|
}
|
|
208
|
-
|
|
139
|
+
// Always use straight lines
|
|
209
140
|
createPolyline({
|
|
210
141
|
L,
|
|
211
142
|
mapRef,
|
|
@@ -216,9 +147,10 @@ export default function StakeholderIcon({
|
|
|
216
147
|
isSelected,
|
|
217
148
|
id,
|
|
218
149
|
listOfPolylines: polylinesRef.current,
|
|
150
|
+
animated: true,
|
|
219
151
|
});
|
|
220
152
|
});
|
|
221
|
-
}, [mapRef, x, y, parentData, linkNodesData, isSelected, zoom, isForceOpen]);
|
|
153
|
+
}, [mapRef, x, y, parentData, linkNodesData, isSelected, zoom, isForceOpen, selectedMarkersId]);
|
|
222
154
|
|
|
223
155
|
return (
|
|
224
156
|
<>
|
|
@@ -233,6 +165,7 @@ export default function StakeholderIcon({
|
|
|
233
165
|
onClickLink: () => {
|
|
234
166
|
onClickLink(data);
|
|
235
167
|
},
|
|
168
|
+
isNewTab: true,
|
|
236
169
|
})}
|
|
237
170
|
getPopupContainer={(triggerNode) => {
|
|
238
171
|
const mapElement = document.getElementById("map");
|
|
@@ -48,6 +48,7 @@ export default function LocationIcon({
|
|
|
48
48
|
const nodes = [];
|
|
49
49
|
const links = data.links || [];
|
|
50
50
|
|
|
51
|
+
// Add links from the location itself
|
|
51
52
|
links.forEach((link) => {
|
|
52
53
|
allData.forEach((d) => {
|
|
53
54
|
if (d.datastakeId === link) {
|
|
@@ -68,8 +69,49 @@ export default function LocationIcon({
|
|
|
68
69
|
});
|
|
69
70
|
});
|
|
70
71
|
|
|
72
|
+
// ADD: Also include links from this location's stakeholders
|
|
73
|
+
const stakeholders = data.stakeholders || [];
|
|
74
|
+
stakeholders.forEach((stakeholder) => {
|
|
75
|
+
const stakeholderLinks = stakeholder.links || [];
|
|
76
|
+
stakeholderLinks.forEach((link) => {
|
|
77
|
+
allData.forEach((d) => {
|
|
78
|
+
// Check if it's a direct location link
|
|
79
|
+
if (d.datastakeId === link) {
|
|
80
|
+
// Avoid duplicates
|
|
81
|
+
if (!nodes.find(n => n.datastakeId === link && !n.isStakeholder)) {
|
|
82
|
+
nodes.push({
|
|
83
|
+
...d,
|
|
84
|
+
fromStakeholderId: stakeholder.datastakeId,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Check if it's a stakeholder link
|
|
89
|
+
if (d.stakeholders && d.stakeholders.length > 0) {
|
|
90
|
+
d.stakeholders.forEach((targetStakeholder) => {
|
|
91
|
+
if (targetStakeholder.datastakeId === link) {
|
|
92
|
+
// Avoid duplicates
|
|
93
|
+
if (!nodes.find(n =>
|
|
94
|
+
n.isStakeholder &&
|
|
95
|
+
n.datastakeId === d.datastakeId &&
|
|
96
|
+
n.stakeholdersIndex === d.stakeholders.indexOf(targetStakeholder)
|
|
97
|
+
)) {
|
|
98
|
+
nodes.push({
|
|
99
|
+
...d,
|
|
100
|
+
isStakeholder: true,
|
|
101
|
+
totalStakeholders: d.stakeholders.length,
|
|
102
|
+
stakeholdersIndex: d.stakeholders.indexOf(targetStakeholder),
|
|
103
|
+
fromStakeholderId: stakeholder.datastakeId,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
71
113
|
return nodes;
|
|
72
|
-
}, [JSON.stringify(allData), JSON.stringify(data.links), zoom]);
|
|
114
|
+
}, [JSON.stringify(allData), JSON.stringify(data.links), JSON.stringify(data.stakeholders), zoom]);
|
|
73
115
|
|
|
74
116
|
const stakeholdersOfLocation = useMemo(() => {
|
|
75
117
|
return data?.stakeholders || [];
|
|
@@ -90,7 +132,16 @@ export default function LocationIcon({
|
|
|
90
132
|
currentRoots.clear();
|
|
91
133
|
markersRef.current = [];
|
|
92
134
|
|
|
93
|
-
//
|
|
135
|
+
// Only create stakeholder markers if this location or any of its stakeholders are selected
|
|
136
|
+
const shouldShowStakeholders = isSelected || stakeholdersOfLocation.some(stk =>
|
|
137
|
+
selectedMarkersId.includes(stk.datastakeId)
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
if (!shouldShowStakeholders || selectedMarkersId.length === 0) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Create new markers only when selected
|
|
94
145
|
stakeholdersOfLocation.forEach((stakeholder, index) => {
|
|
95
146
|
const markerId = `${stakeholder.datastakeId}`;
|
|
96
147
|
const { x, y, radius, center } = getStakeholderPosition({
|
|
@@ -184,6 +235,8 @@ export default function LocationIcon({
|
|
|
184
235
|
isFromStakeholder: true,
|
|
185
236
|
isForceOpen,
|
|
186
237
|
listOfPolylines: polylinesRef.current,
|
|
238
|
+
stakeholderType: stakeholder.type,
|
|
239
|
+
animated: true,
|
|
187
240
|
});
|
|
188
241
|
});
|
|
189
242
|
|
|
@@ -199,12 +252,67 @@ export default function LocationIcon({
|
|
|
199
252
|
rootsMapRef.current.clear();
|
|
200
253
|
markersRef.current = [];
|
|
201
254
|
};
|
|
202
|
-
}, [stakeholdersOfLocation, selectedMarkersId, activeMarker]);
|
|
255
|
+
}, [stakeholdersOfLocation, selectedMarkersId, activeMarker, zoom]);
|
|
203
256
|
|
|
204
|
-
|
|
205
|
-
|
|
257
|
+
// Only create polylines for linked nodes when something is selected
|
|
258
|
+
useEffect(() => {
|
|
259
|
+
if (selectedMarkersId.length === 0) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// IMPORTANT: Only draw links if this location is actually selected
|
|
264
|
+
// Not just highlighted as part of the chain
|
|
265
|
+
if (!isSelected) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Filter linkedNodesData to only include nodes that are in the selected chain
|
|
270
|
+
const relevantLinks = linkedNodesData.filter(node => {
|
|
271
|
+
// Check if the target node (location) is in the selected markers
|
|
272
|
+
const targetLocationInSelection = selectedMarkersId.includes(node.datastakeId);
|
|
273
|
+
|
|
274
|
+
// If connecting to a stakeholder, check if that stakeholder is selected
|
|
275
|
+
if (node.isStakeholder) {
|
|
276
|
+
const stakeholderInSelection = node.stakeholdersIndex !== undefined &&
|
|
277
|
+
selectedMarkersId.includes(node.datastakeId);
|
|
278
|
+
return stakeholderInSelection;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return targetLocationInSelection;
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
relevantLinks.forEach((node) => {
|
|
285
|
+
const id = node.fromStakeholderId
|
|
286
|
+
? `${node.fromStakeholderId}-${node.datastakeId}`
|
|
287
|
+
: `${data.datastakeId}-${node.datastakeId}`;
|
|
206
288
|
const isConnectingToStakeholder = node.isStakeholder;
|
|
207
|
-
|
|
289
|
+
|
|
290
|
+
// If the link is from a stakeholder, start from the stakeholder position
|
|
291
|
+
let startLatLng;
|
|
292
|
+
if (node.fromStakeholderId) {
|
|
293
|
+
// Find the stakeholder index in this location's stakeholders
|
|
294
|
+
const stakeholderIndex = stakeholdersOfLocation.findIndex(
|
|
295
|
+
s => s.datastakeId === node.fromStakeholderId
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
if (stakeholderIndex !== -1) {
|
|
299
|
+
const { x, y } = getStakeholderPosition({
|
|
300
|
+
zoom,
|
|
301
|
+
totalMarkers: stakeholdersOfLocation.length,
|
|
302
|
+
markerIndex: stakeholderIndex,
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
const centerLatLng = L.latLng(data.gps.latitude, data.gps.longitude);
|
|
306
|
+
const centerPoint = mapRef.latLngToLayerPoint(centerLatLng);
|
|
307
|
+
const stakeholderPoint = centerPoint.add(L.point(x, y));
|
|
308
|
+
startLatLng = mapRef.layerPointToLatLng(stakeholderPoint);
|
|
309
|
+
} else {
|
|
310
|
+
startLatLng = L.latLng(data.gps.latitude, data.gps.longitude);
|
|
311
|
+
}
|
|
312
|
+
} else {
|
|
313
|
+
startLatLng = L.latLng(data.gps.latitude, data.gps.longitude);
|
|
314
|
+
}
|
|
315
|
+
|
|
208
316
|
let endLatLng = L.latLng(node.gps.latitude, node.gps.longitude);
|
|
209
317
|
const isConnectingToStakeholderSelected = selectedMarkersId.includes(node.datastakeId);
|
|
210
318
|
|
|
@@ -225,15 +333,15 @@ export default function LocationIcon({
|
|
|
225
333
|
createPolyline({
|
|
226
334
|
L,
|
|
227
335
|
mapRef,
|
|
228
|
-
startLatLng
|
|
336
|
+
startLatLng,
|
|
229
337
|
endLatLng,
|
|
230
|
-
|
|
231
338
|
isSelected: isConnectingToStakeholderSelected,
|
|
232
339
|
id,
|
|
233
340
|
zoom,
|
|
234
341
|
listOfPolylines: polylinesRef.current,
|
|
235
342
|
});
|
|
236
343
|
});
|
|
344
|
+
}, [linkedNodesData, selectedMarkersId, zoom, stakeholdersOfLocation, isSelected]);
|
|
237
345
|
|
|
238
346
|
return (
|
|
239
347
|
<Popover
|
|
@@ -5,13 +5,14 @@ const VILLAGE = "village";
|
|
|
5
5
|
const EXPORTER = "exporter";
|
|
6
6
|
const PROCESSOR = "mineralProcessor";
|
|
7
7
|
const DEPOT = "depot";
|
|
8
|
+
const OPERATOR = "miningOperator";
|
|
8
9
|
|
|
9
10
|
const MAX_EXTRA_SMALL_ZOOM_THRESHOLD = 2;
|
|
10
11
|
const MAX_SMALL_ZOOM_THRESHOLD = 3;
|
|
11
12
|
const MAX_MEDIUM_ZOOM_THRESHOLD = 6;
|
|
12
13
|
|
|
13
14
|
const LOCATION_TYPES = [MINE_SITE, VILLAGE];
|
|
14
|
-
const STAKEHOLDER_TYPES = [EXPORTER, PROCESSOR, DEPOT];
|
|
15
|
+
const STAKEHOLDER_TYPES = [EXPORTER, PROCESSOR, DEPOT, OPERATOR];
|
|
15
16
|
|
|
16
17
|
const RADIUS_SMALL = 15;
|
|
17
18
|
const RADIUS_MEDIUM = 35;
|
|
@@ -100,7 +101,6 @@ export function getStakeholderPosition({ zoom, totalMarkers, markerIndex }) {
|
|
|
100
101
|
|
|
101
102
|
let radius;
|
|
102
103
|
let center = {
|
|
103
|
-
// NOT BEING USED FOR NOW AND MAYBE NEVER
|
|
104
104
|
left: 0,
|
|
105
105
|
top: 0,
|
|
106
106
|
};
|
|
@@ -117,6 +117,32 @@ export function getStakeholderPosition({ zoom, totalMarkers, markerIndex }) {
|
|
|
117
117
|
return { x, y, center, radius, angleDeg };
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
function applyAnimationDirect(el, isShortLink) {
|
|
121
|
+
if (!(el instanceof SVGElement) || isShortLink) return;
|
|
122
|
+
|
|
123
|
+
el.style.strokeDasharray = "10, 10";
|
|
124
|
+
el.style.strokeDashoffset = "0";
|
|
125
|
+
|
|
126
|
+
el.style.animation = "dash-flow 1.2s linear infinite";
|
|
127
|
+
|
|
128
|
+
el.classList.add('animated-polyline');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function removeAnimationFromElement(element) {
|
|
132
|
+
if (!element) return;
|
|
133
|
+
|
|
134
|
+
element.classList.remove('animated-polyline');
|
|
135
|
+
element.style.animation = '';
|
|
136
|
+
element.style.strokeDasharray = '';
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function applyAnimationToPolyline(polyline, isShortLink) {
|
|
140
|
+
const element = polyline.getElement();
|
|
141
|
+
if (element) {
|
|
142
|
+
applyAnimationDirect(element, isShortLink);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
120
146
|
export function createPolyline({
|
|
121
147
|
L,
|
|
122
148
|
startLatLng,
|
|
@@ -127,32 +153,62 @@ export function createPolyline({
|
|
|
127
153
|
listOfPolylines = [],
|
|
128
154
|
isFromStakeholder = false,
|
|
129
155
|
isForceOpen = false,
|
|
156
|
+
stakeholderType = null,
|
|
157
|
+
animated = false,
|
|
158
|
+
mapRef
|
|
130
159
|
}) {
|
|
131
|
-
const
|
|
160
|
+
const lineWidth = isFromStakeholder && isExtraSmallMarker(zoom) && !isForceOpen ? 0 : 1.2;
|
|
161
|
+
|
|
162
|
+
const isShortLink = stakeholderType === OPERATOR || isFromStakeholder;
|
|
163
|
+
const shouldAnimate = animated;
|
|
132
164
|
|
|
133
|
-
const
|
|
165
|
+
const lineCoordinates = [
|
|
134
166
|
[startLatLng.lat, startLatLng.lng],
|
|
135
167
|
[endLatLng.lat, endLatLng.lng],
|
|
136
168
|
];
|
|
137
|
-
|
|
169
|
+
|
|
170
|
+
const polylineStyle = {
|
|
138
171
|
color: "var(--base-gray-70)",
|
|
139
|
-
weight:
|
|
140
|
-
opacity: 0.5,
|
|
141
|
-
smoothFactor:
|
|
172
|
+
weight: lineWidth,
|
|
173
|
+
opacity: isSelected ? 1 : 0.5,
|
|
174
|
+
smoothFactor: 0,
|
|
142
175
|
id,
|
|
143
|
-
dashArray: !isSelected ? "5, 5" : "
|
|
176
|
+
dashArray: isShortLink ? "0, 0" : (shouldAnimate ? "10, 10" : (!isSelected ? "5, 5" : "10, 10")),
|
|
177
|
+
renderer: L.svg()
|
|
144
178
|
};
|
|
145
179
|
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
if (
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
180
|
+
const existingPolyline = listOfPolylines.find(p => p.options.id === id);
|
|
181
|
+
|
|
182
|
+
if (existingPolyline) {
|
|
183
|
+
removeAnimationFromElement(existingPolyline.getElement());
|
|
184
|
+
|
|
185
|
+
existingPolyline.setLatLngs(lineCoordinates);
|
|
186
|
+
existingPolyline.setStyle(polylineStyle);
|
|
187
|
+
|
|
188
|
+
if (shouldAnimate && isSelected) {
|
|
189
|
+
existingPolyline.once('add', () => {
|
|
190
|
+
applyAnimationToPolyline(existingPolyline, isShortLink);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
applyAnimationToPolyline(existingPolyline, isShortLink);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return existingPolyline;
|
|
154
197
|
}
|
|
155
198
|
|
|
199
|
+
const newPolyline = L.polyline(lineCoordinates, polylineStyle);
|
|
200
|
+
|
|
201
|
+
newPolyline.addTo(mapRef);
|
|
202
|
+
listOfPolylines.push(newPolyline);
|
|
203
|
+
|
|
204
|
+
if (shouldAnimate && isSelected) {
|
|
205
|
+
newPolyline.once('add', () => {
|
|
206
|
+
applyAnimationToPolyline(newPolyline, isShortLink);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
applyAnimationToPolyline(newPolyline, isShortLink);
|
|
210
|
+
}
|
|
211
|
+
|
|
156
212
|
return newPolyline;
|
|
157
213
|
}
|
|
158
214
|
|
|
@@ -31,7 +31,7 @@ export const useMap = ({
|
|
|
31
31
|
const container = createRef();
|
|
32
32
|
const [data, setData] = useState([]);
|
|
33
33
|
const [mapRef, setMapRef] = useState(null);
|
|
34
|
-
const { TILE_LAYER_URL, MAP_TOKEN } = useMapConfig({ app, isSatellite });
|
|
34
|
+
const { TILE_LAYER_URL, MAP_TOKEN } = useMapConfig({ app, isSatellite, mapRef: container });
|
|
35
35
|
const [initialMarkerSetIsDone, setInitialMarkerSetIsDone] = useState(false);
|
|
36
36
|
const [mapCenter, setMapCenter] = useState([0, 0]);
|
|
37
37
|
const [emptyStateIsVisible, setEmptyStateIsVisible] = useState(false);
|
|
@@ -48,6 +48,7 @@ export const useMap = ({
|
|
|
48
48
|
const stakeToLoc = new Map();
|
|
49
49
|
const nodeTypes = new Map();
|
|
50
50
|
|
|
51
|
+
// Build the graph
|
|
51
52
|
for (const loc of data) {
|
|
52
53
|
const locId = loc.datastakeId;
|
|
53
54
|
nodeTypes.set(locId, loc.type);
|
|
@@ -81,34 +82,45 @@ export const useMap = ({
|
|
|
81
82
|
|
|
82
83
|
const highlightTable = {};
|
|
83
84
|
|
|
85
|
+
// Perform BFS/DFS to find all connected nodes in the entire chain
|
|
84
86
|
for (const [node] of graph) {
|
|
85
87
|
const highlighted = new Set();
|
|
88
|
+
const queue = [node];
|
|
89
|
+
const visited = new Set([node]);
|
|
86
90
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (nodeIsStakeholder && stakeToLoc.has(node)) {
|
|
91
|
-
const parentLoc = stakeToLoc.get(node);
|
|
92
|
-
highlighted.add(parentLoc);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
for (const neighbor of graph.get(node) || []) {
|
|
96
|
-
const neighborIsStakeholder = !isLocation(nodeTypes.get(neighbor));
|
|
91
|
+
while (queue.length > 0) {
|
|
92
|
+
const current = queue.shift();
|
|
93
|
+
highlighted.add(current);
|
|
97
94
|
|
|
98
|
-
if
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
95
|
+
// Add parent location if current is stakeholder
|
|
96
|
+
const currentIsStakeholder = !isLocation(nodeTypes.get(current));
|
|
97
|
+
if (currentIsStakeholder && stakeToLoc.has(current)) {
|
|
98
|
+
const parentLoc = stakeToLoc.get(current);
|
|
99
|
+
if (!visited.has(parentLoc)) {
|
|
100
|
+
highlighted.add(parentLoc);
|
|
101
|
+
visited.add(parentLoc);
|
|
102
|
+
queue.push(parentLoc);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Traverse all neighbors
|
|
107
|
+
for (const neighbor of graph.get(current) || []) {
|
|
108
|
+
if (!visited.has(neighbor)) {
|
|
109
|
+
visited.add(neighbor);
|
|
110
|
+
queue.push(neighbor);
|
|
107
111
|
highlighted.add(neighbor);
|
|
108
|
-
|
|
112
|
+
|
|
113
|
+
// If neighbor is stakeholder, add its parent location
|
|
114
|
+
const neighborIsStakeholder = !isLocation(nodeTypes.get(neighbor));
|
|
115
|
+
if (neighborIsStakeholder && stakeToLoc.has(neighbor)) {
|
|
116
|
+
const neighborParent = stakeToLoc.get(neighbor);
|
|
117
|
+
if (!visited.has(neighborParent)) {
|
|
118
|
+
highlighted.add(neighborParent);
|
|
119
|
+
visited.add(neighborParent);
|
|
120
|
+
queue.push(neighborParent);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
109
123
|
}
|
|
110
|
-
} else {
|
|
111
|
-
highlighted.add(neighbor);
|
|
112
124
|
}
|
|
113
125
|
}
|
|
114
126
|
|
|
@@ -151,10 +163,21 @@ export const useMap = ({
|
|
|
151
163
|
destroyAllPopovers();
|
|
152
164
|
setSelectedMarkersId((prev) => {
|
|
153
165
|
if (prev.includes(clickedMarker.datastakeId)) {
|
|
166
|
+
// Deselecting - clear polylines
|
|
154
167
|
openPopupIdRef.current = null;
|
|
155
168
|
setMarkerWithPopup(null);
|
|
156
169
|
return [];
|
|
157
170
|
} else {
|
|
171
|
+
// CLEAR OLD POLYLINES BEFORE SELECTING NEW MARKER
|
|
172
|
+
if (polylinesRef.current.length > 0) {
|
|
173
|
+
polylinesRef.current.forEach((polyline) => {
|
|
174
|
+
if (mapRef.hasLayer(polyline)) {
|
|
175
|
+
mapRef.removeLayer(polyline);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
polylinesRef.current = [];
|
|
179
|
+
}
|
|
180
|
+
|
|
158
181
|
setMarkerWithPopup(isStakeholder(clickedMarker.type) ? clickedMarker : null);
|
|
159
182
|
const newSelectedMarkersId = highlightTable[clickedMarker.datastakeId];
|
|
160
183
|
openPopupIdRef.current = clickedMarker.datastakeId;
|
|
@@ -191,12 +214,22 @@ export const useMap = ({
|
|
|
191
214
|
});
|
|
192
215
|
}
|
|
193
216
|
}
|
|
217
|
+
|
|
218
|
+
if (type === "chain" && selectedMarkersId.length === 0) {
|
|
219
|
+
if (polylinesRef.current.length) {
|
|
220
|
+
polylinesRef.current.forEach((polyline) => {
|
|
221
|
+
if (mapRef.hasLayer(polyline)) {
|
|
222
|
+
mapRef.removeLayer(polyline);
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
polylinesRef.current = [];
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
194
229
|
clearMapMarkers();
|
|
195
230
|
if (data) {
|
|
196
|
-
// Filters out locations that are not connected to any stakeholders
|
|
197
|
-
const excludedType = ['village', 'town', 'area', 'territory']
|
|
198
231
|
const filteredData = data?.filter((obj) =>
|
|
199
|
-
|
|
232
|
+
obj.type === 'mineSite' || (obj?.stakeholders?.length > 0 ||
|
|
200
233
|
data.some((other) =>
|
|
201
234
|
other.datastakeId !== obj.datastakeId &&
|
|
202
235
|
(other.stakeholders || []).some((stk) =>
|
|
@@ -218,9 +251,11 @@ export const useMap = ({
|
|
|
218
251
|
);
|
|
219
252
|
});
|
|
220
253
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
254
|
+
if (selectedMarkersId.length > 0) {
|
|
255
|
+
polylinesRef.current.forEach((polyline) => {
|
|
256
|
+
mapRef.addLayer(polyline);
|
|
257
|
+
});
|
|
258
|
+
}
|
|
224
259
|
|
|
225
260
|
mapRef.invalidateSize();
|
|
226
261
|
mapRef.fire("moveend");
|
|
@@ -7,6 +7,8 @@ const Style = styled.div`
|
|
|
7
7
|
width: 100%;
|
|
8
8
|
height: 472px;
|
|
9
9
|
|
|
10
|
+
|
|
11
|
+
|
|
10
12
|
.filter-cont {
|
|
11
13
|
position: absolute;
|
|
12
14
|
top: 24px;
|
|
@@ -109,11 +111,24 @@ const Style = styled.div`
|
|
|
109
111
|
align-items: center;
|
|
110
112
|
}
|
|
111
113
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
114
|
+
.marker-chain {
|
|
115
|
+
display: flex;
|
|
116
|
+
align-items: center;
|
|
117
|
+
justify-content: center;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.animated-polyline {
|
|
121
|
+
stroke-dasharray: 10 10;
|
|
122
|
+
animation: dash-flow 1.5s linear infinite;
|
|
123
|
+
stroke-linecap: round;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@keyframes dash-flow {
|
|
127
|
+
to {
|
|
128
|
+
stroke-dashoffset: -20;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
117
132
|
|
|
118
133
|
}
|
|
119
134
|
|
|
@@ -82,7 +82,7 @@ const CycleOutcomes = ({
|
|
|
82
82
|
<section style={{ flex: 1 }}>
|
|
83
83
|
<StatCard
|
|
84
84
|
title={t("Total Area Restored")}
|
|
85
|
-
value={totalAreaRestored?Number(totalAreaRestored.current).toLocaleString() : 0}
|
|
85
|
+
value={totalAreaRestored?Number(totalAreaRestored.current).toLocaleString() + " ha" : 0 + " ha"}
|
|
86
86
|
icon="Tree"
|
|
87
87
|
change={totalAreaRestoredChange}
|
|
88
88
|
/>
|