datastake-daf 0.6.774 → 0.6.776

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 (27) hide show
  1. package/dist/components/index.js +2583 -2504
  2. package/dist/hooks/index.js +0 -72
  3. package/dist/pages/index.js +1769 -835
  4. package/dist/style/datastake/mapbox-gl.css +330 -0
  5. package/dist/utils/index.js +0 -13
  6. package/package.json +1 -1
  7. package/src/@daf/core/components/Dashboard/Map/ChainIcon/Markers/StakeholderMarker.js +76 -8
  8. package/src/@daf/core/components/Dashboard/Map/ChainIcon/index.js +8 -116
  9. package/src/@daf/core/components/Dashboard/Map/ChainIcon/utils.js +17 -73
  10. package/src/@daf/core/components/Dashboard/Map/helper.js +0 -1
  11. package/src/@daf/core/components/Dashboard/Map/hook.js +29 -53
  12. package/src/@daf/core/components/Dashboard/Map/style.js +5 -20
  13. package/src/@daf/hooks/useTimeFilter.js +56 -0
  14. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/AssociatedInformation/config.js +548 -0
  15. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/AssociatedInformation/index.jsx +137 -24
  16. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CommunityParticipation/JobsTimeline/index.jsx +33 -102
  17. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleIndicators/HealthAndSafety/helper.js +8 -6
  18. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleIndicators/HealthAndSafety/index.jsx +73 -4
  19. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleIndicators/index.jsx +1 -1
  20. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleOutcomes/PlantingActivitiesTimeline.jsx +148 -0
  21. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleOutcomes/RestoredArea.jsx +150 -0
  22. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleOutcomes/index.jsx +11 -390
  23. package/src/@daf/pages/Summary/Activities/PlantingCycle/index.jsx +3 -4
  24. package/src/@daf/utils/object.js +1 -3
  25. package/src/@daf/utils/timeFilterUtils.js +226 -0
  26. package/src/hooks.js +1 -2
  27. package/src/utils.js +1 -1
@@ -7069,7 +7069,7 @@ const getRowConfig$1 = ({
7069
7069
  const MOBILE_W = 850;
7070
7070
  const MOBILE_WIDTH = `max-width: ${MOBILE_W}px`;
7071
7071
 
7072
- const Style$f = styled__default["default"].div`
7072
+ const Style$g = styled__default["default"].div`
7073
7073
  overflow-x: auto;
7074
7074
 
7075
7075
  @media (${MOBILE_WIDTH}) {
@@ -7467,7 +7467,7 @@ function KeyIndicatorsWidget({
7467
7467
  className: formatClassname(["flex-1 h-w-btn-header with-border-header", widgetClassName]),
7468
7468
  title: noTitle ? undefined : t(title),
7469
7469
  noTitle: noTitle,
7470
- children: [children, /*#__PURE__*/jsxRuntime.jsx(Style$f, {
7470
+ children: [children, /*#__PURE__*/jsxRuntime.jsx(Style$g, {
7471
7471
  className: formatClassname(["flex", className]),
7472
7472
  children: config.map((c, i) => /*#__PURE__*/jsxRuntime.jsxs("div", {
7473
7473
  className: "row-item",
@@ -7563,15 +7563,13 @@ KeyIndicators$1.propTypes = {
7563
7563
  goTo: PropTypes__default["default"].func
7564
7564
  };
7565
7565
 
7566
- const Style$e = styled__default["default"].div`
7566
+ const Style$f = styled__default["default"].div`
7567
7567
  display: flex;
7568
7568
  flex-direction: row;
7569
7569
  position: relative;
7570
7570
  width: 100%;
7571
7571
  height: 472px;
7572
7572
 
7573
-
7574
-
7575
7573
  .filter-cont {
7576
7574
  position: absolute;
7577
7575
  top: 24px;
@@ -7674,24 +7672,11 @@ const Style$e = styled__default["default"].div`
7674
7672
  align-items: center;
7675
7673
  }
7676
7674
 
7677
- .marker-chain {
7678
- display: flex;
7679
- align-items: center;
7680
- justify-content: center;
7681
- }
7682
-
7683
- .animated-polyline {
7684
- stroke-dasharray: 10 10;
7685
- animation: dash-flow 1.5s linear infinite;
7686
- stroke-linecap: round;
7687
- }
7688
-
7689
- @keyframes dash-flow {
7690
- to {
7691
- stroke-dashoffset: -20;
7692
- }
7693
- }
7694
-
7675
+ .marker-chain {
7676
+ display: flex;
7677
+ align-items: center;
7678
+ justify-content: center;
7679
+ }
7695
7680
 
7696
7681
  }
7697
7682
 
@@ -8215,15 +8200,18 @@ const VILLAGE = "village";
8215
8200
  const EXPORTER = "exporter";
8216
8201
  const PROCESSOR = "mineralProcessor";
8217
8202
  const DEPOT = "depot";
8218
- const OPERATOR = "miningOperator";
8219
8203
  const MAX_EXTRA_SMALL_ZOOM_THRESHOLD = 2;
8220
8204
  const MAX_SMALL_ZOOM_THRESHOLD = 3;
8221
8205
  const MAX_MEDIUM_ZOOM_THRESHOLD = 6;
8222
8206
  const LOCATION_TYPES = [MINE_SITE, VILLAGE];
8223
- const STAKEHOLDER_TYPES = [EXPORTER, PROCESSOR, DEPOT, OPERATOR];
8207
+ const STAKEHOLDER_TYPES = [EXPORTER, PROCESSOR, DEPOT];
8224
8208
  const RADIUS_SMALL = 15;
8225
8209
  const RADIUS_MEDIUM = 35;
8226
8210
  const RADIUS_LARGE = 60;
8211
+ const RADIUS_CURVE_SMALL = 10;
8212
+ const RADIUS_CURVE_MEDIUM = 15;
8213
+ const RADIUS_CURVE_LARGE = 20;
8214
+ const TENSION = 0.2;
8227
8215
  function isLocation(type) {
8228
8216
  return LOCATION_TYPES.includes(type);
8229
8217
  }
@@ -8280,6 +8268,7 @@ function getStakeholderPosition({
8280
8268
  const isLarge = isLargeMarker(zoom);
8281
8269
  let radius;
8282
8270
  let center = {
8271
+ // NOT BEING USED FOR NOW AND MAYBE NEVER
8283
8272
  left: 0,
8284
8273
  top: 0
8285
8274
  };
@@ -8303,25 +8292,6 @@ function getStakeholderPosition({
8303
8292
  angleDeg
8304
8293
  };
8305
8294
  }
8306
- function applyAnimationDirect(el, isShortLink) {
8307
- if (!(el instanceof SVGElement) || isShortLink) return;
8308
- el.style.strokeDasharray = "10, 10";
8309
- el.style.strokeDashoffset = "0";
8310
- el.style.animation = "dash-flow 1.2s linear infinite";
8311
- el.classList.add('animated-polyline');
8312
- }
8313
- function removeAnimationFromElement(element) {
8314
- if (!element) return;
8315
- element.classList.remove('animated-polyline');
8316
- element.style.animation = '';
8317
- element.style.strokeDasharray = '';
8318
- }
8319
- function applyAnimationToPolyline(polyline, isShortLink) {
8320
- const element = polyline.getElement();
8321
- if (element) {
8322
- applyAnimationDirect(element, isShortLink);
8323
- }
8324
- }
8325
8295
  function createPolyline({
8326
8296
  L,
8327
8297
  startLatLng,
@@ -8331,48 +8301,110 @@ function createPolyline({
8331
8301
  zoom,
8332
8302
  listOfPolylines = [],
8333
8303
  isFromStakeholder = false,
8334
- isForceOpen = false,
8335
- stakeholderType = null,
8336
- animated = false,
8337
- mapRef
8304
+ isForceOpen = false
8338
8305
  }) {
8339
- const lineWidth = isFromStakeholder && isExtraSmallMarker(zoom) && !isForceOpen ? 0 : 1.2;
8340
- const isShortLink = stakeholderType === OPERATOR || isFromStakeholder;
8341
- const shouldAnimate = animated;
8342
- const lineCoordinates = [[startLatLng.lat, startLatLng.lng], [endLatLng.lat, endLatLng.lng]];
8343
- const polylineStyle = {
8306
+ const width = isFromStakeholder && isExtraSmallMarker(zoom) && !isForceOpen ? 0 : 1.2;
8307
+ const coordinates = [[startLatLng.lat, startLatLng.lng], [endLatLng.lat, endLatLng.lng]];
8308
+ const style = {
8344
8309
  color: "var(--base-gray-70)",
8345
- weight: lineWidth,
8346
- opacity: isSelected ? 1 : 0.5,
8347
- smoothFactor: 0,
8310
+ weight: width,
8311
+ opacity: 0.5,
8312
+ smoothFactor: 1,
8348
8313
  id,
8349
- dashArray: isShortLink ? "0, 0" : shouldAnimate ? "10, 10" : !isSelected ? "5, 5" : "10, 10",
8350
- renderer: L.svg()
8314
+ dashArray: !isSelected ? "5, 5" : "0, 0"
8351
8315
  };
8352
- const existingPolyline = listOfPolylines.find(p => p.options.id === id);
8353
- if (existingPolyline) {
8354
- removeAnimationFromElement(existingPolyline.getElement());
8355
- existingPolyline.setLatLngs(lineCoordinates);
8356
- existingPolyline.setStyle(polylineStyle);
8357
- if (shouldAnimate && isSelected) {
8358
- existingPolyline.once('add', () => {
8359
- applyAnimationToPolyline(existingPolyline, isShortLink);
8360
- });
8361
- applyAnimationToPolyline(existingPolyline, isShortLink);
8362
- }
8363
- return existingPolyline;
8364
- }
8365
- const newPolyline = L.polyline(lineCoordinates, polylineStyle);
8366
- newPolyline.addTo(mapRef);
8367
- listOfPolylines.push(newPolyline);
8368
- if (shouldAnimate && isSelected) {
8369
- newPolyline.once('add', () => {
8370
- applyAnimationToPolyline(newPolyline, isShortLink);
8371
- });
8372
- applyAnimationToPolyline(newPolyline, isShortLink);
8316
+ const newPolyline = L.polyline(coordinates, style);
8317
+ if (listOfPolylines.find(p => p.options.id === id)) {
8318
+ const polylineToUpdateCoordinates = listOfPolylines.find(p => p.options.id === id);
8319
+ polylineToUpdateCoordinates.setLatLngs(coordinates);
8320
+ polylineToUpdateCoordinates.setStyle(style);
8321
+ } else {
8322
+ listOfPolylines.push(newPolyline);
8373
8323
  }
8374
8324
  return newPolyline;
8375
8325
  }
8326
+ function createCurvePath({
8327
+ zoom,
8328
+ totalMarkers,
8329
+ markerIndex
8330
+ }) {
8331
+ const radius = getCurvePointRadius(zoom);
8332
+ const {
8333
+ x,
8334
+ y,
8335
+ angleDeg
8336
+ } = getAngleDeg(totalMarkers, markerIndex, radius);
8337
+ return {
8338
+ x,
8339
+ y,
8340
+ angleDeg
8341
+ };
8342
+ }
8343
+ function getCurvePointRadius(zoom) {
8344
+ const isSmall = isSmallMarker(zoom) || isExtraSmallMarker(zoom);
8345
+ const isMedium = isMediumMarker(zoom);
8346
+ if (isSmall) {
8347
+ return RADIUS_SMALL + RADIUS_CURVE_SMALL;
8348
+ } else if (isMedium) {
8349
+ return RADIUS_MEDIUM + RADIUS_CURVE_MEDIUM;
8350
+ } else {
8351
+ return RADIUS_LARGE + RADIUS_CURVE_LARGE;
8352
+ }
8353
+ }
8354
+ function buildSmoothCurve(layerPoints, mapRef) {
8355
+ const path = [];
8356
+ for (let i = 0; i < layerPoints.length - 1; i++) {
8357
+ const p0 = layerPoints[i];
8358
+ const p1 = layerPoints[i + 1];
8359
+ const pPrev = layerPoints[i - 1] || p0;
8360
+ const pNext = layerPoints[i + 2] || p1;
8361
+ const cp1 = L__namespace.point(p0.x + (p1.x - pPrev.x) * TENSION, p0.y + (p1.y - pPrev.y) * TENSION);
8362
+ const cp2 = L__namespace.point(p1.x - (pNext.x - p0.x) * TENSION, p1.y - (pNext.y - p0.y) * TENSION);
8363
+ if (i === 0) {
8364
+ path.push("M", [mapRef.layerPointToLatLng(p0).lat, mapRef.layerPointToLatLng(p0).lng]);
8365
+ }
8366
+ path.push("C", [mapRef.layerPointToLatLng(cp1).lat, mapRef.layerPointToLatLng(cp1).lng], [mapRef.layerPointToLatLng(cp2).lat, mapRef.layerPointToLatLng(cp2).lng], [mapRef.layerPointToLatLng(p1).lat, mapRef.layerPointToLatLng(p1).lng]);
8367
+ }
8368
+ return path;
8369
+ }
8370
+ function getSiblingCurveStrength(zoom) {
8371
+ if (isExtraSmallMarker(zoom)) return RADIUS_CURVE_SMALL / 2;
8372
+ if (isSmallMarker(zoom)) return RADIUS_CURVE_MEDIUM;
8373
+ if (isMediumMarker(zoom)) return RADIUS_CURVE_LARGE;
8374
+ return RADIUS_CURVE_LARGE;
8375
+ }
8376
+ function buildCurveWIthTwoSiblings({
8377
+ mapRef,
8378
+ startLatLng,
8379
+ endLatLng,
8380
+ zoom,
8381
+ isSelected,
8382
+ id
8383
+ }) {
8384
+ const fromPoint = mapRef.latLngToLayerPoint(startLatLng);
8385
+ const toPoint = mapRef.latLngToLayerPoint(endLatLng);
8386
+ const midX = (fromPoint.x + toPoint.x) / 2;
8387
+ const midY = (fromPoint.y + toPoint.y) / 2 + (isSmallMarker(zoom) ? RADIUS_CURVE_SMALL / 2 : 0);
8388
+ const dx = toPoint.x - fromPoint.x;
8389
+ const dy = toPoint.y - fromPoint.y;
8390
+ const normal = L__namespace.point(-dy, dx);
8391
+ const length = Math.sqrt(normal.x ** 2 + normal.y ** 2) || 1;
8392
+ const normalized = normal.multiplyBy(1 / length);
8393
+ const curveStrength = getSiblingCurveStrength(zoom);
8394
+ const controlPoint = L__namespace.point(midX, midY).add(normalized.multiplyBy(curveStrength));
8395
+ const latlngs = [startLatLng, mapRef.layerPointToLatLng(controlPoint), endLatLng];
8396
+ const layerPoints = latlngs.map(latlng => mapRef.latLngToLayerPoint(latlng));
8397
+ const path = buildSmoothCurve(layerPoints, mapRef);
8398
+ const curve = L__namespace.curve(path, {
8399
+ color: "var(--base-gray-70)",
8400
+ weight: isExtraSmallMarker(zoom) ? 0 : 1.2,
8401
+ opacity: 0.5,
8402
+ smoothFactor: 1,
8403
+ id,
8404
+ dashArray: !isSelected ? "5, 5" : "0, 0"
8405
+ });
8406
+ mapRef.addLayer(curve);
8407
+ }
8376
8408
 
8377
8409
  const StakeholderMarker = styled__default["default"].div`
8378
8410
  background-color: var(--blue-50);
@@ -8673,9 +8705,6 @@ function StakeholderIcon$1({
8673
8705
  return null;
8674
8706
  }, [parentId, allData]);
8675
8707
  React.useEffect(() => {
8676
- if (selectedMarkersId.length === 0 || !isSelected) {
8677
- return;
8678
- }
8679
8708
  linkNodesData.map(node => {
8680
8709
  const isConnectingToStakeholder = node.isStakeholder;
8681
8710
  const id = `${data.datastakeId}-${node.stakeholderId || node.datastakeId}`;
@@ -8687,6 +8716,8 @@ function StakeholderIcon$1({
8687
8716
  const stakeholderPoint = centerPoint.add(L__namespace.point(x, y));
8688
8717
  const stakeholderLatLng = mapRef.layerPointToLatLng(stakeholderPoint);
8689
8718
  let endLatLng = L__namespace.latLng(node.gps.latitude, node.gps.longitude);
8719
+ const areNextToEachOther = targetMarkerIndex === index + 1 || targetMarkerIndex === index - 1 || index === 0 && targetMarkerIndex === node.totalStakeholders - 1 || targetMarkerIndex === 0 && index === node.totalStakeholders - 1;
8720
+ const areOnlyTwoSiblings = node.totalStakeholders === 2;
8690
8721
  if (isExtraSmallMarker(zoom) && !isForceOpen) {
8691
8722
  createPolyline({
8692
8723
  L: L__namespace,
@@ -8696,8 +8727,7 @@ function StakeholderIcon$1({
8696
8727
  zoom,
8697
8728
  isSelected,
8698
8729
  id,
8699
- listOfPolylines: polylinesRef.current,
8700
- animated: true
8730
+ listOfPolylines: polylinesRef.current
8701
8731
  });
8702
8732
  return;
8703
8733
  }
@@ -8715,8 +8745,61 @@ function StakeholderIcon$1({
8715
8745
  const nodePoint = mapRef.latLngToLayerPoint(nodeLatLng);
8716
8746
  const endPoint = L__namespace.point(x + nodePoint.x + center.left, y + nodePoint.y + center.top);
8717
8747
  endLatLng = mapRef.layerPointToLatLng(endPoint);
8748
+ if (isSibling && (!areNextToEachOther || areOnlyTwoSiblings)) {
8749
+ if (areOnlyTwoSiblings) {
8750
+ buildCurveWIthTwoSiblings({
8751
+ mapRef,
8752
+ startLatLng: stakeholderLatLng,
8753
+ endLatLng,
8754
+ zoom,
8755
+ isSelected,
8756
+ id
8757
+ });
8758
+ return;
8759
+ }
8760
+ const total = node.totalStakeholders;
8761
+ let from = index;
8762
+ let to = targetMarkerIndex;
8763
+ let flip = false;
8764
+ const forwardDistance = (to - from + total) % total;
8765
+ const backwardDistance = (from - to + total) % total;
8766
+ if (backwardDistance < forwardDistance) {
8767
+ [from, to] = [to, from];
8768
+ flip = true;
8769
+ }
8770
+ const intermediateIndices = [];
8771
+ for (let i = 1; i < (to - from + total) % total; i++) {
8772
+ intermediateIndices.push((from + i) % total);
8773
+ }
8774
+ const indices = [from, ...intermediateIndices, to];
8775
+ const intermediatePoints = [];
8776
+ for (const i of indices) {
8777
+ const {
8778
+ x,
8779
+ y
8780
+ } = createCurvePath({
8781
+ zoom,
8782
+ totalMarkers: node.totalStakeholders,
8783
+ markerIndex: i
8784
+ });
8785
+ const point = centerPoint.add(L__namespace.point(x, y));
8786
+ const latlng = mapRef.layerPointToLatLng(point);
8787
+ intermediatePoints.push(latlng);
8788
+ }
8789
+ const latlngs = flip ? [endLatLng, ...intermediatePoints, stakeholderLatLng] : [stakeholderLatLng, ...intermediatePoints, endLatLng];
8790
+ const layerPoints = latlngs.map(latlng => mapRef.latLngToLayerPoint(latlng));
8791
+ const path = buildSmoothCurve(layerPoints, mapRef);
8792
+ const curve = L__namespace?.curve?.(path, {
8793
+ color: "var(--base-gray-70)",
8794
+ weight: isExtraSmallMarker(zoom) ? 0 : 1,
8795
+ opacity: isSelected ? 1 : 0.5,
8796
+ smoothFactor: 1,
8797
+ id
8798
+ });
8799
+ mapRef.addLayer(curve);
8800
+ return;
8801
+ }
8718
8802
  }
8719
- // Always use straight lines
8720
8803
  createPolyline({
8721
8804
  L: L__namespace,
8722
8805
  mapRef,
@@ -8726,11 +8809,10 @@ function StakeholderIcon$1({
8726
8809
  isFromStakeholder: false,
8727
8810
  isSelected,
8728
8811
  id,
8729
- listOfPolylines: polylinesRef.current,
8730
- animated: true
8812
+ listOfPolylines: polylinesRef.current
8731
8813
  });
8732
8814
  });
8733
- }, [mapRef, x, y, parentData, linkNodesData, isSelected, zoom, isForceOpen, selectedMarkersId]);
8815
+ }, [mapRef, x, y, parentData, linkNodesData, isSelected, zoom, isForceOpen]);
8734
8816
  return /*#__PURE__*/jsxRuntime.jsx(jsxRuntime.Fragment, {
8735
8817
  children: /*#__PURE__*/jsxRuntime.jsx(antd.Popover, {
8736
8818
  content: renderTooltipJsx({
@@ -8797,8 +8879,6 @@ function LocationIcon({
8797
8879
  const linkedNodesData = React.useMemo(() => {
8798
8880
  const nodes = [];
8799
8881
  const links = data.links || [];
8800
-
8801
- // Add links from the location itself
8802
8882
  links.forEach(link => {
8803
8883
  allData.forEach(d => {
8804
8884
  if (d.datastakeId === link) {
@@ -8818,45 +8898,8 @@ function LocationIcon({
8818
8898
  }
8819
8899
  });
8820
8900
  });
8821
-
8822
- // ADD: Also include links from this location's stakeholders
8823
- const stakeholders = data.stakeholders || [];
8824
- stakeholders.forEach(stakeholder => {
8825
- const stakeholderLinks = stakeholder.links || [];
8826
- stakeholderLinks.forEach(link => {
8827
- allData.forEach(d => {
8828
- // Check if it's a direct location link
8829
- if (d.datastakeId === link) {
8830
- // Avoid duplicates
8831
- if (!nodes.find(n => n.datastakeId === link && !n.isStakeholder)) {
8832
- nodes.push({
8833
- ...d,
8834
- fromStakeholderId: stakeholder.datastakeId
8835
- });
8836
- }
8837
- }
8838
- // Check if it's a stakeholder link
8839
- if (d.stakeholders && d.stakeholders.length > 0) {
8840
- d.stakeholders.forEach(targetStakeholder => {
8841
- if (targetStakeholder.datastakeId === link) {
8842
- // Avoid duplicates
8843
- if (!nodes.find(n => n.isStakeholder && n.datastakeId === d.datastakeId && n.stakeholdersIndex === d.stakeholders.indexOf(targetStakeholder))) {
8844
- nodes.push({
8845
- ...d,
8846
- isStakeholder: true,
8847
- totalStakeholders: d.stakeholders.length,
8848
- stakeholdersIndex: d.stakeholders.indexOf(targetStakeholder),
8849
- fromStakeholderId: stakeholder.datastakeId
8850
- });
8851
- }
8852
- }
8853
- });
8854
- }
8855
- });
8856
- });
8857
- });
8858
8901
  return nodes;
8859
- }, [JSON.stringify(allData), JSON.stringify(data.links), JSON.stringify(data.stakeholders), zoom]);
8902
+ }, [JSON.stringify(allData), JSON.stringify(data.links), zoom]);
8860
8903
  const stakeholdersOfLocation = React.useMemo(() => {
8861
8904
  return data?.stakeholders || [];
8862
8905
  }, [data.stakeholders, zoom]);
@@ -8874,13 +8917,7 @@ function LocationIcon({
8874
8917
  currentRoots.clear();
8875
8918
  markersRef.current = [];
8876
8919
 
8877
- // Only create stakeholder markers if this location or any of its stakeholders are selected
8878
- const shouldShowStakeholders = isSelected || stakeholdersOfLocation.some(stk => selectedMarkersId.includes(stk.datastakeId));
8879
- if (!shouldShowStakeholders || selectedMarkersId.length === 0) {
8880
- return;
8881
- }
8882
-
8883
- // Create new markers only when selected
8920
+ // Create new markers
8884
8921
  stakeholdersOfLocation.forEach((stakeholder, index) => {
8885
8922
  const markerId = `${stakeholder.datastakeId}`;
8886
8923
  const {
@@ -8966,9 +9003,7 @@ function LocationIcon({
8966
9003
  zoom,
8967
9004
  isFromStakeholder: true,
8968
9005
  isForceOpen,
8969
- listOfPolylines: polylinesRef.current,
8970
- stakeholderType: stakeholder.type,
8971
- animated: true
9006
+ listOfPolylines: polylinesRef.current
8972
9007
  });
8973
9008
  });
8974
9009
  return () => {
@@ -8983,88 +9018,38 @@ function LocationIcon({
8983
9018
  rootsMapRef.current.clear();
8984
9019
  markersRef.current = [];
8985
9020
  };
8986
- }, [stakeholdersOfLocation, selectedMarkersId, activeMarker, zoom]);
8987
-
8988
- // Only create polylines for linked nodes when something is selected
8989
- React.useEffect(() => {
8990
- if (selectedMarkersId.length === 0) {
8991
- return;
8992
- }
8993
-
8994
- // IMPORTANT: Only draw links if this location is actually selected
8995
- // Not just highlighted as part of the chain
8996
- if (!isSelected) {
8997
- return;
8998
- }
8999
-
9000
- // Filter linkedNodesData to only include nodes that are in the selected chain
9001
- const relevantLinks = linkedNodesData.filter(node => {
9002
- // Check if the target node (location) is in the selected markers
9003
- const targetLocationInSelection = selectedMarkersId.includes(node.datastakeId);
9004
-
9005
- // If connecting to a stakeholder, check if that stakeholder is selected
9006
- if (node.isStakeholder) {
9007
- const stakeholderInSelection = node.stakeholdersIndex !== undefined && selectedMarkersId.includes(node.datastakeId);
9008
- return stakeholderInSelection;
9009
- }
9010
- return targetLocationInSelection;
9011
- });
9012
- relevantLinks.forEach(node => {
9013
- const id = node.fromStakeholderId ? `${node.fromStakeholderId}-${node.datastakeId}` : `${data.datastakeId}-${node.datastakeId}`;
9014
- const isConnectingToStakeholder = node.isStakeholder;
9015
-
9016
- // If the link is from a stakeholder, start from the stakeholder position
9017
- let startLatLng;
9018
- if (node.fromStakeholderId) {
9019
- // Find the stakeholder index in this location's stakeholders
9020
- const stakeholderIndex = stakeholdersOfLocation.findIndex(s => s.datastakeId === node.fromStakeholderId);
9021
- if (stakeholderIndex !== -1) {
9022
- const {
9023
- x,
9024
- y
9025
- } = getStakeholderPosition({
9026
- zoom,
9027
- totalMarkers: stakeholdersOfLocation.length,
9028
- markerIndex: stakeholderIndex
9029
- });
9030
- const centerLatLng = L__namespace.latLng(data.gps.latitude, data.gps.longitude);
9031
- const centerPoint = mapRef.latLngToLayerPoint(centerLatLng);
9032
- const stakeholderPoint = centerPoint.add(L__namespace.point(x, y));
9033
- startLatLng = mapRef.layerPointToLatLng(stakeholderPoint);
9034
- } else {
9035
- startLatLng = L__namespace.latLng(data.gps.latitude, data.gps.longitude);
9036
- }
9037
- } else {
9038
- startLatLng = L__namespace.latLng(data.gps.latitude, data.gps.longitude);
9039
- }
9040
- let endLatLng = L__namespace.latLng(node.gps.latitude, node.gps.longitude);
9041
- const isConnectingToStakeholderSelected = selectedMarkersId.includes(node.datastakeId);
9042
- if (isConnectingToStakeholder && !isExtraSmallMarker(zoom)) {
9043
- const {
9044
- x,
9045
- y
9046
- } = getStakeholderPosition({
9047
- zoom,
9048
- totalMarkers: node.totalStakeholders,
9049
- markerIndex: node.stakeholdersIndex
9050
- });
9051
- const nodeLatLng = L__namespace.latLng(node.gps.latitude, node.gps.longitude);
9052
- const nodePoint = mapRef.latLngToLayerPoint(nodeLatLng);
9053
- const endPoint = L__namespace.point(x + nodePoint.x, y + nodePoint.y);
9054
- endLatLng = mapRef.layerPointToLatLng(endPoint);
9055
- }
9056
- createPolyline({
9057
- L: L__namespace,
9058
- mapRef,
9059
- startLatLng,
9060
- endLatLng,
9061
- isSelected: isConnectingToStakeholderSelected,
9062
- id,
9021
+ }, [stakeholdersOfLocation, selectedMarkersId, activeMarker]);
9022
+ linkedNodesData.map(node => {
9023
+ const id = `${data.datastakeId}-${node.datastakeId}`;
9024
+ const isConnectingToStakeholder = node.isStakeholder;
9025
+ const centerLatLng = L__namespace.latLng(data.gps.latitude, data.gps.longitude);
9026
+ let endLatLng = L__namespace.latLng(node.gps.latitude, node.gps.longitude);
9027
+ const isConnectingToStakeholderSelected = selectedMarkersId.includes(node.datastakeId);
9028
+ if (isConnectingToStakeholder && !isExtraSmallMarker(zoom)) {
9029
+ const {
9030
+ x,
9031
+ y
9032
+ } = getStakeholderPosition({
9063
9033
  zoom,
9064
- listOfPolylines: polylinesRef.current
9034
+ totalMarkers: node.totalStakeholders,
9035
+ markerIndex: node.stakeholdersIndex
9065
9036
  });
9037
+ const nodeLatLng = L__namespace.latLng(node.gps.latitude, node.gps.longitude);
9038
+ const nodePoint = mapRef.latLngToLayerPoint(nodeLatLng);
9039
+ const endPoint = L__namespace.point(x + nodePoint.x, y + nodePoint.y);
9040
+ endLatLng = mapRef.layerPointToLatLng(endPoint);
9041
+ }
9042
+ createPolyline({
9043
+ L: L__namespace,
9044
+ mapRef,
9045
+ startLatLng: centerLatLng,
9046
+ endLatLng,
9047
+ isSelected: isConnectingToStakeholderSelected,
9048
+ id,
9049
+ zoom,
9050
+ listOfPolylines: polylinesRef.current
9066
9051
  });
9067
- }, [linkedNodesData, selectedMarkersId, zoom, stakeholdersOfLocation, isSelected]);
9052
+ });
9068
9053
  return /*#__PURE__*/jsxRuntime.jsx(antd.Popover, {
9069
9054
  content: renderTooltipJsx({
9070
9055
  title: data.name,
@@ -9119,7 +9104,7 @@ function LocationIcon({
9119
9104
  });
9120
9105
  }
9121
9106
 
9122
- const Style$d = styled__default["default"].div`
9107
+ const Style$e = styled__default["default"].div`
9123
9108
  .main {
9124
9109
  width: 24px;
9125
9110
  height: 24px;
@@ -9264,7 +9249,7 @@ function StakeholderIcon({
9264
9249
  onClickLink: () => onClickLink(marker)
9265
9250
  }),
9266
9251
  getPopupContainer: () => document.getElementById("map"),
9267
- children: /*#__PURE__*/jsxRuntime.jsx(Style$d
9252
+ children: /*#__PURE__*/jsxRuntime.jsx(Style$e
9268
9253
  // onClick={toggleOpen}
9269
9254
  , {
9270
9255
  className: `map-marker marker closed`,
@@ -9277,7 +9262,7 @@ function StakeholderIcon({
9277
9262
  })
9278
9263
  });
9279
9264
  }
9280
- return /*#__PURE__*/jsxRuntime.jsxs(Style$d, {
9265
+ return /*#__PURE__*/jsxRuntime.jsxs(Style$e, {
9281
9266
  onClick: toggleOpen,
9282
9267
  className: `map-marker marker ${isActive ? "opened" : "closed"}`,
9283
9268
  children: [/*#__PURE__*/jsxRuntime.jsx("div", {
@@ -9580,8 +9565,7 @@ function useMapHelper$1({
9580
9565
  link: link,
9581
9566
  onClickLink: onClickLink,
9582
9567
  activeStakeholder: activeStakeholder,
9583
- setActiveStakeholder: setActiveStakeholder,
9584
- mapRef: mapRef
9568
+ setActiveStakeholder: setActiveStakeholder
9585
9569
  }));
9586
9570
  roots.current.push(root);
9587
9571
  } else if (type === "location") {
@@ -9786,8 +9770,7 @@ const useMap = ({
9786
9770
  MAP_TOKEN
9787
9771
  } = useMapConfig({
9788
9772
  app,
9789
- isSatellite,
9790
- mapRef: container
9773
+ isSatellite
9791
9774
  });
9792
9775
  const [initialMarkerSetIsDone, setInitialMarkerSetIsDone] = React.useState(false);
9793
9776
  const [mapCenter, setMapCenter] = React.useState([0, 0]);
@@ -9804,8 +9787,6 @@ const useMap = ({
9804
9787
  const graph = new Map();
9805
9788
  const stakeToLoc = new Map();
9806
9789
  const nodeTypes = new Map();
9807
-
9808
- // Build the graph
9809
9790
  for (const loc of data) {
9810
9791
  const locId = loc.datastakeId;
9811
9792
  nodeTypes.set(locId, loc.type);
@@ -9830,45 +9811,26 @@ const useMap = ({
9830
9811
  }
9831
9812
  }
9832
9813
  const highlightTable = {};
9833
-
9834
- // Perform BFS/DFS to find all connected nodes in the entire chain
9835
9814
  for (const [node] of graph) {
9836
9815
  const highlighted = new Set();
9837
- const queue = [node];
9838
- const visited = new Set([node]);
9839
- while (queue.length > 0) {
9840
- const current = queue.shift();
9841
- highlighted.add(current);
9842
-
9843
- // Add parent location if current is stakeholder
9844
- const currentIsStakeholder = !isLocation(nodeTypes.get(current));
9845
- if (currentIsStakeholder && stakeToLoc.has(current)) {
9846
- const parentLoc = stakeToLoc.get(current);
9847
- if (!visited.has(parentLoc)) {
9848
- highlighted.add(parentLoc);
9849
- visited.add(parentLoc);
9850
- queue.push(parentLoc);
9851
- }
9852
- }
9853
-
9854
- // Traverse all neighbors
9855
- for (const neighbor of graph.get(current) || []) {
9856
- if (!visited.has(neighbor)) {
9857
- visited.add(neighbor);
9858
- queue.push(neighbor);
9816
+ highlighted.add(node);
9817
+ const nodeIsStakeholder = !isLocation(nodeTypes.get(node));
9818
+ if (nodeIsStakeholder && stakeToLoc.has(node)) {
9819
+ const parentLoc = stakeToLoc.get(node);
9820
+ highlighted.add(parentLoc);
9821
+ }
9822
+ for (const neighbor of graph.get(node) || []) {
9823
+ const neighborIsStakeholder = !isLocation(nodeTypes.get(neighbor));
9824
+ if (neighborIsStakeholder && stakeToLoc.has(neighbor)) {
9825
+ const neighborParent = stakeToLoc.get(neighbor);
9826
+ if (isLocation(nodeTypes.get(node)) && neighborParent === node || nodeIsStakeholder && stakeToLoc.get(node) === neighborParent) {
9859
9827
  highlighted.add(neighbor);
9860
-
9861
- // If neighbor is stakeholder, add its parent location
9862
- const neighborIsStakeholder = !isLocation(nodeTypes.get(neighbor));
9863
- if (neighborIsStakeholder && stakeToLoc.has(neighbor)) {
9864
- const neighborParent = stakeToLoc.get(neighbor);
9865
- if (!visited.has(neighborParent)) {
9866
- highlighted.add(neighborParent);
9867
- visited.add(neighborParent);
9868
- queue.push(neighborParent);
9869
- }
9870
- }
9828
+ } else {
9829
+ highlighted.add(neighbor);
9830
+ highlighted.add(neighborParent);
9871
9831
  }
9832
+ } else {
9833
+ highlighted.add(neighbor);
9872
9834
  }
9873
9835
  }
9874
9836
  highlightTable[node] = [...highlighted];
@@ -9933,29 +9895,19 @@ const useMap = ({
9933
9895
  });
9934
9896
  }
9935
9897
  }
9936
- if (type === "chain" && selectedMarkersId.length === 0) {
9937
- if (polylinesRef.current.length) {
9938
- polylinesRef.current.forEach(polyline => {
9939
- if (mapRef.hasLayer(polyline)) {
9940
- mapRef.removeLayer(polyline);
9941
- }
9942
- });
9943
- polylinesRef.current = [];
9944
- }
9945
- }
9946
9898
  clearMapMarkers();
9947
9899
  if (data) {
9948
- const filteredData = data?.filter(obj => obj.type === 'mineSite' || obj?.stakeholders?.length > 0 || data.some(other => other.datastakeId !== obj.datastakeId && (other.stakeholders || []).some(stk => (stk.links || []).includes(obj.datastakeId))));
9900
+ // Filters out locations that are not connected to any stakeholders
9901
+ const excludedType = ['village', 'town', 'area', 'territory'];
9902
+ const filteredData = data?.filter(obj => !excludedType.includes(obj?.type) && (obj?.stakeholders?.length > 0 || data.some(other => other.datastakeId !== obj.datastakeId && (other.stakeholders || []).some(stk => (stk.links || []).includes(obj.datastakeId)))));
9949
9903
  const maxTotal = Math.max(...(data || []).map(d => d.total));
9950
9904
  const dataToRender = type === "chain" ? filteredData : data;
9951
9905
  dataToRender.forEach((d, i) => {
9952
9906
  addIconToMapInitialy([d?.marker?.lat, d?.marker?.lng], "location", d.category || "mineSite", d, maxTotal, i);
9953
9907
  });
9954
- if (selectedMarkersId.length > 0) {
9955
- polylinesRef.current.forEach(polyline => {
9956
- mapRef.addLayer(polyline);
9957
- });
9958
- }
9908
+ polylinesRef.current.forEach(polyline => {
9909
+ mapRef.addLayer(polyline);
9910
+ });
9959
9911
  mapRef.invalidateSize();
9960
9912
  mapRef.fire("moveend");
9961
9913
  }
@@ -10116,7 +10068,7 @@ const useMap = ({
10116
10068
  };
10117
10069
  };
10118
10070
 
10119
- const Style$c = styled__default["default"].div`
10071
+ const Style$d = styled__default["default"].div`
10120
10072
  position: relative;
10121
10073
  width: 100%;
10122
10074
  height: 100%;
@@ -10189,7 +10141,7 @@ function ComponentWithFocus({
10189
10141
  setClosed(false);
10190
10142
  }
10191
10143
  }, [closed]);
10192
- return /*#__PURE__*/jsxRuntime.jsxs(Style$c, {
10144
+ return /*#__PURE__*/jsxRuntime.jsxs(Style$d, {
10193
10145
  className: className,
10194
10146
  ref: ref,
10195
10147
  style: style,
@@ -10201,7 +10153,7 @@ function ComponentWithFocus({
10201
10153
  });
10202
10154
  }
10203
10155
 
10204
- const Style$b = styled__default["default"].div`
10156
+ const Style$c = styled__default["default"].div`
10205
10157
  position: absolute;
10206
10158
  top: 24px;
10207
10159
  left: 24px;
@@ -10376,7 +10328,7 @@ function Filters({
10376
10328
  return null;
10377
10329
  }
10378
10330
  };
10379
- return /*#__PURE__*/jsxRuntime.jsxs(Style$b, {
10331
+ return /*#__PURE__*/jsxRuntime.jsxs(Style$c, {
10380
10332
  children: [/*#__PURE__*/jsxRuntime.jsxs(antd.Button, {
10381
10333
  onClick: () => setOpened(p => !p),
10382
10334
  children: [t("Filter"), /*#__PURE__*/jsxRuntime.jsx(CustomIcon, {
@@ -10457,7 +10409,7 @@ function Map$1({
10457
10409
  isSatellite
10458
10410
  });
10459
10411
  return /*#__PURE__*/jsxRuntime.jsx(ComponentWithFocus, {
10460
- children: /*#__PURE__*/jsxRuntime.jsxs(Style$e, {
10412
+ children: /*#__PURE__*/jsxRuntime.jsxs(Style$f, {
10461
10413
  className: formatClassname([showSider && activeMarker && "with-sider"]),
10462
10414
  children: [filtersConfig ? /*#__PURE__*/jsxRuntime.jsx(Filters, {
10463
10415
  t: t,
@@ -10666,7 +10618,7 @@ function PrimaryNode({
10666
10618
  style: {
10667
10619
  opacity: 0
10668
10620
  }
10669
- }), /*#__PURE__*/jsxRuntime.jsxs(Style$a, {
10621
+ }), /*#__PURE__*/jsxRuntime.jsxs(Style$b, {
10670
10622
  $isPdf: isPdf,
10671
10623
  className: "flex",
10672
10624
  children: [" ", /*#__PURE__*/jsxRuntime.jsx("div", {
@@ -10777,7 +10729,7 @@ function PrimaryNode({
10777
10729
  })]
10778
10730
  });
10779
10731
  }
10780
- const Style$a = styled__default["default"].div`
10732
+ const Style$b = styled__default["default"].div`
10781
10733
  width: ${MAIN_NODE_WIDTH}px;
10782
10734
  height: ${MAIN_NODE_HEIGHT}px; /* Explicitly set height */
10783
10735
  display: flex;
@@ -10913,7 +10865,7 @@ function IconNode({
10913
10865
  }), /*#__PURE__*/jsxRuntime.jsx(antd.Tooltip, {
10914
10866
  title: data?.name,
10915
10867
  defaultOpen: data?.ONLY_IN_STORYBOOK,
10916
- children: /*#__PURE__*/jsxRuntime.jsx(Style$9, {
10868
+ children: /*#__PURE__*/jsxRuntime.jsx(Style$a, {
10917
10869
  hoverColor: data?.iconHoverColor,
10918
10870
  children: /*#__PURE__*/jsxRuntime.jsx(antd.ConfigProvider, {
10919
10871
  theme: {
@@ -10956,7 +10908,7 @@ function IconNode({
10956
10908
  })]
10957
10909
  });
10958
10910
  }
10959
- const Style$9 = styled__default["default"].div`
10911
+ const Style$a = styled__default["default"].div`
10960
10912
  height: 40px;
10961
10913
  width: 40px;
10962
10914
 
@@ -11034,7 +10986,7 @@ function NameNode({
11034
10986
  style: {
11035
10987
  opacity: 0
11036
10988
  }
11037
- }), /*#__PURE__*/jsxRuntime.jsxs(Style$8, {
10989
+ }), /*#__PURE__*/jsxRuntime.jsxs(Style$9, {
11038
10990
  $isPdf: isPdf,
11039
10991
  style: {
11040
10992
  opacity: data.isEmpty ? 0.5 : 1
@@ -11122,7 +11074,7 @@ function NameNode({
11122
11074
  })]
11123
11075
  });
11124
11076
  }
11125
- const Style$8 = styled__default["default"].div`
11077
+ const Style$9 = styled__default["default"].div`
11126
11078
  width: ${NAME_CARD_WIDTH}px;
11127
11079
  height: 55px;
11128
11080
  display: flex;
@@ -11153,7 +11105,7 @@ const Style$8 = styled__default["default"].div`
11153
11105
  }
11154
11106
  `;
11155
11107
 
11156
- const Style$7 = styled__default["default"].div`
11108
+ const Style$8 = styled__default["default"].div`
11157
11109
  width: 405px;
11158
11110
  display: flex;
11159
11111
  height: 140px;
@@ -11264,7 +11216,7 @@ function ExpandedNode({
11264
11216
  style: {
11265
11217
  opacity: 0
11266
11218
  }
11267
- }), /*#__PURE__*/jsxRuntime.jsxs(Style$7, {
11219
+ }), /*#__PURE__*/jsxRuntime.jsxs(Style$8, {
11268
11220
  style: {
11269
11221
  opacity
11270
11222
  },
@@ -12516,7 +12468,7 @@ Identification.propTypes = {
12516
12468
  theme: PropTypes__default["default"].object
12517
12469
  };
12518
12470
 
12519
- const Style$6 = styled__default["default"].div`
12471
+ const Style$7 = styled__default["default"].div`
12520
12472
  position: absolute;
12521
12473
  display: flex;
12522
12474
  gap: 12px;
@@ -12531,7 +12483,7 @@ function Tooltip({
12531
12483
  mouseY = 0,
12532
12484
  children = null
12533
12485
  }) {
12534
- return /*#__PURE__*/jsxRuntime.jsx(Style$6, {
12486
+ return /*#__PURE__*/jsxRuntime.jsx(Style$7, {
12535
12487
  style: {
12536
12488
  top: mouseY,
12537
12489
  left: mouseX
@@ -15224,7 +15176,7 @@ const renderStatusTag = ({
15224
15176
  }
15225
15177
  };
15226
15178
 
15227
- const getColumns$b = ({
15179
+ const getColumns$c = ({
15228
15180
  t,
15229
15181
  goTo,
15230
15182
  user,
@@ -20881,7 +20833,7 @@ function Comments({
20881
20833
  });
20882
20834
  }
20883
20835
 
20884
- var Style$5 = styled__default["default"].div`
20836
+ var Style$6 = styled__default["default"].div`
20885
20837
  border: 1px solid var(--base-gray-40);
20886
20838
  padding: 12px;
20887
20839
  background: var(--base-gray-20);
@@ -21027,7 +20979,7 @@ function GroupInfoHOC({
21027
20979
  meta,
21028
20980
  t
21029
20981
  });
21030
- return /*#__PURE__*/jsxRuntime.jsxs(Style$5, {
20982
+ return /*#__PURE__*/jsxRuntime.jsxs(Style$6, {
21031
20983
  className: formatClassname([!isExpanded && 'collapsed', className, isSuccess && 'success']),
21032
20984
  children: [/*#__PURE__*/jsxRuntime.jsxs("div", {
21033
20985
  className: "cont-header",
@@ -21184,7 +21136,7 @@ function GroupInfo({
21184
21136
  });
21185
21137
  }
21186
21138
  };
21187
- return /*#__PURE__*/jsxRuntime.jsxs(Style$5, {
21139
+ return /*#__PURE__*/jsxRuntime.jsxs(Style$6, {
21188
21140
  className: formatClassname([!isExpanded && 'collapsed', className]),
21189
21141
  children: [/*#__PURE__*/jsxRuntime.jsxs("div", {
21190
21142
  className: "cont-header",
@@ -21225,7 +21177,7 @@ function GroupInfos({
21225
21177
  }, key));
21226
21178
  }
21227
21179
 
21228
- const Style$4 = styled__default["default"].div`
21180
+ const Style$5 = styled__default["default"].div`
21229
21181
  gap: 16px;
21230
21182
  display: flex;
21231
21183
  max-width: 465px;
@@ -21247,7 +21199,7 @@ function EvaluationPopover({
21247
21199
  t = s => s,
21248
21200
  evaluationConfig = []
21249
21201
  }) {
21250
- return /*#__PURE__*/jsxRuntime.jsx(Style$4, {
21202
+ return /*#__PURE__*/jsxRuntime.jsx(Style$5, {
21251
21203
  children: evaluationConfig.map(c =>
21252
21204
  /*#__PURE__*/
21253
21205
  //? ASK REDI
@@ -23219,7 +23171,7 @@ const useMapHelper = ({
23219
23171
  };
23220
23172
  };
23221
23173
 
23222
- const Style$3 = styled__default["default"].div`
23174
+ const Style$4 = styled__default["default"].div`
23223
23175
  width: 100%;
23224
23176
  height: 20rem;
23225
23177
  flex: 1;
@@ -23346,7 +23298,7 @@ function PolygonSelector({
23346
23298
  searchForLocation(mapRef);
23347
23299
  }
23348
23300
  }, [searchValue, mapRef]);
23349
- return /*#__PURE__*/jsxRuntime.jsxs(Style$3, {
23301
+ return /*#__PURE__*/jsxRuntime.jsxs(Style$4, {
23350
23302
  className: "polygon-selector",
23351
23303
  children: [/*#__PURE__*/jsxRuntime.jsx("div", {
23352
23304
  className: "map-container",
@@ -30104,7 +30056,7 @@ const NavigationAction = ({
30104
30056
  });
30105
30057
  };
30106
30058
 
30107
- const getColumns$a = ({
30059
+ const getColumns$b = ({
30108
30060
  t,
30109
30061
  goTo,
30110
30062
  user,
@@ -30676,7 +30628,7 @@ const viewConfig$6 = {
30676
30628
  createTitle: "Create Worker"
30677
30629
  };
30678
30630
 
30679
- const getColumns$9 = ({
30631
+ const getColumns$a = ({
30680
30632
  t,
30681
30633
  goTo,
30682
30634
  user,
@@ -31178,7 +31130,7 @@ MoreTags.propTypes = {
31178
31130
  limit: PropTypes__default["default"].number
31179
31131
  };
31180
31132
 
31181
- const getColumns$8 = ({
31133
+ const getColumns$9 = ({
31182
31134
  t,
31183
31135
  goTo,
31184
31136
  user,
@@ -31633,7 +31585,7 @@ const getEventCategoryBySubject = (eventCategoryObject, subject, isSingular = fa
31633
31585
  return eventCategoryObject[key] || null;
31634
31586
  };
31635
31587
 
31636
- const getColumns$7 = ({
31588
+ const getColumns$8 = ({
31637
31589
  t,
31638
31590
  goTo,
31639
31591
  user,
@@ -32058,7 +32010,7 @@ const viewConfig$3 = {
32058
32010
  createTitle: "Create Incident"
32059
32011
  };
32060
32012
 
32061
- const getColumns$6 = ({
32013
+ const getColumns$7 = ({
32062
32014
  t,
32063
32015
  goTo,
32064
32016
  user,
@@ -32316,7 +32268,7 @@ const viewConfig$2 = {
32316
32268
  createTitle: "Create Location"
32317
32269
  };
32318
32270
 
32319
- const getColumns$5 = ({
32271
+ const getColumns$6 = ({
32320
32272
  t,
32321
32273
  goTo,
32322
32274
  user,
@@ -32730,7 +32682,7 @@ MoreOptions.propTypes = {
32730
32682
  limit: PropTypes__default["default"].number
32731
32683
  };
32732
32684
 
32733
- const getColumns$4 = ({
32685
+ const getColumns$5 = ({
32734
32686
  t,
32735
32687
  goTo,
32736
32688
  user,
@@ -32966,7 +32918,7 @@ const viewConfig = {
32966
32918
  createTitle: "Create Document"
32967
32919
  };
32968
32920
 
32969
- const getColumns$3 = ({
32921
+ const getColumns$4 = ({
32970
32922
  t,
32971
32923
  goTo,
32972
32924
  user,
@@ -33091,63 +33043,63 @@ const FILTER_REGISTRY = {
33091
33043
  options: getFilterOptions$a,
33092
33044
  formConfig: formConfig$8,
33093
33045
  viewConfig: viewConfig$8,
33094
- columns: getColumns$a
33046
+ columns: getColumns$b
33095
33047
  },
33096
33048
  workers: {
33097
33049
  config: getFiltersConfig$8,
33098
33050
  options: getFilterOptions$8,
33099
33051
  formConfig: formConfig$6,
33100
33052
  viewConfig: viewConfig$6,
33101
- columns: getColumns$9
33053
+ columns: getColumns$a
33102
33054
  },
33103
33055
  operators: {
33104
33056
  config: getFiltersConfig$9,
33105
33057
  options: getFilterOptions$9,
33106
33058
  formConfig: formConfig$7,
33107
33059
  viewConfig: viewConfig$7,
33108
- columns: getColumns$b
33060
+ columns: getColumns$c
33109
33061
  },
33110
33062
  events: {
33111
33063
  config: getFiltersConfig$7,
33112
33064
  options: getFilterOptions$7,
33113
33065
  formConfig: formConfig$5,
33114
33066
  viewConfig: viewConfig$5,
33115
- columns: getColumns$8
33067
+ columns: getColumns$9
33116
33068
  },
33117
33069
  activities: {
33118
33070
  config: getFiltersConfig$6,
33119
33071
  options: getFilterOptions$6,
33120
33072
  formConfig: formConfig$4,
33121
33073
  viewConfig: viewConfig$4,
33122
- columns: getColumns$7
33074
+ columns: getColumns$8
33123
33075
  },
33124
33076
  incidents: {
33125
33077
  config: getFiltersConfig$5,
33126
33078
  options: getFilterOptions$5,
33127
33079
  formConfig: formConfig$3,
33128
33080
  viewConfig: viewConfig$3,
33129
- columns: getColumns$6
33081
+ columns: getColumns$7
33130
33082
  },
33131
33083
  locations: {
33132
33084
  config: getFiltersConfig$4,
33133
33085
  options: getFilterOptions$4,
33134
33086
  formConfig: formConfig$2,
33135
33087
  viewConfig: viewConfig$2,
33136
- columns: getColumns$5
33088
+ columns: getColumns$6
33137
33089
  },
33138
33090
  'production-sites': {
33139
33091
  config: getFiltersConfig$3,
33140
33092
  options: getFilterOptions$3,
33141
33093
  formConfig: formConfig$1,
33142
33094
  viewConfig: viewConfig$1,
33143
- columns: getColumns$4
33095
+ columns: getColumns$5
33144
33096
  },
33145
33097
  documents: {
33146
33098
  config: getFiltersConfig$2,
33147
33099
  options: getFilterOptions$2,
33148
33100
  formConfig: formConfig,
33149
33101
  viewConfig: viewConfig,
33150
- columns: getColumns$3
33102
+ columns: getColumns$4
33151
33103
  }
33152
33104
  };
33153
33105
  const DEFAULT_SUBJECT = 'stakeholders';
@@ -33198,7 +33150,7 @@ const getViewConfig = ({
33198
33150
  return registry?.viewConfig;
33199
33151
  };
33200
33152
 
33201
- const getColumns$2 = ({
33153
+ const getColumns$3 = ({
33202
33154
  t,
33203
33155
  goTo,
33204
33156
  user,
@@ -33285,7 +33237,7 @@ const useTablePage = ({
33285
33237
  extendingFilters: extendingFilters,
33286
33238
  subject
33287
33239
  });
33288
- const columns = React.useMemo(() => getColumns$2({
33240
+ const columns = React.useMemo(() => getColumns$3({
33289
33241
  t,
33290
33242
  goTo,
33291
33243
  user,
@@ -33296,7 +33248,7 @@ const useTablePage = ({
33296
33248
  subject,
33297
33249
  data,
33298
33250
  applications
33299
- }), [t, goTo, user, options, activeTab, getRedirectLink, theme, data, applications, subject, getColumns$2]);
33251
+ }), [t, goTo, user, options, activeTab, getRedirectLink, theme, data, applications, subject, getColumns$3]);
33300
33252
  const selectFiltersConfig = React.useMemo(() => getFiltersConfig$1({
33301
33253
  t,
33302
33254
  subject
@@ -33861,7 +33813,7 @@ const WorkersTable = ({
33861
33813
  getData: getData,
33862
33814
  getApiBaseUrl: getApiBaseUrl,
33863
33815
  getAppHeader: getAppHeader,
33864
- getColumns: getColumns$9,
33816
+ getColumns: getColumns$a,
33865
33817
  breadcrumbs: breadcrumbs,
33866
33818
  extendingFilters: extendingFilters,
33867
33819
  formConfig: {
@@ -34086,7 +34038,7 @@ const ProductionSitesTable = ({
34086
34038
  });
34087
34039
  };
34088
34040
 
34089
- const getColumns$1 = ({
34041
+ const getColumns$2 = ({
34090
34042
  t,
34091
34043
  goTo,
34092
34044
  user,
@@ -34564,7 +34516,7 @@ const UsersTable = ({
34564
34516
  const params = new URLSearchParams(location?.search);
34565
34517
  const [openCreateModal, setOpenCreateModal] = React.useState(params.has("create"));
34566
34518
  const [userToEdit, setUserToEdit] = React.useState(null);
34567
- const columns = React.useMemo(() => getColumns$1({
34519
+ const columns = React.useMemo(() => getColumns$2({
34568
34520
  t,
34569
34521
  goTo,
34570
34522
  user,
@@ -34955,7 +34907,7 @@ const partnershipTypes = [{
34955
34907
  value: "exchange"
34956
34908
  }];
34957
34909
 
34958
- const getColumns = ({
34910
+ const getColumns$1 = ({
34959
34911
  t,
34960
34912
  accept,
34961
34913
  decline,
@@ -35465,7 +35417,7 @@ const Create = ({
35465
35417
  });
35466
35418
  };
35467
35419
 
35468
- const Style$2 = styled__default["default"].div`
35420
+ const Style$3 = styled__default["default"].div`
35469
35421
  display: flex;
35470
35422
  gap: 8px;
35471
35423
  justify-content: center;
@@ -35488,7 +35440,7 @@ function Footer({
35488
35440
  disabledFooterText,
35489
35441
  disabledFooter
35490
35442
  }) {
35491
- return /*#__PURE__*/jsxRuntime.jsxs(Style$2, {
35443
+ return /*#__PURE__*/jsxRuntime.jsxs(Style$3, {
35492
35444
  className: className,
35493
35445
  children: [disabledFooterText ? /*#__PURE__*/jsxRuntime.jsx(antd.Tooltip, {
35494
35446
  title: t(disabledFooterText),
@@ -35700,7 +35652,7 @@ const PartnersTable = ({
35700
35652
  setTotalRequests,
35701
35653
  t
35702
35654
  });
35703
- const columns = React.useMemo(() => getColumns({
35655
+ const columns = React.useMemo(() => getColumns$1({
35704
35656
  t,
35705
35657
  accept,
35706
35658
  decline,
@@ -37867,7 +37819,63 @@ const OperatorSummary = ({
37867
37819
  });
37868
37820
  };
37869
37821
 
37870
- styled__default["default"].div`
37822
+ function StickyTable({
37823
+ size = "small",
37824
+ maxHeight = 300,
37825
+ containerHeight = 525,
37826
+ dataSource = [],
37827
+ columns = {},
37828
+ pagination = false,
37829
+ doEmptyRows = true,
37830
+ ...props
37831
+ }) {
37832
+ const data = React__default["default"].useMemo(() => {
37833
+ if (!doEmptyRows) {
37834
+ return dataSource;
37835
+ }
37836
+ const MIN_ROWS = 4;
37837
+ if (dataSource.length < MIN_ROWS) {
37838
+ const paddedData = [...dataSource];
37839
+ while (paddedData.length < MIN_ROWS) {
37840
+ paddedData.push({
37841
+ empty: true
37842
+ });
37843
+ }
37844
+ return paddedData;
37845
+ }
37846
+ return dataSource;
37847
+ }, [dataSource, doEmptyRows]);
37848
+ const Wrapper = React__default["default"].useMemo(() => {
37849
+ return data.length > 5 ? ComponentWithFocus : "div";
37850
+ }, [data.length]);
37851
+ return /*#__PURE__*/jsxRuntime.jsx(Wrapper, {
37852
+ children: /*#__PURE__*/jsxRuntime.jsx(Style$2, {
37853
+ children: /*#__PURE__*/jsxRuntime.jsx("div", {
37854
+ className: "daf-table-wrapper",
37855
+ style: {
37856
+ maxHeight: containerHeight,
37857
+ overflowY: "auto"
37858
+ },
37859
+ children: /*#__PURE__*/jsxRuntime.jsx("div", {
37860
+ className: "daf-sticky-table",
37861
+ children: /*#__PURE__*/jsxRuntime.jsx(antd.Table, {
37862
+ ...props,
37863
+ size: size,
37864
+ scroll: true,
37865
+ sticky: true,
37866
+ style: {
37867
+ maxHeight
37868
+ },
37869
+ dataSource: data,
37870
+ columns: columns,
37871
+ pagination: pagination
37872
+ })
37873
+ })
37874
+ })
37875
+ })
37876
+ });
37877
+ }
37878
+ const Style$2 = styled__default["default"].div`
37871
37879
  max-width: calc(100% - 48px);
37872
37880
  margin-left: var(--size-lg);
37873
37881
  overflow: hidden;
@@ -37891,13 +37899,13 @@ styled__default["default"].div`
37891
37899
  padding-top: 16px;
37892
37900
  }
37893
37901
  `;
37894
- ({
37902
+ StickyTable.propTypes = {
37895
37903
  size: PropTypes__default["default"].any,
37896
37904
  maxHeight: PropTypes__default["default"].number,
37897
37905
  dataSource: PropTypes__default["default"].array,
37898
37906
  columns: PropTypes__default["default"].object,
37899
37907
  pagination: PropTypes__default["default"].any
37900
- });
37908
+ };
37901
37909
 
37902
37910
  ({
37903
37911
  content: PropTypes__default["default"].string,
@@ -40829,6 +40837,25 @@ styled__default["default"].div`
40829
40837
  }
40830
40838
  `;
40831
40839
 
40840
+ /**
40841
+ * Formats a number for display with locale-specific formatting
40842
+ * @param {any} val - The value to format
40843
+ * @param {boolean} doubleDigit - Whether to ensure single digit numbers are padded with a leading zero
40844
+ * @returns {string} Formatted number string or '--' if input is not a number
40845
+ */
40846
+ const renderNumber = (val, doubleDigit = false) => {
40847
+ if (typeof val !== 'number') {
40848
+ return '--';
40849
+ }
40850
+ const _string = Number(val).toLocaleString('en-us');
40851
+ if (doubleDigit) {
40852
+ if (_string.length === 1) {
40853
+ return '0' + _string;
40854
+ }
40855
+ }
40856
+ return _string;
40857
+ };
40858
+
40832
40859
  styled__default["default"].div`
40833
40860
  height: 333px;
40834
40861
  width: calc(100% - 48px);
@@ -41851,6 +41878,13 @@ styled__default["default"].div`
41851
41878
  }
41852
41879
  `;
41853
41880
 
41881
+ const getRedirectLink = (link, APP) => {
41882
+ if (window.location.pathname.includes(`/${APP}`)) {
41883
+ return `${APP}${link}`;
41884
+ }
41885
+ return link;
41886
+ };
41887
+
41854
41888
  styled__default["default"].div`
41855
41889
  display: flex;
41856
41890
  flex-direction: column;
@@ -42836,6 +42870,551 @@ const calculateStatChange = (data, options = {}) => {
42836
42870
  };
42837
42871
  };
42838
42872
 
42873
+ /**
42874
+ * Formats a date based on the time filter
42875
+ * @param {dayjs.Dayjs} date - The date to format
42876
+ * @param {boolean} breakLine - Whether to add a line break (for tooltips)
42877
+ * @param {string} timeFilter - The time filter ('daily', 'weekly', 'monthly')
42878
+ * @returns {string} Formatted date string
42879
+ */
42880
+ const getFormatDate = (date, breakLine = false, timeFilter = 'monthly') => {
42881
+ switch (timeFilter) {
42882
+ case "daily":
42883
+ return date.format("DD/MM");
42884
+ case "weekly":
42885
+ return `W${renderNumber(date.week())}`;
42886
+ default:
42887
+ // Monthly format: "Dec 24", "Jan 25", etc.
42888
+
42889
+ return breakLine ? `${capitalize(date.format("MMM"))}\n${date.format("YY")}` : `${capitalize(date.format("MMM"))} ${date.format("YY")}`;
42890
+ }
42891
+ };
42892
+
42893
+ /**
42894
+ * Gets the time quantity string for dayjs operations
42895
+ * @param {string} timeFilter - The time filter ('daily', 'weekly', 'monthly')
42896
+ * @returns {string} Time quantity string ('days', 'weeks', 'months')
42897
+ */
42898
+ const getTimeQuantity = (timeFilter = 'monthly') => {
42899
+ return timeFilter === "monthly" ? "months" : timeFilter === "daily" ? "days" : "weeks";
42900
+ };
42901
+
42902
+ /**
42903
+ * Gets previous cumulative score from data before start date
42904
+ * @param {Array} dates - Array of data objects with date field
42905
+ * @param {dayjs.Dayjs} startDate - The start date
42906
+ * @param {string} timeFilter - The time filter
42907
+ * @param {string} valueField - The field name to extract value from (default: 'total')
42908
+ * @returns {Object} Object with hasPreviousData, previousCumulativeScore, previousMaxScore
42909
+ */
42910
+ const getPreviousGraphData = (dates, startDate, timeFilter, valueField = 'total') => {
42911
+ let previousCumulativeScore = 0;
42912
+ let previousMaxScore = 0;
42913
+ let hasPreviousData = false;
42914
+ dates.forEach(d => {
42915
+ const date = dayjs__default["default"](d.date, "YYYY-MM-DD");
42916
+ if (!date.isValid()) return;
42917
+ let isBeforeStart = false;
42918
+ switch (timeFilter) {
42919
+ case "daily":
42920
+ isBeforeStart = date.isBefore(startDate, 'day');
42921
+ break;
42922
+ case "weekly":
42923
+ isBeforeStart = date.isBefore(startDate, 'week');
42924
+ break;
42925
+ default:
42926
+ isBeforeStart = date.isBefore(startDate, 'month');
42927
+ break;
42928
+ }
42929
+ if (isBeforeStart) {
42930
+ hasPreviousData = true;
42931
+ const value = Number(d[valueField] || d.count || d.jobs || d.value || 0) || 0;
42932
+ previousCumulativeScore += value;
42933
+ previousMaxScore = Math.max(previousMaxScore, value);
42934
+ }
42935
+ });
42936
+ return {
42937
+ hasPreviousData,
42938
+ previousCumulativeScore,
42939
+ previousMaxScore
42940
+ };
42941
+ };
42942
+
42943
+ /**
42944
+ * Processes chart data with time filtering support
42945
+ * @param {Object} params - Parameters object
42946
+ * @param {Array} params.mainData - Array of data objects with date and value fields
42947
+ * @param {string} params.timeFilter - Time filter ('daily', 'weekly', 'monthly')
42948
+ * @param {Object} params.filters - Optional filters object with timeframe
42949
+ * @param {boolean} params.isCumulative - Whether to calculate cumulative values (default: false)
42950
+ * @param {string} params.valueField - Field name to extract value from (default: 'total', also checks 'count', 'jobs', 'value')
42951
+ * @returns {Array} Processed chart data array
42952
+ */
42953
+ const processChartDateData = ({
42954
+ mainData,
42955
+ timeFilter: filter,
42956
+ filters = {},
42957
+ isCumulative = false,
42958
+ valueField = 'total'
42959
+ }) => {
42960
+ if (!mainData || !Array.isArray(mainData) || mainData.length === 0) {
42961
+ return [];
42962
+ }
42963
+ const timeQuantity = getTimeQuantity(filter);
42964
+ const dates = mainData;
42965
+ const _data = [];
42966
+ let end = filters?.timeframe?.endDate || dayjs__default["default"]();
42967
+ let start = filters?.timeframe?.startDate || dayjs__default["default"]().add(-12, timeQuantity);
42968
+
42969
+ // Normalize start and end to period boundaries
42970
+ if (filter === "daily") {
42971
+ start = start.startOf('day');
42972
+ end = end.startOf('day');
42973
+ } else if (filter === "weekly") {
42974
+ start = start.startOf('week');
42975
+ end = end.startOf('week');
42976
+ } else {
42977
+ start = start.startOf('month');
42978
+ end = end.startOf('month');
42979
+ }
42980
+ let i = 0;
42981
+ let cumulativeScore = 0;
42982
+
42983
+ // Calculate initial cumulative value if needed
42984
+ if (isCumulative) {
42985
+ const {
42986
+ hasPreviousData,
42987
+ previousCumulativeScore
42988
+ } = getPreviousGraphData(dates, start, filter, valueField);
42989
+ cumulativeScore = hasPreviousData ? previousCumulativeScore : 0;
42990
+ }
42991
+
42992
+ // Loop until we reach the end date
42993
+ let currentDate = start.clone();
42994
+ while (currentDate.isBefore(end) || currentDate.isSame(end, filter === "daily" ? "day" : filter === "weekly" ? "week" : "month")) {
42995
+ // Filter data points that fall within this period
42996
+ const score = dates.filter(d => {
42997
+ if (!d.date) return false;
42998
+ switch (filter) {
42999
+ case "daily":
43000
+ return d.date === currentDate.format("YYYY-MM-DD");
43001
+ case "weekly":
43002
+ return dayjs__default["default"](d.date, "YYYY-MM-DD").week() === currentDate.week() && dayjs__default["default"](d.date, "YYYY-MM-DD").year() === currentDate.year();
43003
+ default:
43004
+ return dayjs__default["default"](d.date, "YYYY-MM-DD").format("YYYY-MM") === currentDate.format("YYYY-MM");
43005
+ }
43006
+ }).reduce((a, b) => {
43007
+ const value = Number(b[valueField] || b.count || b.jobs || b.value || 0) || 0;
43008
+ return a + value;
43009
+ }, 0);
43010
+ if (isCumulative) {
43011
+ cumulativeScore += score;
43012
+ }
43013
+ _data.push({
43014
+ date: getFormatDate(currentDate, false, filter),
43015
+ value: isCumulative ? cumulativeScore : score,
43016
+ period: score,
43017
+ // Period value for tooltip
43018
+ jobs: score,
43019
+ // For compatibility with jobs field
43020
+ month: currentDate.format('YYYY-MM-DD'),
43021
+ // For compatibility
43022
+ key: i
43023
+ });
43024
+ currentDate = currentDate.add(1, timeQuantity);
43025
+ i++;
43026
+ }
43027
+ return _data;
43028
+ };
43029
+
43030
+ /**
43031
+ * Formats date axis labels, checking if already formatted
43032
+ * @param {string} label - The label to format
43033
+ * @param {Function} getFormatDateFn - Function to format dates
43034
+ * @returns {string} Formatted date string
43035
+ */
43036
+ const formatDateAxis = (label, getFormatDateFn) => {
43037
+ if (!label) return label;
43038
+
43039
+ // Check if label is already in the correct format (MMM YY, DD/MM, or W#)
43040
+ // If it matches our format patterns, return as-is
43041
+ if (typeof label === 'string') {
43042
+ // Check for MMM YY format (e.g., "Dec 24", "Jan 25")
43043
+ if (/^[A-Z][a-z]{2} \d{2}$/.test(label)) {
43044
+ return label;
43045
+ }
43046
+ // Check for DD/MM format (e.g., "03/11")
43047
+ if (/^\d{2}\/\d{2}$/.test(label)) {
43048
+ return label;
43049
+ }
43050
+ // Check for W# format (e.g., "W1", "W12")
43051
+ if (/^W\d+$/.test(label)) {
43052
+ return label;
43053
+ }
43054
+ }
43055
+
43056
+ // Otherwise, try to parse and format it
43057
+ let date = dayjs__default["default"](label);
43058
+
43059
+ // If first attempt fails, try parsing as ISO date string
43060
+ if (!date.isValid() && typeof label === 'string') {
43061
+ date = dayjs__default["default"](label, ['YYYY-MM-DD', 'YYYY-MM', 'MMM YY', 'MMM YYYY', 'DD/MM'], true);
43062
+ }
43063
+
43064
+ // If it's a valid date, format it using getFormatDate
43065
+ if (date.isValid() && getFormatDateFn) {
43066
+ return getFormatDateFn(date, false);
43067
+ }
43068
+
43069
+ // Return as-is if we can't parse it
43070
+ return label;
43071
+ };
43072
+
43073
+ /**
43074
+ * Custom hook for time filtering functionality
43075
+ * Provides state management and formatting functions for time-based charts
43076
+ *
43077
+ * @param {Object} options - Configuration options
43078
+ * @param {string} options.defaultFilter - Default time filter ('daily', 'weekly', 'monthly')
43079
+ * @returns {Object} Time filter state and utilities
43080
+ */
43081
+ const useTimeFilter = ({
43082
+ defaultFilter = 'monthly'
43083
+ } = {}) => {
43084
+ const [timeFilter, setTimeFilter] = React.useState(defaultFilter);
43085
+
43086
+ // Memoized format date function bound to current timeFilter
43087
+ const getFormatDateFn = React.useCallback((date, breakLine = false) => getFormatDate(date, breakLine, timeFilter), [timeFilter]);
43088
+
43089
+ // Memoized time quantity function bound to current timeFilter
43090
+ const getTimeQuantityFn = React.useCallback(() => getTimeQuantity(timeFilter), [timeFilter]);
43091
+
43092
+ // Memoized date axis formatter
43093
+ const formatDateAxisFn = React.useMemo(() => label => formatDateAxis(label, getFormatDateFn), [getFormatDateFn]);
43094
+
43095
+ // Process chart data with current time filter
43096
+ const processData = React.useCallback(({
43097
+ mainData,
43098
+ filters = {},
43099
+ isCumulative = false,
43100
+ valueField = 'total'
43101
+ }) => {
43102
+ return processChartDateData({
43103
+ mainData,
43104
+ timeFilter,
43105
+ filters,
43106
+ isCumulative,
43107
+ valueField
43108
+ });
43109
+ }, [timeFilter]);
43110
+ return {
43111
+ timeFilter,
43112
+ setTimeFilter,
43113
+ getFormatDate: getFormatDateFn,
43114
+ getTimeQuantity: getTimeQuantityFn,
43115
+ formatDateAxis: formatDateAxisFn,
43116
+ processChartDateData: processData
43117
+ };
43118
+ };
43119
+
43120
+ const selectOptions$2 = [{
43121
+ label: "Daily",
43122
+ value: "daily"
43123
+ }, {
43124
+ label: "Weekly",
43125
+ value: "weekly"
43126
+ }, {
43127
+ label: "Monthly",
43128
+ value: "monthly"
43129
+ }];
43130
+ const RestoredArea = ({
43131
+ restoredAreaChart,
43132
+ t = s => s
43133
+ }) => {
43134
+ const {
43135
+ timeFilter,
43136
+ setTimeFilter,
43137
+ formatDateAxis,
43138
+ processChartDateData
43139
+ } = useTimeFilter({
43140
+ defaultFilter: 'monthly'
43141
+ });
43142
+
43143
+ // Map restoredAreaChart data to LineChart format with time filter support
43144
+ // Y-axis: total/cumulated value
43145
+ // X-axis: date (formatted based on timeFilter)
43146
+ // Fill all periods in the range, even if empty
43147
+ const restoredAreaChartData = React.useMemo(() => {
43148
+ if (!restoredAreaChart || !Array.isArray(restoredAreaChart) || restoredAreaChart.length === 0) {
43149
+ return [];
43150
+ }
43151
+
43152
+ // Process data with cumulative calculation (for restored area)
43153
+ return processChartDateData({
43154
+ mainData: restoredAreaChart,
43155
+ isCumulative: true,
43156
+ valueField: 'total'
43157
+ });
43158
+ }, [restoredAreaChart, processChartDateData]);
43159
+
43160
+ // Calculate max value for yAxis dynamically (round up to nearest 10)
43161
+ const maxYValue = React.useMemo(() => {
43162
+ if (!restoredAreaChartData || restoredAreaChartData.length === 0) {
43163
+ return 80;
43164
+ }
43165
+ const maxValue = Math.max(...restoredAreaChartData.map(item => item.value || 0));
43166
+ // If max is 0, set default to 80 to show Y-axis
43167
+ // Otherwise, round up to nearest 10
43168
+ if (maxValue === 0) {
43169
+ return 80;
43170
+ }
43171
+ return Math.ceil(maxValue / 10) * 10 || 80;
43172
+ }, [restoredAreaChartData]);
43173
+ return /*#__PURE__*/jsxRuntime.jsx(Widget, {
43174
+ title: t("Restored Area"),
43175
+ className: "with-border-header h-w-btn-header",
43176
+ addedHeader: /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
43177
+ children: [/*#__PURE__*/jsxRuntime.jsx("div", {
43178
+ className: "flex-1"
43179
+ }), /*#__PURE__*/jsxRuntime.jsx(antd.Select, {
43180
+ value: timeFilter,
43181
+ style: {
43182
+ width: 100
43183
+ },
43184
+ onChange: value => setTimeFilter(value),
43185
+ options: selectOptions$2,
43186
+ popupMatchSelectWidth: 120
43187
+ })]
43188
+ }),
43189
+ children: /*#__PURE__*/jsxRuntime.jsx("div", {
43190
+ className: "flex flex-1 flex-column justify-content-center",
43191
+ children: /*#__PURE__*/jsxRuntime.jsx("div", {
43192
+ className: "flex justify-content-center w-full",
43193
+ children: /*#__PURE__*/jsxRuntime.jsx(LineChart, {
43194
+ animated: true,
43195
+ isArea: true,
43196
+ color: '#00AEB1',
43197
+ data: restoredAreaChartData,
43198
+ fillOpacity: 0.7,
43199
+ height: 200,
43200
+ renderTooltipContent: (title, data) => {
43201
+ if (!data || data.length === 0) return {};
43202
+ const item = data[0]?.data || data[0];
43203
+ const periodValue = item?.period !== undefined ? item.period : item?.value || 0;
43204
+ const cumulatedValue = item?.value || 0;
43205
+ return {
43206
+ title: t("Restored Area"),
43207
+ subTitle: formatDateAxis(title),
43208
+ items: [{
43209
+ label: t("Period"),
43210
+ value: `${periodValue.toLocaleString()} ha`
43211
+ }, {
43212
+ label: t("Cumulated"),
43213
+ value: `${cumulatedValue.toLocaleString()} ha`
43214
+ }]
43215
+ };
43216
+ },
43217
+ xFieldKey: "date",
43218
+ yFieldKey: "value",
43219
+ formattedXAxis: formatDateAxis,
43220
+ style: {
43221
+ width: '100%'
43222
+ },
43223
+ yAxis: {
43224
+ min: 0,
43225
+ max: maxYValue,
43226
+ tickMethod: () => {
43227
+ // Generate ticks every 10 units for maxYValue of 80
43228
+ // For other values, show ticks every 10 units
43229
+ const step = maxYValue <= 80 ? 10 : Math.max(10, Math.floor(maxYValue / 8));
43230
+ const ticks = [];
43231
+ for (let i = 0; i <= maxYValue; i += step) {
43232
+ ticks.push(i);
43233
+ }
43234
+ // Ensure max value is included
43235
+ if (ticks.length === 0 || ticks[ticks.length - 1] < maxYValue) {
43236
+ ticks.push(maxYValue);
43237
+ }
43238
+ return ticks;
43239
+ },
43240
+ label: {
43241
+ style: {
43242
+ fontSize: 12,
43243
+ fill: '#666'
43244
+ }
43245
+ },
43246
+ grid: {
43247
+ line: {
43248
+ style: {
43249
+ stroke: '#E5E7EB',
43250
+ lineWidth: 1
43251
+ }
43252
+ }
43253
+ }
43254
+ },
43255
+ xAxis: {
43256
+ label: {
43257
+ formatter: formatDateAxis,
43258
+ style: {
43259
+ fontSize: 12,
43260
+ fill: '#666'
43261
+ }
43262
+ }
43263
+ }
43264
+ })
43265
+ })
43266
+ })
43267
+ });
43268
+ };
43269
+
43270
+ const selectOptions$1 = [{
43271
+ label: "Daily",
43272
+ value: "daily"
43273
+ }, {
43274
+ label: "Weekly",
43275
+ value: "weekly"
43276
+ }, {
43277
+ label: "Monthly",
43278
+ value: "monthly"
43279
+ }];
43280
+ const PlantingActivitiesTimeline = ({
43281
+ activitiesTimelineChart,
43282
+ t = s => s
43283
+ }) => {
43284
+ const {
43285
+ timeFilter,
43286
+ setTimeFilter,
43287
+ formatDateAxis,
43288
+ processChartDateData
43289
+ } = useTimeFilter({
43290
+ defaultFilter: 'monthly'
43291
+ });
43292
+
43293
+ // Map activitiesTimelineChart data to ColumnChart format with time filter support
43294
+ // Data structure: [{count: 2, date: "2025-11-03"}]
43295
+ // Fill all periods in the range, even if empty
43296
+ const activitiesTimelineData = React.useMemo(() => {
43297
+ if (!activitiesTimelineChart || !Array.isArray(activitiesTimelineChart) || activitiesTimelineChart.length === 0) {
43298
+ return [];
43299
+ }
43300
+
43301
+ // Process data without cumulative calculation (for activities timeline)
43302
+ return processChartDateData({
43303
+ mainData: activitiesTimelineChart,
43304
+ isCumulative: false,
43305
+ valueField: 'count'
43306
+ });
43307
+ }, [activitiesTimelineChart, processChartDateData]);
43308
+
43309
+ // Calculate max value for Y-axis (default to 100 if all values are 0 or very small)
43310
+ const maxActivitiesYValue = React.useMemo(() => {
43311
+ if (!activitiesTimelineData || activitiesTimelineData.length === 0) {
43312
+ return 100;
43313
+ }
43314
+ const maxValue = Math.max(...activitiesTimelineData.map(item => item.jobs || 0));
43315
+ // If max is 0, set default to 100 to show Y-axis
43316
+ if (maxValue === 0) {
43317
+ return 100;
43318
+ }
43319
+ // Round up to nearest 10, but ensure minimum of 100
43320
+ const roundedMax = Math.ceil(maxValue / 10) * 10;
43321
+ return Math.max(100, roundedMax);
43322
+ }, [activitiesTimelineData]);
43323
+ return /*#__PURE__*/jsxRuntime.jsx(Widget, {
43324
+ title: t("Planting Activities Timeline"),
43325
+ className: "with-border-header h-w-btn-header",
43326
+ addedHeader: /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
43327
+ children: [/*#__PURE__*/jsxRuntime.jsx("div", {
43328
+ className: "flex-1"
43329
+ }), /*#__PURE__*/jsxRuntime.jsx(antd.Select, {
43330
+ value: timeFilter,
43331
+ style: {
43332
+ width: 100
43333
+ },
43334
+ onChange: value => setTimeFilter(value),
43335
+ options: selectOptions$1,
43336
+ popupMatchSelectWidth: 120
43337
+ })]
43338
+ }),
43339
+ children: /*#__PURE__*/jsxRuntime.jsx("div", {
43340
+ className: "flex flex-1 flex-column justify-content-center",
43341
+ children: /*#__PURE__*/jsxRuntime.jsx("div", {
43342
+ className: "flex justify-content-center w-full",
43343
+ children: /*#__PURE__*/jsxRuntime.jsx(ColumnChart, {
43344
+ data: activitiesTimelineData,
43345
+ xFieldKey: "date",
43346
+ yFieldKey: "jobs",
43347
+ animated: true,
43348
+ height: 400,
43349
+ color: "#016C6E",
43350
+ renderTooltipContent: (title, data) => {
43351
+ if (!data || data.length === 0) return {};
43352
+ // For ColumnChart, data structure: data[0]?.data contains the actual data point
43353
+ const item = data[0]?.data || data[0];
43354
+ const count = item?.jobs || item?.value || 0;
43355
+ // Title is the X-axis value (month/date), use it for formatting
43356
+ const dateValue = item?.date || title || '';
43357
+ return {
43358
+ title: t("Planting Activities"),
43359
+ subTitle: formatDateAxis(dateValue),
43360
+ items: [{
43361
+ label: t("Total"),
43362
+ value: count
43363
+ }]
43364
+ };
43365
+ },
43366
+ formattedXAxis: formatDateAxis,
43367
+ formattedYAxis: value => {
43368
+ return `${value}`.replace(/\d{1,3}(?=(\d{3})+$)/g, s => `${s},`);
43369
+ },
43370
+ yAxis: {
43371
+ min: 0,
43372
+ max: maxActivitiesYValue,
43373
+ tickMethod: () => {
43374
+ // Generate ticks: for 100 max, show 0, 20, 40, 60, 80, 100
43375
+ // For other values, show ticks every 20 units
43376
+ const step = maxActivitiesYValue <= 100 ? 20 : Math.max(20, Math.floor(maxActivitiesYValue / 5));
43377
+ const ticks = [];
43378
+ for (let i = 0; i <= maxActivitiesYValue; i += step) {
43379
+ ticks.push(i);
43380
+ }
43381
+ // Ensure max value is included
43382
+ if (ticks.length === 0 || ticks[ticks.length - 1] < maxActivitiesYValue) {
43383
+ ticks.push(maxActivitiesYValue);
43384
+ }
43385
+ return ticks;
43386
+ },
43387
+ label: {
43388
+ style: {
43389
+ fontSize: 12,
43390
+ fill: '#666'
43391
+ }
43392
+ },
43393
+ grid: {
43394
+ line: {
43395
+ style: {
43396
+ stroke: '#E5E7EB',
43397
+ lineWidth: 1
43398
+ }
43399
+ }
43400
+ }
43401
+ },
43402
+ xAxis: {
43403
+ label: {
43404
+ formatter: formatDateAxis,
43405
+ autoHide: true,
43406
+ style: {
43407
+ fontSize: 12,
43408
+ fill: '#666'
43409
+ }
43410
+ }
43411
+ }
43412
+ })
43413
+ })
43414
+ })
43415
+ });
43416
+ };
43417
+
42839
43418
  const CycleOutcomes = ({
42840
43419
  id,
42841
43420
  getSummaryDetail,
@@ -42896,223 +43475,6 @@ const CycleOutcomes = ({
42896
43475
  format: 'absolute'
42897
43476
  });
42898
43477
  }, [locationsCount, t]);
42899
-
42900
- // Map restoredAreaChart data to LineChart format
42901
- // Y-axis: total/cumulated value
42902
- // X-axis: date
42903
- // Fill all months in the range, even if empty
42904
- const restoredAreaChartData = React.useMemo(() => {
42905
- // Always show last 12 months, even if no data
42906
- const now = dayjs__default["default"]().startOf('month');
42907
- const twelveMonthsAgo = now.subtract(11, 'month'); // 11 months ago + current month = 12 months
42908
-
42909
- // Create a map of existing data by month (YYYY-MM format)
42910
- const dataMap = new Map();
42911
- const dates = [];
42912
-
42913
- // Process restored area data if available
42914
- if (restoredAreaChart && Array.isArray(restoredAreaChart) && restoredAreaChart.length > 0) {
42915
- restoredAreaChart.forEach(item => {
42916
- if (typeof item === 'object' && item !== null) {
42917
- // Priority: look for date field first
42918
- const dateValue = item.date || item.label || item.name || item.period || item.month;
42919
- if (dateValue) {
42920
- const date = dayjs__default["default"](dateValue);
42921
- if (date.isValid()) {
42922
- const monthKey = date.format('YYYY-MM');
42923
- // Total/cumulated value for Y-axis (this is what gets plotted)
42924
- const totalValue = Number(item.cumulated || item.cumulative || item.total || item.value || 0) || 0;
42925
- // Period value for tooltip only
42926
- const periodValue = Number(item.period || item.area || item.count || 0) || 0;
42927
- dates.push(date);
42928
-
42929
- // If multiple entries for same month, use the latest one (or sum if needed)
42930
- if (!dataMap.has(monthKey) || dataMap.get(monthKey).value < totalValue) {
42931
- dataMap.set(monthKey, {
42932
- date: dateValue,
42933
- value: totalValue,
42934
- period: periodValue
42935
- });
42936
- }
42937
- }
42938
- }
42939
- }
42940
- });
42941
- }
42942
-
42943
- // Determine date range
42944
- let minDate = twelveMonthsAgo;
42945
- let maxDate = now;
42946
-
42947
- // If we have data, adjust range to include it
42948
- if (dates.length > 0) {
42949
- const sortedDates = dates.sort((a, b) => a.valueOf() - b.valueOf());
42950
- const firstDataDate = sortedDates[0].startOf('month');
42951
- const lastDataDate = sortedDates[sortedDates.length - 1].startOf('month');
42952
-
42953
- // Start from the earlier of: 12 months ago, or first data date
42954
- minDate = twelveMonthsAgo.isBefore(firstDataDate) ? twelveMonthsAgo : firstDataDate;
42955
-
42956
- // End at the later of: current month, or last data date
42957
- maxDate = now.isAfter(lastDataDate) ? now : lastDataDate;
42958
- }
42959
-
42960
- // Generate all months in the range
42961
- const result = [];
42962
- let currentDate = minDate.clone();
42963
- let lastKnownValue = 0; // For cumulative data, carry forward the last known value
42964
-
42965
- while (currentDate.isBefore(maxDate) || currentDate.isSame(maxDate, 'month')) {
42966
- const monthKey = currentDate.format('YYYY-MM');
42967
- const existingData = dataMap.get(monthKey);
42968
- if (existingData) {
42969
- lastKnownValue = existingData.value;
42970
- result.push(existingData);
42971
- } else {
42972
- // Fill missing month - for cumulative data, use last known value
42973
- result.push({
42974
- date: currentDate.format('YYYY-MM-DD'),
42975
- value: lastKnownValue,
42976
- // Carry forward cumulative value
42977
- period: 0
42978
- });
42979
- }
42980
- currentDate = currentDate.add(1, 'month');
42981
- }
42982
- return result;
42983
- }, [restoredAreaChart]);
42984
-
42985
- // Calculate max value for yAxis dynamically (round up to nearest 10)
42986
- const maxYValue = React.useMemo(() => {
42987
- if (!restoredAreaChartData || restoredAreaChartData.length === 0) {
42988
- return 80;
42989
- }
42990
- const maxValue = Math.max(...restoredAreaChartData.map(item => item.value || 0));
42991
- // If max is 0, set default to 80 to show Y-axis
42992
- // Otherwise, round up to nearest 10
42993
- if (maxValue === 0) {
42994
- return 80;
42995
- }
42996
- return Math.ceil(maxValue / 10) * 10 || 80;
42997
- }, [restoredAreaChartData]);
42998
-
42999
- // Format date to "Mmm YY" format for X-axis
43000
- const formatDateAxis = React.useMemo(() => {
43001
- return label => {
43002
- if (!label) return label;
43003
-
43004
- // Try to parse the date using dayjs with various formats
43005
- let date = dayjs__default["default"](label);
43006
-
43007
- // If first attempt fails, try parsing as ISO date string
43008
- if (!date.isValid() && typeof label === 'string') {
43009
- date = dayjs__default["default"](label, ['YYYY-MM-DD', 'YYYY-MM', 'MMM YY', 'MMM YYYY'], true);
43010
- }
43011
-
43012
- // If it's a valid date, format it as "Mmm YY"
43013
- if (date.isValid()) {
43014
- return date.format('MMM YY');
43015
- }
43016
-
43017
- // If it's already in "Mmm YY" format or similar, return as is
43018
- // Otherwise return the original label
43019
- return label;
43020
- };
43021
- }, []);
43022
-
43023
- // Map activitiesTimelineChart data to ColumnChart format
43024
- // Data structure: [{count: 2, date: "2025-11-03"}]
43025
- // Fill all months in the range, even if empty
43026
- const activitiesTimelineData = React.useMemo(() => {
43027
- // Always show last 12 months, even if no data
43028
- const now = dayjs__default["default"]().startOf('month');
43029
- const twelveMonthsAgo = now.subtract(11, 'month'); // 11 months ago + current month = 12 months
43030
-
43031
- // Create a map of existing data by month (YYYY-MM format)
43032
- const dataMap = new Map();
43033
- const dates = [];
43034
-
43035
- // Process activities timeline data if available
43036
- if (activitiesTimelineChart && Array.isArray(activitiesTimelineChart) && activitiesTimelineChart.length > 0) {
43037
- activitiesTimelineChart.forEach(item => {
43038
- if (typeof item === 'object' && item !== null && item.date) {
43039
- const date = dayjs__default["default"](item.date);
43040
- if (date.isValid()) {
43041
- const monthKey = date.format('YYYY-MM');
43042
- const count = Number(item.count || item.jobs || item.value || 0) || 0;
43043
- dates.push(date);
43044
-
43045
- // If multiple entries for same month, sum them
43046
- if (dataMap.has(monthKey)) {
43047
- dataMap.set(monthKey, {
43048
- ...dataMap.get(monthKey),
43049
- jobs: dataMap.get(monthKey).jobs + count
43050
- });
43051
- } else {
43052
- dataMap.set(monthKey, {
43053
- month: item.date,
43054
- jobs: count,
43055
- date: item.date
43056
- });
43057
- }
43058
- }
43059
- }
43060
- });
43061
- }
43062
-
43063
- // Determine date range
43064
- let minDate = twelveMonthsAgo;
43065
- let maxDate = now;
43066
-
43067
- // If we have data, adjust range to include it
43068
- if (dates.length > 0) {
43069
- const sortedDates = dates.sort((a, b) => a.valueOf() - b.valueOf());
43070
- const firstDataDate = sortedDates[0].startOf('month');
43071
- const lastDataDate = sortedDates[sortedDates.length - 1].startOf('month');
43072
-
43073
- // Start from the earlier of: 12 months ago, or first data date
43074
- minDate = twelveMonthsAgo.isBefore(firstDataDate) ? twelveMonthsAgo : firstDataDate;
43075
-
43076
- // End at the later of: current month, or last data date
43077
- maxDate = now.isAfter(lastDataDate) ? now : lastDataDate;
43078
- }
43079
-
43080
- // Generate all months in the range
43081
- const result = [];
43082
- let currentDate = minDate.clone();
43083
- while (currentDate.isBefore(maxDate) || currentDate.isSame(maxDate, 'month')) {
43084
- const monthKey = currentDate.format('YYYY-MM');
43085
- const existingData = dataMap.get(monthKey);
43086
- if (existingData) {
43087
- result.push(existingData);
43088
- } else {
43089
- // Fill missing month with 0
43090
- result.push({
43091
- month: currentDate.format('YYYY-MM-DD'),
43092
- // Use first day of month
43093
- jobs: 0,
43094
- date: currentDate.format('YYYY-MM-DD')
43095
- });
43096
- }
43097
- currentDate = currentDate.add(1, 'month');
43098
- }
43099
- return result;
43100
- }, [activitiesTimelineChart]);
43101
-
43102
- // Calculate max value for Y-axis (default to 100 if all values are 0 or very small)
43103
- const maxActivitiesYValue = React.useMemo(() => {
43104
- if (!activitiesTimelineData || activitiesTimelineData.length === 0) {
43105
- return 100;
43106
- }
43107
- const maxValue = Math.max(...activitiesTimelineData.map(item => item.jobs || 0));
43108
- // If max is 0, set default to 100 to show Y-axis
43109
- if (maxValue === 0) {
43110
- return 100;
43111
- }
43112
- // Round up to nearest 10, but ensure minimum of 100
43113
- const roundedMax = Math.ceil(maxValue / 10) * 10;
43114
- return Math.max(100, roundedMax);
43115
- }, [activitiesTimelineData]);
43116
43478
  return /*#__PURE__*/jsxRuntime.jsx("section", {
43117
43479
  children: /*#__PURE__*/jsxRuntime.jsxs(Widget, {
43118
43480
  title: t("Restoration Cycle Outcomes"),
@@ -43154,172 +43516,17 @@ const CycleOutcomes = ({
43154
43516
  style: {
43155
43517
  flex: 1
43156
43518
  },
43157
- children: /*#__PURE__*/jsxRuntime.jsx(Widget, {
43158
- title: t("Restored Area"),
43159
- className: "with-border-header h-w-btn-header",
43160
- children: /*#__PURE__*/jsxRuntime.jsx("div", {
43161
- className: "flex flex-1 flex-column justify-content-center",
43162
- children: /*#__PURE__*/jsxRuntime.jsx("div", {
43163
- className: "flex justify-content-center w-full",
43164
- children: /*#__PURE__*/jsxRuntime.jsx(LineChart, {
43165
- animated: true,
43166
- isArea: true,
43167
- color: '#00AEB1',
43168
- data: restoredAreaChartData,
43169
- fillOpacity: 0.7,
43170
- height: 200,
43171
- renderTooltipContent: (title, data) => {
43172
- if (!data || data.length === 0) return {};
43173
- const item = data[0]?.data || data[0];
43174
- const periodValue = item?.period !== undefined ? item.period : item?.value || 0;
43175
- const cumulatedValue = item?.value || 0;
43176
- return {
43177
- title: t("Restored Area"),
43178
- subTitle: formatDateAxis(title),
43179
- items: [{
43180
- label: t("Period"),
43181
- value: `${periodValue.toLocaleString()} ha`
43182
- }, {
43183
- label: t("Cumulated"),
43184
- value: `${cumulatedValue.toLocaleString()} ha`
43185
- }]
43186
- };
43187
- },
43188
- xFieldKey: "date",
43189
- yFieldKey: "value",
43190
- formattedXAxis: formatDateAxis,
43191
- style: {
43192
- width: '100%'
43193
- },
43194
- yAxis: {
43195
- min: 0,
43196
- max: maxYValue,
43197
- tickMethod: () => {
43198
- // Generate ticks every 10 units for maxYValue of 80
43199
- // For other values, show ticks every 10 units
43200
- const step = maxYValue <= 80 ? 10 : Math.max(10, Math.floor(maxYValue / 8));
43201
- const ticks = [];
43202
- for (let i = 0; i <= maxYValue; i += step) {
43203
- ticks.push(i);
43204
- }
43205
- // Ensure max value is included
43206
- if (ticks.length === 0 || ticks[ticks.length - 1] < maxYValue) {
43207
- ticks.push(maxYValue);
43208
- }
43209
- return ticks;
43210
- },
43211
- label: {
43212
- style: {
43213
- fontSize: 12,
43214
- fill: '#666'
43215
- }
43216
- },
43217
- grid: {
43218
- line: {
43219
- style: {
43220
- stroke: '#E5E7EB',
43221
- lineWidth: 1
43222
- }
43223
- }
43224
- }
43225
- },
43226
- xAxis: {
43227
- label: {
43228
- formatter: formatDateAxis,
43229
- // Ensure formatter is applied
43230
- style: {
43231
- fontSize: 12,
43232
- fill: '#666'
43233
- }
43234
- }
43235
- }
43236
- })
43237
- })
43238
- })
43519
+ children: /*#__PURE__*/jsxRuntime.jsx(RestoredArea, {
43520
+ restoredAreaChart: restoredAreaChart,
43521
+ t: t
43239
43522
  })
43240
43523
  }), /*#__PURE__*/jsxRuntime.jsx("section", {
43241
43524
  style: {
43242
43525
  flex: 1
43243
43526
  },
43244
- children: /*#__PURE__*/jsxRuntime.jsx(Widget, {
43245
- title: t("Planting Activities Timeline"),
43246
- className: "with-border-header h-w-btn-header",
43247
- children: /*#__PURE__*/jsxRuntime.jsx("div", {
43248
- className: "flex flex-1 flex-column justify-content-center",
43249
- children: /*#__PURE__*/jsxRuntime.jsx("div", {
43250
- className: "flex justify-content-center w-full",
43251
- children: /*#__PURE__*/jsxRuntime.jsx(ColumnChart, {
43252
- data: activitiesTimelineData,
43253
- xFieldKey: "month",
43254
- yFieldKey: "jobs",
43255
- animated: true,
43256
- height: 400,
43257
- color: "#016C6E",
43258
- renderTooltipContent: (title, data) => {
43259
- if (!data || data.length === 0) return {};
43260
- // For ColumnChart, data structure: data[0]?.data contains the actual data point
43261
- const item = data[0]?.data || data[0];
43262
- const count = item?.jobs || item?.value || 0;
43263
- // Title is the X-axis value (month/date), use it for formatting
43264
- const dateValue = item?.date || title || '';
43265
- return {
43266
- title: t("Planting Activities"),
43267
- subTitle: formatDateAxis(dateValue),
43268
- items: [{
43269
- label: t("Total"),
43270
- value: count
43271
- }]
43272
- };
43273
- },
43274
- formattedXAxis: formatDateAxis,
43275
- formattedYAxis: value => {
43276
- return `${value}`.replace(/\d{1,3}(?=(\d{3})+$)/g, s => `${s},`);
43277
- },
43278
- yAxis: {
43279
- min: 0,
43280
- max: maxActivitiesYValue,
43281
- tickMethod: () => {
43282
- // Generate ticks: for 100 max, show 0, 20, 40, 60, 80, 100
43283
- // For other values, show ticks every 20 units
43284
- const step = maxActivitiesYValue <= 100 ? 20 : Math.max(20, Math.floor(maxActivitiesYValue / 5));
43285
- const ticks = [];
43286
- for (let i = 0; i <= maxActivitiesYValue; i += step) {
43287
- ticks.push(i);
43288
- }
43289
- // Ensure max value is included
43290
- if (ticks.length === 0 || ticks[ticks.length - 1] < maxActivitiesYValue) {
43291
- ticks.push(maxActivitiesYValue);
43292
- }
43293
- return ticks;
43294
- },
43295
- label: {
43296
- style: {
43297
- fontSize: 12,
43298
- fill: '#666'
43299
- }
43300
- },
43301
- grid: {
43302
- line: {
43303
- style: {
43304
- stroke: '#E5E7EB',
43305
- lineWidth: 1
43306
- }
43307
- }
43308
- }
43309
- },
43310
- xAxis: {
43311
- label: {
43312
- formatter: formatDateAxis,
43313
- autoHide: true,
43314
- style: {
43315
- fontSize: 12,
43316
- fill: '#666'
43317
- }
43318
- }
43319
- }
43320
- })
43321
- })
43322
- })
43527
+ children: /*#__PURE__*/jsxRuntime.jsx(PlantingActivitiesTimeline, {
43528
+ activitiesTimelineChart: activitiesTimelineChart,
43529
+ t: t
43323
43530
  })
43324
43531
  })]
43325
43532
  })]
@@ -43445,8 +43652,8 @@ const CyclePartners = ({
43445
43652
  };
43446
43653
 
43447
43654
  const HEALTH_SAFETY_COLORS = {
43448
- compliant: '#52C41A',
43449
- notCompliant: '#FF4D4F',
43655
+ compliant: '#016C6E',
43656
+ notCompliant: '#F97066',
43450
43657
  empty: '#D9D9D9'
43451
43658
  };
43452
43659
  const getIndicatorType = value => {
@@ -43515,8 +43722,9 @@ const isHealthAndSafetyDistributionEmpty = healthAndSafetyDistributionData => {
43515
43722
  const calculateHealthAndSafetyPieData = (healthAndSafetyDistributionData, t) => {
43516
43723
  const total = Object.values(healthAndSafetyDistributionData).reduce((all, val) => all + (val || 0), 0);
43517
43724
  const labels = {
43518
- compliant: t("Compliant"),
43519
- notCompliant: t("Not Compliant")
43725
+ compliant: t("Available"),
43726
+ notCompliant: t("Not available"),
43727
+ empty: t("Not answered")
43520
43728
  };
43521
43729
  return Object.keys(healthAndSafetyDistributionData).map(key => {
43522
43730
  const color = HEALTH_SAFETY_COLORS[key] || '#D9D9D9';
@@ -43554,8 +43762,9 @@ const getHealthAndSafetyTooltipChildren = (item, isEmpty, healthAndSafetyDistrib
43554
43762
  return null;
43555
43763
  }
43556
43764
  const labels = {
43557
- compliant: t("Compliant"),
43558
- notCompliant: t("Not Compliant")
43765
+ compliant: t("Available"),
43766
+ notCompliant: t("Not available"),
43767
+ empty: t("Not answered")
43559
43768
  };
43560
43769
 
43561
43770
  // Filter items with values > 0
@@ -43582,16 +43791,93 @@ const getHealthAndSafetyTooltipChildren = (item, isEmpty, healthAndSafetyDistrib
43582
43791
  };
43583
43792
 
43584
43793
  const HealthAndSafety = ({
43585
- activityData,
43794
+ id,
43795
+ getSummaryDetail,
43586
43796
  loading = false,
43587
43797
  t = s => s
43588
43798
  }) => {
43589
- const healthAndSafetyDistributionData = React.useMemo(() => getHealthAndSafetyDistributionData(activityData), [activityData]);
43799
+ const defaultConfig = React.useMemo(() => ({
43800
+ basepath: "planting-cycle",
43801
+ url: `/summary/${id}/filtered-piechart`,
43802
+ filters: {
43803
+ field: 'duosFormed'
43804
+ },
43805
+ stop: !id
43806
+ }), [id]);
43807
+ const customGetData = React.useMemo(() => {
43808
+ if (getSummaryDetail && id) {
43809
+ return rest => {
43810
+ const {
43811
+ url,
43812
+ filters: restFilters
43813
+ } = rest;
43814
+ const match = url.match(/\/summary\/[^/]+\/(.+)/);
43815
+ if (match) {
43816
+ const [, type] = match;
43817
+ // Pass filters as params for the query string
43818
+ const params = {
43819
+ ...(restFilters || {})
43820
+ };
43821
+ return getSummaryDetail(id, type, params);
43822
+ }
43823
+ throw new Error(`Invalid URL format: ${url}`);
43824
+ };
43825
+ }
43826
+ return undefined;
43827
+ }, [getSummaryDetail, id]);
43828
+ const {
43829
+ loading: pieChartLoading,
43830
+ data: pieChartData
43831
+ } = useWidgetFetch({
43832
+ config: defaultConfig,
43833
+ getData: customGetData
43834
+ });
43835
+
43836
+ // Process the fetched pie chart data
43837
+ // The API returns data in format: [{count: 1, duosFormed: "null"}, {count: 1, duosFormed: "no"}, {count: 1, duosFormed: "yes"}]
43838
+ const healthAndSafetyDistributionData = React.useMemo(() => {
43839
+ if (!pieChartData) return {
43840
+ compliant: 0,
43841
+ notCompliant: 0,
43842
+ empty: 0
43843
+ };
43844
+
43845
+ // If it's already a distribution object
43846
+ if (pieChartData.compliant !== undefined || pieChartData.notCompliant !== undefined) {
43847
+ return pieChartData;
43848
+ }
43849
+
43850
+ // If it's an array, process it
43851
+ if (Array.isArray(pieChartData)) {
43852
+ const distribution = {
43853
+ compliant: 0,
43854
+ notCompliant: 0,
43855
+ empty: 0
43856
+ };
43857
+ pieChartData.forEach(item => {
43858
+ const duosFormedValue = item.duosFormed;
43859
+ const count = item.count || 0;
43860
+
43861
+ // Map duosFormed values to distribution categories
43862
+ if (duosFormedValue === "yes" || duosFormedValue === true) {
43863
+ distribution.compliant += count;
43864
+ } else if (duosFormedValue === "no" || duosFormedValue === false) {
43865
+ distribution.notCompliant += count;
43866
+ } else if (duosFormedValue === "null" || duosFormedValue === null || duosFormedValue === undefined) {
43867
+ distribution.empty += count;
43868
+ }
43869
+ });
43870
+ return distribution;
43871
+ }
43872
+
43873
+ // Fallback: try to extract from activityData-like structure
43874
+ return getHealthAndSafetyDistributionData(pieChartData);
43875
+ }, [pieChartData]);
43590
43876
  const isEmpty = React.useMemo(() => isHealthAndSafetyDistributionEmpty(healthAndSafetyDistributionData), [healthAndSafetyDistributionData]);
43591
43877
  const pieData = React.useMemo(() => calculateHealthAndSafetyPieData(healthAndSafetyDistributionData, t), [healthAndSafetyDistributionData, t]);
43592
43878
  const getTooltipChildren = React.useCallback(item => getHealthAndSafetyTooltipChildren(item, isEmpty, healthAndSafetyDistributionData, t, renderTooltipJsx), [t, isEmpty, healthAndSafetyDistributionData]);
43593
43879
  return /*#__PURE__*/jsxRuntime.jsx(Widget, {
43594
- loading: loading,
43880
+ loading: loading || pieChartLoading,
43595
43881
  title: /*#__PURE__*/jsxRuntime.jsx("div", {
43596
43882
  children: t("Health and Safety")
43597
43883
  }),
@@ -43726,7 +44012,8 @@ const CycleIndicators = ({
43726
44012
  flex: 1
43727
44013
  },
43728
44014
  children: /*#__PURE__*/jsxRuntime.jsx(HealthAndSafety, {
43729
- activityData: indicatorsData,
44015
+ id: id,
44016
+ getSummaryDetail: getSummaryDetail,
43730
44017
  loading: indicatorsLoading,
43731
44018
  t: t
43732
44019
  })
@@ -43873,110 +44160,43 @@ const GenderDistribution = ({
43873
44160
  });
43874
44161
  };
43875
44162
 
44163
+ const selectOptions = [{
44164
+ label: "Daily",
44165
+ value: "daily"
44166
+ }, {
44167
+ label: "Weekly",
44168
+ value: "weekly"
44169
+ }, {
44170
+ label: "Monthly",
44171
+ value: "monthly"
44172
+ }];
43876
44173
  const JobsTimeline = ({
43877
44174
  dayJobsTimeline,
43878
44175
  loading = false,
43879
44176
  t = s => s
43880
44177
  }) => {
43881
- console.log('dayJobsTimeline', dayJobsTimeline);
44178
+ const {
44179
+ timeFilter,
44180
+ setTimeFilter,
44181
+ formatDateAxis,
44182
+ processChartDateData
44183
+ } = useTimeFilter({
44184
+ defaultFilter: 'monthly'
44185
+ });
43882
44186
  const jobsData = Array.isArray(dayJobsTimeline) ? dayJobsTimeline : dayJobsTimeline?.jobsTimeline || dayJobsTimeline?.jobs || dayJobsTimeline?.timeline || [];
43883
- const formatDateAxis = React.useMemo(() => {
43884
- return label => {
43885
- if (!label) return label;
43886
-
43887
- // Try to parse the date using dayjs with various formats
43888
- let date = dayjs__default["default"](label);
43889
-
43890
- // If first attempt fails, try parsing as ISO date string
43891
- if (!date.isValid() && typeof label === 'string') {
43892
- date = dayjs__default["default"](label, ['YYYY-MM-DD', 'YYYY-MM', 'MMM YY', 'MMM YYYY'], true);
43893
- }
43894
-
43895
- // If it's a valid date, format it as "Mmm YY"
43896
- if (date.isValid()) {
43897
- return date.format('MMM YY');
43898
- }
43899
-
43900
- // If it's already in "Mmm YY" format or similar, return as is
43901
- // Otherwise return the original label
43902
- return label;
43903
- };
43904
- }, []);
43905
44187
  const jobsTimelineData = React.useMemo(() => {
43906
- // Always show last 12 months, even if no data
43907
- const now = dayjs__default["default"]().startOf('month');
43908
- const twelveMonthsAgo = now.subtract(11, 'month'); // 11 months ago + current month = 12 months
43909
-
43910
- // Create a map of existing data by month (YYYY-MM format)
43911
- const dataMap = new Map();
43912
- const dates = [];
43913
-
43914
- // Process jobs data if available
43915
- if (jobsData && Array.isArray(jobsData) && jobsData.length > 0) {
43916
- jobsData.forEach(item => {
43917
- if (typeof item === 'object' && item !== null && item.date) {
43918
- const date = dayjs__default["default"](item.date);
43919
- if (date.isValid()) {
43920
- const monthKey = date.format('YYYY-MM');
43921
- const count = Number(item.total || item.count || item.jobs || item.value || 0) || 0;
43922
- dates.push(date);
43923
-
43924
- // If multiple entries for same month, sum them
43925
- if (dataMap.has(monthKey)) {
43926
- dataMap.set(monthKey, {
43927
- ...dataMap.get(monthKey),
43928
- jobs: dataMap.get(monthKey).jobs + count
43929
- });
43930
- } else {
43931
- dataMap.set(monthKey, {
43932
- month: item.date,
43933
- jobs: count,
43934
- date: item.date
43935
- });
43936
- }
43937
- }
43938
- }
43939
- });
43940
- }
43941
-
43942
- // Determine date range
43943
- let minDate = twelveMonthsAgo;
43944
- let maxDate = now;
43945
-
43946
- // If we have data, adjust range to include it
43947
- if (dates.length > 0) {
43948
- const sortedDates = dates.sort((a, b) => a.valueOf() - b.valueOf());
43949
- const firstDataDate = sortedDates[0].startOf('month');
43950
- const lastDataDate = sortedDates[sortedDates.length - 1].startOf('month');
43951
-
43952
- // Start from the earlier of: 12 months ago, or first data date
43953
- minDate = twelveMonthsAgo.isBefore(firstDataDate) ? twelveMonthsAgo : firstDataDate;
43954
-
43955
- // End at the later of: current month, or last data date
43956
- maxDate = now.isAfter(lastDataDate) ? now : lastDataDate;
44188
+ if (!jobsData || !Array.isArray(jobsData) || jobsData.length === 0) {
44189
+ return [];
43957
44190
  }
43958
44191
 
43959
- // Generate all months in the range
43960
- const result = [];
43961
- let currentDate = minDate.clone();
43962
- while (currentDate.isBefore(maxDate) || currentDate.isSame(maxDate, 'month')) {
43963
- const monthKey = currentDate.format('YYYY-MM');
43964
- const existingData = dataMap.get(monthKey);
43965
- if (existingData) {
43966
- result.push(existingData);
43967
- } else {
43968
- // Fill missing month with 0
43969
- result.push({
43970
- month: currentDate.format('YYYY-MM-DD'),
43971
- // Use first day of month
43972
- jobs: 0,
43973
- date: currentDate.format('YYYY-MM-DD')
43974
- });
43975
- }
43976
- currentDate = currentDate.add(1, 'month');
43977
- }
43978
- return result;
43979
- }, [jobsData]);
44192
+ // Process data without cumulative calculation (for jobs timeline)
44193
+ // Try to find value in total, count, jobs, or value fields
44194
+ return processChartDateData({
44195
+ mainData: jobsData,
44196
+ isCumulative: false,
44197
+ valueField: 'total' // Will fallback to count/jobs/value if total doesn't exist
44198
+ });
44199
+ }, [jobsData, processChartDateData]);
43980
44200
  const maxYValue = React.useMemo(() => {
43981
44201
  if (!jobsTimelineData || jobsTimelineData.length === 0) {
43982
44202
  return 100;
@@ -43994,9 +44214,22 @@ const JobsTimeline = ({
43994
44214
  children: t("Day Jobs Timeline")
43995
44215
  }),
43996
44216
  className: "with-border-header h-w-btn-header ",
44217
+ addedHeader: /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
44218
+ children: [/*#__PURE__*/jsxRuntime.jsx("div", {
44219
+ className: "flex-1"
44220
+ }), /*#__PURE__*/jsxRuntime.jsx(antd.Select, {
44221
+ value: timeFilter,
44222
+ style: {
44223
+ width: 100
44224
+ },
44225
+ onChange: value => setTimeFilter(value),
44226
+ options: selectOptions,
44227
+ popupMatchSelectWidth: 120
44228
+ })]
44229
+ }),
43997
44230
  children: /*#__PURE__*/jsxRuntime.jsx(ColumnChart, {
43998
44231
  data: jobsTimelineData,
43999
- xFieldKey: "month",
44232
+ xFieldKey: "date",
44000
44233
  yFieldKey: "jobs",
44001
44234
  animated: true
44002
44235
  // height={200}
@@ -44238,37 +44471,732 @@ const CommunityParticipation = ({
44238
44471
  });
44239
44472
  };
44240
44473
 
44474
+ const ACTIVITIES_TAB$1 = 'activities';
44475
+ const PARTNERS_TAB$1 = 'partners';
44476
+ const INCIDENTS_TAB$1 = 'incidents';
44477
+ const getColumns = ({
44478
+ projectId,
44479
+ t,
44480
+ show = 'show',
44481
+ navigate,
44482
+ selectOptions,
44483
+ view,
44484
+ getRedirectLink,
44485
+ activeTab = ACTIVITIES_TAB$1
44486
+ }) => {
44487
+ if (activeTab === ACTIVITIES_TAB$1) {
44488
+ return [{
44489
+ title: "",
44490
+ dataIndex: 'image',
44491
+ key: 'image',
44492
+ active: true,
44493
+ width: 80,
44494
+ pending: true,
44495
+ suspended: true,
44496
+ ellipsis: true,
44497
+ render: (v, all) => {
44498
+ if (all.empty) {
44499
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
44500
+ className: "daf-default-cell"
44501
+ });
44502
+ }
44503
+ const firstGroupPhoto = all?.groupPhotos?.[0];
44504
+ const pictures = firstGroupPhoto?.pictures;
44505
+ const firstPicture = Array.isArray(pictures) && pictures.length > 0 ? pictures[0] : null;
44506
+ let imageUrl = firstPicture?.url;
44507
+ const normalizeUrl = url => url?.endsWith(':') ? url.slice(0, -1) : url;
44508
+ let normalizedUrl = imageUrl ? normalizeUrl(imageUrl.trim()) : null;
44509
+ if (normalizedUrl && !normalizedUrl.startsWith('http://') && !normalizedUrl.startsWith('https://') && !normalizedUrl.startsWith('//')) {
44510
+ if (normalizedUrl.startsWith('cdn.') || normalizedUrl.includes('.') && !normalizedUrl.startsWith('/')) {
44511
+ normalizedUrl = `https://${normalizedUrl}`;
44512
+ }
44513
+ }
44514
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
44515
+ className: "daf-default-cell",
44516
+ style: {
44517
+ display: 'flex',
44518
+ alignItems: 'center',
44519
+ justifyContent: 'center'
44520
+ },
44521
+ children: /*#__PURE__*/jsxRuntime.jsx("div", {
44522
+ style: {
44523
+ width: 32,
44524
+ height: 32,
44525
+ borderWidth: '1px',
44526
+ borderStyle: 'solid',
44527
+ borderColor: '#E5E7EB',
44528
+ borderRadius: '6px',
44529
+ background: '#F9FAFB',
44530
+ display: 'flex',
44531
+ alignItems: 'center',
44532
+ justifyContent: 'center',
44533
+ overflow: 'hidden',
44534
+ position: 'relative'
44535
+ },
44536
+ children: normalizedUrl && /*#__PURE__*/jsxRuntime.jsx("img", {
44537
+ src: normalizedUrl,
44538
+ alt: all.name || 'Activity image',
44539
+ style: {
44540
+ width: '100%',
44541
+ height: '100%',
44542
+ objectFit: 'cover',
44543
+ borderRadius: '6px'
44544
+ },
44545
+ onError: e => {
44546
+ e.target.style.display = 'none';
44547
+ }
44548
+ })
44549
+ })
44550
+ });
44551
+ }
44552
+ }, {
44553
+ title: t('ID'),
44554
+ dataIndex: 'datastakeId',
44555
+ key: 'datastakeId',
44556
+ active: true,
44557
+ pending: true,
44558
+ suspended: true,
44559
+ ellipsis: true,
44560
+ render: (v, all) => {
44561
+ if (all.empty) {
44562
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
44563
+ className: "daf-default-cell"
44564
+ });
44565
+ }
44566
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
44567
+ className: "daf-default-cell",
44568
+ children: all.datastakeId
44569
+ });
44570
+ }
44571
+ }, {
44572
+ title: t("Title"),
44573
+ dataIndex: "name",
44574
+ key: "name",
44575
+ ellipsis: true,
44576
+ active: true,
44577
+ pending: true,
44578
+ suspended: true,
44579
+ render: value => /*#__PURE__*/jsxRuntime.jsx(antd.Tooltip, {
44580
+ title: value,
44581
+ children: value
44582
+ })
44583
+ }, {
44584
+ title: t("Type"),
44585
+ dataIndex: "type",
44586
+ key: "type",
44587
+ ellipsis: true,
44588
+ active: true,
44589
+ pending: true,
44590
+ suspended: true,
44591
+ render: (value, all) => {
44592
+ if (all.empty) {
44593
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
44594
+ className: "daf-default-cell"
44595
+ });
44596
+ }
44597
+ return /*#__PURE__*/jsxRuntime.jsx(antd.Tooltip, {
44598
+ title: value,
44599
+ children: capitalize(value) || '-'
44600
+ });
44601
+ }
44602
+ }, {
44603
+ title: t("Location"),
44604
+ dataIndex: "location",
44605
+ key: "location",
44606
+ ellipsis: true,
44607
+ active: true,
44608
+ pending: true,
44609
+ width: 220,
44610
+ suspended: true,
44611
+ show: true,
44612
+ render: (value, all) => {
44613
+ if (all.empty) {
44614
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
44615
+ className: "daf-default-cell"
44616
+ });
44617
+ }
44618
+ const location = all.location || value;
44619
+ if (location && typeof location === 'object' && typeof location.latitude === 'number' && typeof location.longitude === 'number') {
44620
+ const coordinates = convertDMS(location.latitude, location.longitude);
44621
+ const iconColor = "#016C6E";
44622
+ return /*#__PURE__*/jsxRuntime.jsxs("div", {
44623
+ style: {
44624
+ display: 'flex',
44625
+ alignItems: 'center',
44626
+ gap: '8px',
44627
+ flexWrap: 'nowrap'
44628
+ },
44629
+ children: [/*#__PURE__*/jsxRuntime.jsxs("div", {
44630
+ style: {
44631
+ display: 'flex',
44632
+ alignItems: 'center',
44633
+ gap: '4px'
44634
+ },
44635
+ children: [/*#__PURE__*/jsxRuntime.jsx(CustomIcon, {
44636
+ name: "SpacingHeight",
44637
+ width: 14,
44638
+ height: 14,
44639
+ fill: iconColor
44640
+ }), /*#__PURE__*/jsxRuntime.jsx("span", {
44641
+ children: coordinates[0]
44642
+ })]
44643
+ }), /*#__PURE__*/jsxRuntime.jsxs("div", {
44644
+ style: {
44645
+ display: 'flex',
44646
+ alignItems: 'center',
44647
+ gap: '4px'
44648
+ },
44649
+ children: [/*#__PURE__*/jsxRuntime.jsx(CustomIcon, {
44650
+ name: "SpacingWidth",
44651
+ width: 14,
44652
+ height: 14,
44653
+ fill: iconColor
44654
+ }), /*#__PURE__*/jsxRuntime.jsx("span", {
44655
+ children: coordinates[1]
44656
+ })]
44657
+ })]
44658
+ });
44659
+ }
44660
+ let locationDisplay = '-';
44661
+ if (all.project?.name || all.project?.title) {
44662
+ locationDisplay = all.project?.name || all.project?.title;
44663
+ } else if (location?.name) {
44664
+ locationDisplay = location.name;
44665
+ } else if (value && typeof value === 'string') {
44666
+ locationDisplay = value;
44667
+ }
44668
+ return /*#__PURE__*/jsxRuntime.jsx(antd.Tooltip, {
44669
+ title: locationDisplay,
44670
+ children: locationDisplay
44671
+ });
44672
+ }
44673
+ }, {
44674
+ title: t("Implementer"),
44675
+ dataIndex: "technicalPartner",
44676
+ key: "implementer",
44677
+ ellipsis: true,
44678
+ show: true,
44679
+ render: (value, all) => {
44680
+ if (all.empty) {
44681
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
44682
+ className: "daf-default-cell"
44683
+ });
44684
+ }
44685
+ const implementerName = all.technicalPartner || all.implementer?.name || all.implementer?.title || value || '-';
44686
+ return /*#__PURE__*/jsxRuntime.jsx(antd.Tooltip, {
44687
+ title: implementerName,
44688
+ children: implementerName
44689
+ });
44690
+ }
44691
+ }, {
44692
+ title: t("Status"),
44693
+ dataIndex: "published",
44694
+ key: "published",
44695
+ ellipsis: false,
44696
+ show: true,
44697
+ render: (value, all) => {
44698
+ if (all.empty) {
44699
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
44700
+ className: "daf-default-cell"
44701
+ });
44702
+ }
44703
+ let statusDisplay = '-';
44704
+ let color = 'default';
44705
+ if (value !== undefined) {
44706
+ statusDisplay = value ? t('Published') : t('Unpublished');
44707
+ color = value ? 'green' : 'default';
44708
+ }
44709
+ return /*#__PURE__*/jsxRuntime.jsx(antd.Tag, {
44710
+ color: color,
44711
+ style: {
44712
+ width: 100
44713
+ },
44714
+ className: "text-center",
44715
+ children: t(statusDisplay)
44716
+ });
44717
+ }
44718
+ }, {
44719
+ title: t("Date"),
44720
+ dataIndex: "lastUpdated",
44721
+ key: "lastUpdated",
44722
+ ellipsis: true,
44723
+ active: true,
44724
+ pending: false,
44725
+ suspended: true,
44726
+ show: true,
44727
+ render: (val, all) => {
44728
+ if (all.empty) {
44729
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
44730
+ className: "daf-default-cell"
44731
+ });
44732
+ }
44733
+ const title = renderDateFormatted(all.updatedAt || all.date, "DD MMM YYYY");
44734
+ return /*#__PURE__*/jsxRuntime.jsx(antd.Tooltip, {
44735
+ title: title,
44736
+ children: title
44737
+ });
44738
+ }
44739
+ }, {
44740
+ title: "",
44741
+ dataIndex: "actions",
44742
+ key: "actions",
44743
+ width: 60,
44744
+ render: (_, val) => {
44745
+ if (val.empty) {
44746
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
44747
+ className: "daf-default-cell"
44748
+ });
44749
+ }
44750
+ const items = [{
44751
+ key: "viewSummary",
44752
+ label: t("Summary"),
44753
+ onClick: () => {
44754
+ const link = `/app/projects/${projectId}/restoration/summary/${val.datastakeId}`;
44755
+ navigate(getRedirectLink(link));
44756
+ }
44757
+ }, {
44758
+ key: "viewDetails",
44759
+ label: t("Details"),
44760
+ onClick: () => {
44761
+ const link = `/app/projects/${projectId}/restoration/view/general/${val.datastakeId}/identification`;
44762
+ navigate(getRedirectLink(link));
44763
+ }
44764
+ }];
44765
+ return /*#__PURE__*/jsxRuntime.jsx(MoreMenu, {
44766
+ items: items
44767
+ });
44768
+ },
44769
+ ellipsis: true
44770
+ }];
44771
+ }
44772
+ if (activeTab === PARTNERS_TAB$1) {
44773
+ return [{
44774
+ title: t('ID'),
44775
+ dataIndex: 'datastakeId',
44776
+ key: 'datastakeId',
44777
+ ellipsis: true,
44778
+ width: 160,
44779
+ render: (v, all) => {
44780
+ if (all.empty) {
44781
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
44782
+ className: "daf-default-cell"
44783
+ });
44784
+ }
44785
+ return /*#__PURE__*/jsxRuntime.jsx(antd.Tooltip, {
44786
+ title: v,
44787
+ children: v || '-'
44788
+ });
44789
+ }
44790
+ }, {
44791
+ title: t("Name"),
44792
+ dataIndex: "name",
44793
+ key: "name",
44794
+ ellipsis: true,
44795
+ render: (value, all) => {
44796
+ if (all.empty) {
44797
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
44798
+ className: "daf-default-cell"
44799
+ });
44800
+ }
44801
+ const name = value || all.nickName || '-';
44802
+ return /*#__PURE__*/jsxRuntime.jsx(antd.Tooltip, {
44803
+ title: name,
44804
+ children: name
44805
+ });
44806
+ }
44807
+ }, {
44808
+ title: t("Category"),
44809
+ dataIndex: "category",
44810
+ key: "category",
44811
+ ellipsis: true,
44812
+ render: (value, all) => {
44813
+ if (all.empty) {
44814
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
44815
+ className: "daf-default-cell"
44816
+ });
44817
+ }
44818
+ return /*#__PURE__*/jsxRuntime.jsx(antd.Tooltip, {
44819
+ title: value,
44820
+ children: value || '-'
44821
+ });
44822
+ }
44823
+ }, {
44824
+ title: t("Subcategory"),
44825
+ dataIndex: "subcategory",
44826
+ key: "subcategory",
44827
+ ellipsis: true,
44828
+ render: (value, all) => {
44829
+ if (all.empty) {
44830
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
44831
+ className: "daf-default-cell"
44832
+ });
44833
+ }
44834
+ return /*#__PURE__*/jsxRuntime.jsx(antd.Tooltip, {
44835
+ title: value,
44836
+ children: value || '-'
44837
+ });
44838
+ }
44839
+ }, {
44840
+ title: t("Country"),
44841
+ dataIndex: "country",
44842
+ key: "country",
44843
+ ellipsis: true,
44844
+ render: (value, all) => {
44845
+ if (all.empty) {
44846
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
44847
+ className: "daf-default-cell"
44848
+ });
44849
+ }
44850
+ const title = findOptions(value, selectOptions?.country || []) || '-';
44851
+ return /*#__PURE__*/jsxRuntime.jsx(antd.Tooltip, {
44852
+ title: title,
44853
+ children: title
44854
+ });
44855
+ }
44856
+ }, {
44857
+ title: t("Last Update"),
44858
+ dataIndex: "lastUpdated",
44859
+ key: "lastUpdated",
44860
+ ellipsis: true,
44861
+ render: (val, all) => {
44862
+ if (all.empty) {
44863
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
44864
+ className: "daf-default-cell"
44865
+ });
44866
+ }
44867
+ const title = renderDateFormatted(all.updatedAt || all.lastUpdated, "DD MMM YYYY");
44868
+ return /*#__PURE__*/jsxRuntime.jsx(antd.Tooltip, {
44869
+ title: title,
44870
+ children: title
44871
+ });
44872
+ }
44873
+ }, {
44874
+ title: t(''),
44875
+ dataIndex: 'actions',
44876
+ key: 'actions',
44877
+ width: 60,
44878
+ render: (_, record) => {
44879
+ const onClick = () => {
44880
+ record.id;
44881
+ const link = `/app/projects/${projectId}/implementation-partners/view/general/${record?.datastakeId}/identification`;
44882
+ navigate(getRedirectLink(link));
44883
+ };
44884
+ return !record.empty ? /*#__PURE__*/jsxRuntime.jsx("div", {
44885
+ className: "cursor-pointer",
44886
+ onClick: onClick,
44887
+ children: /*#__PURE__*/jsxRuntime.jsx(CustomIcon, {
44888
+ name: "Link",
44889
+ width: 16,
44890
+ height: 16,
44891
+ color: "#6C737F"
44892
+ })
44893
+ }) : null;
44894
+ },
44895
+ active: true,
44896
+ pending: true,
44897
+ suspended: true
44898
+ }];
44899
+ }
44900
+ if (activeTab === INCIDENTS_TAB$1) {
44901
+ return [{
44902
+ title: t('ID'),
44903
+ dataIndex: 'datastakeId',
44904
+ key: 'datastakeId',
44905
+ ellipsis: true,
44906
+ width: 160,
44907
+ render: (v, all) => {
44908
+ if (all.empty) {
44909
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
44910
+ className: "daf-default-cell"
44911
+ });
44912
+ }
44913
+ return /*#__PURE__*/jsxRuntime.jsx(antd.Tooltip, {
44914
+ title: v,
44915
+ children: v
44916
+ });
44917
+ }
44918
+ }, {
44919
+ title: t("Title"),
44920
+ dataIndex: "name",
44921
+ key: "name",
44922
+ ellipsis: true,
44923
+ render: value => /*#__PURE__*/jsxRuntime.jsx(antd.Tooltip, {
44924
+ title: value,
44925
+ children: value
44926
+ })
44927
+ }, {
44928
+ title: t("Date"),
44929
+ dataIndex: "date",
44930
+ key: "date",
44931
+ ellipsis: true,
44932
+ render: (val, all) => {
44933
+ if (all.empty) {
44934
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
44935
+ className: "daf-default-cell"
44936
+ });
44937
+ }
44938
+ const title = renderDateFormatted(all.date || all.updatedAt, "DD MMM YYYY");
44939
+ return /*#__PURE__*/jsxRuntime.jsx(antd.Tooltip, {
44940
+ title: title,
44941
+ children: title
44942
+ });
44943
+ }
44944
+ }, {
44945
+ title: t("Location"),
44946
+ dataIndex: "location",
44947
+ key: "location",
44948
+ ellipsis: true,
44949
+ render: (value, all) => {
44950
+ if (all.empty) {
44951
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
44952
+ className: "daf-default-cell"
44953
+ });
44954
+ }
44955
+ let locationDisplay = '-';
44956
+ if (all.location?.name) {
44957
+ locationDisplay = all.location.name;
44958
+ } else if (all.mineSite?.name) {
44959
+ locationDisplay = all.mineSite.name;
44960
+ } else if (all.location && typeof all.location === 'object' && all.location.latitude && all.location.longitude) {
44961
+ locationDisplay = `${all.location.latitude.toFixed(6)}, ${all.location.longitude.toFixed(6)}`;
44962
+ } else if (value && typeof value === 'string') {
44963
+ locationDisplay = value;
44964
+ }
44965
+ return /*#__PURE__*/jsxRuntime.jsx(antd.Tooltip, {
44966
+ title: locationDisplay,
44967
+ children: locationDisplay
44968
+ });
44969
+ }
44970
+ }, {
44971
+ title: t("Province"),
44972
+ dataIndex: "province",
44973
+ key: "province",
44974
+ ellipsis: true,
44975
+ render: (value, all) => {
44976
+ if (all.empty) {
44977
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
44978
+ className: "daf-default-cell"
44979
+ });
44980
+ }
44981
+ let province = '-';
44982
+ if (all.province) {
44983
+ province = all.province;
44984
+ } else if (all.location && typeof all.location === 'object' && all.location.administrativeLevel1) {
44985
+ province = all.location.administrativeLevel1;
44986
+ }
44987
+ return /*#__PURE__*/jsxRuntime.jsx(antd.Tooltip, {
44988
+ title: province,
44989
+ children: province
44990
+ });
44991
+ }
44992
+ }, {
44993
+ title: t("Territory"),
44994
+ dataIndex: "territory",
44995
+ key: "territory",
44996
+ ellipsis: true,
44997
+ render: (value, all) => {
44998
+ if (all.empty) {
44999
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
45000
+ className: "daf-default-cell"
45001
+ });
45002
+ }
45003
+ let territory = '-';
45004
+ if (all.territory) {
45005
+ territory = all.territory;
45006
+ } else if (all.location && typeof all.location === 'object' && all.location.administrativeLevel2) {
45007
+ territory = all.location.administrativeLevel2;
45008
+ }
45009
+ return /*#__PURE__*/jsxRuntime.jsx(antd.Tooltip, {
45010
+ title: territory,
45011
+ children: territory
45012
+ });
45013
+ }
45014
+ }, {
45015
+ title: t("Category"),
45016
+ dataIndex: "category",
45017
+ key: "category",
45018
+ ellipsis: true,
45019
+ render: (value, all) => {
45020
+ if (all.empty) {
45021
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
45022
+ className: "daf-default-cell"
45023
+ });
45024
+ }
45025
+ const category = all.eventCategory || all.category || value || '-';
45026
+ return /*#__PURE__*/jsxRuntime.jsx(antd.Tooltip, {
45027
+ title: category,
45028
+ children: category
45029
+ });
45030
+ }
45031
+ }, {
45032
+ title: t(''),
45033
+ dataIndex: 'actions',
45034
+ key: 'actions',
45035
+ width: 60,
45036
+ render: (_, record) => {
45037
+ const onClick = () => {
45038
+ const link = `/app/projects/${projectId}/implementation-partners/view/general/${record.datastakeId}/identification`;
45039
+ getRedirectLink(link);
45040
+ };
45041
+ return !record.empty ? /*#__PURE__*/jsxRuntime.jsx("div", {
45042
+ className: "cursor-pointer",
45043
+ onClick: onClick,
45044
+ children: /*#__PURE__*/jsxRuntime.jsx(CustomIcon, {
45045
+ name: "Link",
45046
+ width: 16,
45047
+ height: 16,
45048
+ color: "#6C737F"
45049
+ })
45050
+ }) : null;
45051
+ },
45052
+ active: true,
45053
+ pending: true,
45054
+ suspended: true
45055
+ }];
45056
+ }
45057
+ return [];
45058
+ };
45059
+
44241
45060
  const ACTIVITIES_TAB = 'activities';
44242
45061
  const PARTNERS_TAB = 'partners';
44243
45062
  const INCIDENTS_TAB = 'incidents';
45063
+ const DEFAULT_SEARCH_FIELDS = ["name", "datastakeId"];
45064
+ const URL_PATTERN = /\/summary\/[^/]+\/(.+)/;
45065
+
45066
+ // Configuration
45067
+ const TABS_CONFIG = [{
45068
+ label: "straatos::activities",
45069
+ value: ACTIVITIES_TAB
45070
+ }, {
45071
+ label: "straatos::partners",
45072
+ value: PARTNERS_TAB
45073
+ }, {
45074
+ label: "straatos::incidents",
45075
+ value: INCIDENTS_TAB,
45076
+ disabled: true
45077
+ }];
45078
+
45079
+ // Helper functions
45080
+ const getSearchFields = activeTab => DEFAULT_SEARCH_FIELDS;
45081
+ const buildSearchFilter = (search, fields) => search ? {
45082
+ search: {
45083
+ qs: search,
45084
+ fields
45085
+ }
45086
+ } : {};
45087
+ const extractTypeFromUrl = url => {
45088
+ const match = url.match(URL_PATTERN);
45089
+ if (!match) {
45090
+ throw new Error(`Invalid URL format: ${url}`);
45091
+ }
45092
+ return match[1];
45093
+ };
45094
+ const ensureArray = data => Array.isArray(data) ? data : [];
44244
45095
  const AssociatedInformation = ({
44245
- activityData,
45096
+ id,
45097
+ navigate,
45098
+ getSummaryDetail,
44246
45099
  loading = false,
45100
+ projectId,
45101
+ basepath = "planting-cycle",
45102
+ endpoint = "associated-information",
45103
+ tabsConfig = TABS_CONFIG,
45104
+ searchFieldsMap = getSearchFields,
45105
+ selectOptions,
44247
45106
  t = s => s
44248
45107
  }) => {
44249
45108
  const [activeTab, setActiveTab] = React.useState(ACTIVITIES_TAB);
45109
+ const [search, setSearch] = React.useState('');
45110
+ const searchFields = React.useMemo(() => searchFieldsMap(activeTab), [activeTab, searchFieldsMap]);
45111
+ const filters = React.useMemo(() => ({
45112
+ type: activeTab,
45113
+ ...buildSearchFilter(search, searchFields)
45114
+ }), [activeTab, search, searchFields]);
45115
+ const defaultConfig = React.useMemo(() => ({
45116
+ basepath,
45117
+ url: `/summary/${id}/${endpoint}`,
45118
+ filters,
45119
+ stop: !id
45120
+ }), [id, filters, basepath, endpoint]);
45121
+ const customGetData = React.useMemo(() => {
45122
+ if (!getSummaryDetail || !id) return undefined;
45123
+ return rest => {
45124
+ const {
45125
+ url,
45126
+ filters: restFilters
45127
+ } = rest;
45128
+ const type = extractTypeFromUrl(url);
45129
+ const params = {
45130
+ ...(restFilters || {}),
45131
+ type: restFilters?.type || activeTab
45132
+ };
45133
+ return getSummaryDetail(id, type, params);
45134
+ };
45135
+ }, [getSummaryDetail, id, activeTab]);
45136
+ const {
45137
+ loading: associatedInformationLoading,
45138
+ data: associatedInformationData,
45139
+ setData
45140
+ } = useWidgetFetch({
45141
+ config: defaultConfig,
45142
+ getData: customGetData
45143
+ });
45144
+
45145
+ // Reset data and search when tab changes
45146
+ React.useEffect(() => {
45147
+ setData([]);
45148
+ setSearch('');
45149
+ }, [activeTab, setData]);
45150
+ const handleSearch = React.useCallback((activeFilter, searchValue) => {
45151
+ setSearch(searchValue || '');
45152
+ }, []);
45153
+ const handleTabChange = React.useCallback(value => {
45154
+ setActiveTab(value);
45155
+ }, []);
45156
+ const columns = React.useMemo(() => getColumns({
45157
+ t,
45158
+ activeTab,
45159
+ view: activeTab,
45160
+ projectId,
45161
+ navigate,
45162
+ getRedirectLink,
45163
+ selectOptions
45164
+ }), [t, activeTab, projectId, navigate, selectOptions]);
45165
+ const tableDataSource = React.useMemo(() => ensureArray(associatedInformationData), [associatedInformationData]);
45166
+ const translatedTabs = React.useMemo(() => tabsConfig.map(tab => ({
45167
+ ...tab,
45168
+ label: t(tab.label)
45169
+ })), [tabsConfig, t]);
44250
45170
  return /*#__PURE__*/jsxRuntime.jsx("section", {
44251
- children: /*#__PURE__*/jsxRuntime.jsx(Widget, {
44252
- className: "v2-widget no-px h-w-btn-header no-p-body",
45171
+ children: /*#__PURE__*/jsxRuntime.jsxs(Widget, {
45172
+ className: "v2-widget no-px no-p-body h-w-btn-header with-border-header",
44253
45173
  title: t("Associated Information"),
44254
45174
  tabsConfig: {
44255
- tabs: [{
44256
- label: t("straatos::activities"),
44257
- value: ACTIVITIES_TAB
44258
- }, {
44259
- label: t("straatos::partners"),
44260
- value: PARTNERS_TAB
44261
- }, {
44262
- label: t("straatos::incidents"),
44263
- value: INCIDENTS_TAB
44264
- }],
45175
+ tabs: translatedTabs,
44265
45176
  value: activeTab,
44266
- onChange: value => {
44267
- setActiveTab(value);
44268
- // setData([]);
44269
- }
45177
+ onChange: handleTabChange
44270
45178
  },
44271
- children: /*#__PURE__*/jsxRuntime.jsx("div", {})
45179
+ children: [/*#__PURE__*/jsxRuntime.jsx("div", {
45180
+ className: "mt-6 ml-6 mr-6",
45181
+ children: /*#__PURE__*/jsxRuntime.jsx(SearchFilters, {
45182
+ t: t,
45183
+ showFilter: false,
45184
+ hasError: false,
45185
+ canClear: true,
45186
+ setHasError: () => {},
45187
+ onSearch: handleSearch,
45188
+ activeFilters: {
45189
+ search
45190
+ }
45191
+ })
45192
+ }), /*#__PURE__*/jsxRuntime.jsx("div", {
45193
+ className: "mb-6",
45194
+ children: /*#__PURE__*/jsxRuntime.jsx(StickyTable, {
45195
+ columns: columns,
45196
+ dataSource: tableDataSource,
45197
+ loading: associatedInformationLoading || loading
45198
+ })
45199
+ })]
44272
45200
  })
44273
45201
  });
44274
45202
  };
@@ -44365,12 +45293,15 @@ const PlantingCycleSummary = ({
44365
45293
  activityData,
44366
45294
  loading = false,
44367
45295
  id,
45296
+ projectId,
44368
45297
  t = () => {},
44369
- getSummaryDetail
45298
+ getSummaryDetail,
45299
+ navigate,
45300
+ selectOptions
44370
45301
  }) => {
44371
45302
  return /*#__PURE__*/jsxRuntime.jsxs(DashboardLayout, {
44372
45303
  header: /*#__PURE__*/jsxRuntime.jsx(DAFHeader, {
44373
- title: header?.title || '',
45304
+ title: header?.title + ' Summary' || '',
44374
45305
  supportText: header?.supportText || '',
44375
45306
  onDownload: header?.onDownload,
44376
45307
  downloadDisabled: header?.downloadDisabled,
@@ -44406,9 +45337,12 @@ const PlantingCycleSummary = ({
44406
45337
  t: t
44407
45338
  }), /*#__PURE__*/jsxRuntime.jsx(AssociatedInformation, {
44408
45339
  id: id,
45340
+ projectId: projectId,
44409
45341
  getSummaryDetail: getSummaryDetail,
44410
45342
  loading: loading,
44411
- t: t
45343
+ t: t,
45344
+ navigate: navigate,
45345
+ selectOptions: selectOptions
44412
45346
  })]
44413
45347
  });
44414
45348
  };