datastake-daf 0.6.766 → 0.6.767

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.
@@ -7934,7 +7934,12 @@ const defaultMapConfig = {
7934
7934
  maxZoom: 18,
7935
7935
  preferCanvas: true,
7936
7936
  zoomControl: false,
7937
- maxBounds: maxBounds
7937
+ maxBounds: maxBounds,
7938
+ zoomSnap: 0.5,
7939
+ zoomDelta: 0.5,
7940
+ wheelPxPerZoomLevel: 150,
7941
+ zoomAnimation: true,
7942
+ zoomAnimationThreshold: 10
7938
7943
  };
7939
7944
  const filterValidGPS = data => {
7940
7945
  const latCheck = lat => lat > -90 || lat < 90;
@@ -8809,7 +8814,7 @@ function StakeholderIcon$1({
8809
8814
  onClickLink(data);
8810
8815
  }
8811
8816
  }),
8812
- getPopupContainer: () => document.getElementById(data.datastakeId),
8817
+ getPopupContainer: () => document.getElementById("map"),
8813
8818
  children: /*#__PURE__*/jsxRuntime.jsx(StakeholderMarker, {
8814
8819
  className: `${data.type} ${isSelected ? "selected" : selectedMarkersId.length > 0 ? "unselected" : ""}
8815
8820
  ${isSmall ? "small" : isMedium ? "medium" : "large"}
@@ -9012,8 +9017,7 @@ function LocationIcon({
9012
9017
  // isHovering
9013
9018
  // }
9014
9019
  ,
9015
-
9016
- getPopupContainer: () => document.getElementById(i),
9020
+ getPopupContainer: () => document.getElementById("map"),
9017
9021
  children: /*#__PURE__*/jsxRuntime.jsxs("div", {
9018
9022
  style: {
9019
9023
  position: "relative",
@@ -9114,6 +9118,7 @@ function StakeholderIcon({
9114
9118
  link,
9115
9119
  onClickLink: () => onClickLink(marker)
9116
9120
  }),
9121
+ getPopupContainer: () => document.getElementById("map"),
9117
9122
  children: /*#__PURE__*/jsxRuntime.jsx("div", {
9118
9123
  style: {
9119
9124
  position: "absolute",
@@ -9155,6 +9160,7 @@ function StakeholderIcon({
9155
9160
  link,
9156
9161
  onClickLink: () => onClickLink(marker)
9157
9162
  }),
9163
+ getPopupContainer: () => document.getElementById("map"),
9158
9164
  children: /*#__PURE__*/jsxRuntime.jsx("div", {
9159
9165
  style: {
9160
9166
  position: "absolute",
@@ -9190,6 +9196,7 @@ function StakeholderIcon({
9190
9196
  link,
9191
9197
  onClickLink: () => onClickLink(marker)
9192
9198
  }),
9199
+ getPopupContainer: () => document.getElementById("map"),
9193
9200
  children: /*#__PURE__*/jsxRuntime.jsx(Style$c
9194
9201
  // onClick={toggleOpen}
9195
9202
  , {
@@ -9913,7 +9920,13 @@ const useMap = ({
9913
9920
  if (mapRef) {
9914
9921
  L__namespace.control.scale().addTo(mapRef);
9915
9922
  L__namespace.tileLayer(TILE_LAYER_URL, {
9916
- access_token: MAP_TOKEN
9923
+ access_token: MAP_TOKEN,
9924
+ keepBuffer: 4,
9925
+ updateWhenZooming: false,
9926
+ updateInterval: 200,
9927
+ maxNativeZoom: 18,
9928
+ tileSize: 256,
9929
+ fadeAnimation: true
9917
9930
  }).addTo(mapRef);
9918
9931
  }
9919
9932
  }, [mapRef]);
@@ -9977,6 +9990,23 @@ const useMap = ({
9977
9990
  mapRef.setView([marker.coordinates[0], marker.coordinates[1]], getZoom(mapRef));
9978
9991
  }
9979
9992
  }, [activeMarker, mapRef]);
9993
+ React.useEffect(() => {
9994
+ if (mapRef && type === "chain") {
9995
+ const handleMapClick = e => {
9996
+ const clickedElement = e.originalEvent.target;
9997
+ const isMarkerClick = clickedElement.closest('.marker-chain') || clickedElement.closest('.leaflet-marker-icon');
9998
+ if (!isMarkerClick && selectedMarkersId.length > 0) {
9999
+ setSelectedMarkersId([]);
10000
+ openPopupIdRef.current = null;
10001
+ setMarkerWithPopup(null);
10002
+ }
10003
+ };
10004
+ mapRef.on('click', handleMapClick);
10005
+ return () => {
10006
+ mapRef.off('click', handleMapClick);
10007
+ };
10008
+ }
10009
+ }, [mapRef, type, selectedMarkersId]);
9980
10010
  return {
9981
10011
  container,
9982
10012
  activeMarker,
@@ -10469,6 +10499,7 @@ function SupplyChainMap({
10469
10499
  } = useWidgetFetch({
10470
10500
  config: defaultConfig
10471
10501
  });
10502
+ console.log('data', data);
10472
10503
  return /*#__PURE__*/jsxRuntime.jsx(Widget, {
10473
10504
  title: t("Supply Chain Map"),
10474
10505
  loading: loading,
@@ -10478,11 +10509,11 @@ function SupplyChainMap({
10478
10509
  data: Array.isArray(data) && data.length > 0 ? data : [],
10479
10510
  renderTooltip: data => {
10480
10511
  const productsText = data?.products?.[0];
10481
- const minerals = options?.minerals || [];
10512
+ const minerals = options?.mineralOptions || [];
10482
10513
  const participants = data?.stakeholders?.map(stakeholder => stakeholder.name);
10483
10514
  return [{
10484
10515
  label: t("Products"),
10485
- value: minerals.find(mineral => mineral.value === productsText)?.label || productsText || "-",
10516
+ value: findOptions(productsText, minerals) || productsText || "-",
10486
10517
  ...(productsText && {
10487
10518
  tag: getTagColor(productsText)
10488
10519
  })
@@ -11639,17 +11670,23 @@ const BaseGraph = /*#__PURE__*/React.forwardRef(function BaseGraph({
11639
11670
  }
11640
11671
  return result;
11641
11672
  }, [nodes.length, mandatoryNodesToFit?.length, mandatoryNodesToFit]);
11673
+
11674
+ // In BaseGraph.jsx, replace the useEffect with:
11642
11675
  React.useEffect(() => {
11643
11676
  if (nodesToFit.length === 0) return;
11644
- requestAnimationFrame(() => {
11677
+
11678
+ // Use setTimeout instead of requestAnimationFrame to ensure nodes are rendered
11679
+ const timer = setTimeout(() => {
11645
11680
  fitView({
11646
11681
  padding: 0.4,
11647
11682
  nodes: [...nodesToFit],
11648
11683
  // duration: withDuration ? 300 : undefined,
11649
11684
  maxZoom: 0.9
11650
11685
  });
11651
- });
11652
- }, [JSON.stringify(nodesToFit), withDuration]);
11686
+ }, 100); // Small delay to ensure nodes are rendered
11687
+
11688
+ return () => clearTimeout(timer);
11689
+ }, [nodesToFit.length, nodesToFit.map(n => `${n.id}-${n.width}-${n.height}`).join(','), withDuration]);
11653
11690
  return /*#__PURE__*/jsxRuntime.jsx(ComponentWithFocus, {
11654
11691
  children: /*#__PURE__*/jsxRuntime.jsxs("div", {
11655
11692
  style: {
@@ -11678,8 +11715,9 @@ const BaseGraph = /*#__PURE__*/React.forwardRef(function BaseGraph({
11678
11715
  fitView: true // zoom out on default
11679
11716
  ,
11680
11717
  fitViewOptions: {
11681
- padding: 0.2 //zoom out on default
11682
- // ...(zoomOutTransition ? { duration: withDuration ? 300 : undefined } : {}),
11718
+ padding: 0.2,
11719
+ //zoom out on default
11720
+ duration: withDuration ? 300 : undefined
11683
11721
  },
11684
11722
  ...props,
11685
11723
  children: !isPdf && /*#__PURE__*/jsxRuntime.jsxs(react.Controls, {
@@ -32912,10 +32950,6 @@ const WorkersTable = ({
32912
32950
  };
32913
32951
 
32914
32952
  const getEventCategoryBySubject = (eventCategoryObject, subject, isSingular = false) => {
32915
- console.log({
32916
- eventCategoryObject,
32917
- subject
32918
- });
32919
32953
  if (!eventCategoryObject || typeof eventCategoryObject !== 'object') {
32920
32954
  return null;
32921
32955
  }
@@ -34363,7 +34397,7 @@ const getColumns$2 = ({
34363
34397
  className: "daf-default-cell"
34364
34398
  });
34365
34399
  }
34366
- const operators = all?.operator?.map(operator => operator?.clientLocation?.name);
34400
+ const operators = all?.operator?.map(operator => operator?.locationClient?.name)?.filter(Boolean);
34367
34401
  return operators && operators.length > 0 ? /*#__PURE__*/jsxRuntime.jsx(MoreOptions, {
34368
34402
  data: operators || [],
34369
34403
  toolTipPlacement: operators?.length < 2 ? "topLeft" : "top"
@@ -37557,7 +37591,8 @@ function TradeRelationship({
37557
37591
  filtersConfig,
37558
37592
  onFilterChange = () => {},
37559
37593
  renderTooltipItems = () => [],
37560
- getTotal = () => 0
37594
+ getTotal = () => 0,
37595
+ onRenderComplete = () => {}
37561
37596
  }) {
37562
37597
  const reactFlowWrapper = React.useRef(null);
37563
37598
  const [nodes, setNodes] = react.useNodesState([]);
@@ -37570,6 +37605,23 @@ function TradeRelationship({
37570
37605
  const [activeNode, setActiveNode] = React.useState(null);
37571
37606
  // const [initCenter, setInitCenter] = useState(true);
37572
37607
  const [associatedNodes, setAssociatedNodes] = React.useState(null);
37608
+ const isFullyRenderedRef = React.useRef(false);
37609
+ const [isFullyRendered, setIsFullyRendered] = React.useState(false);
37610
+ React.useEffect(() => {
37611
+ isFullyRenderedRef.current = false;
37612
+ setIsFullyRendered(false);
37613
+ setActiveNode(null);
37614
+ }, [data]);
37615
+ React.useEffect(() => {
37616
+ if (nodes.length > 0 && edges.length > 0 && !isFullyRenderedRef.current) {
37617
+ const timeoutId = setTimeout(() => {
37618
+ isFullyRenderedRef.current = true;
37619
+ setIsFullyRendered(true);
37620
+ onRenderComplete(true);
37621
+ }, 200);
37622
+ return () => clearTimeout(timeoutId);
37623
+ }
37624
+ }, [nodes.length, edges.length, associatedNodes]);
37573
37625
  React.useEffect(() => {
37574
37626
  setActiveNode(null);
37575
37627
  }, [data]);
@@ -37609,7 +37661,7 @@ function TradeRelationship({
37609
37661
  return;
37610
37662
  }
37611
37663
  const _nodes = [{
37612
- id: data.id,
37664
+ id: data.id?.toString(),
37613
37665
  type: "expandedNode",
37614
37666
  position: {
37615
37667
  x: xInit,
@@ -37621,10 +37673,10 @@ function TradeRelationship({
37621
37673
  if (isBilateral) {
37622
37674
  (data.sources || []).forEach(source => {
37623
37675
  const hasPrev = false;
37624
- _edges.push({
37676
+ const edge = {
37625
37677
  id: `e-${data.id}-${source}`,
37626
- source: source,
37627
- type: "default",
37678
+ source: source?.toString(),
37679
+ type: "defaultEdge",
37628
37680
  target: data?.id?.toString(),
37629
37681
  sourceHandle: "left",
37630
37682
  targetHandle: "right",
@@ -37638,7 +37690,8 @@ function TradeRelationship({
37638
37690
  tooltipTitle,
37639
37691
  moreLeft: false
37640
37692
  }
37641
- });
37693
+ };
37694
+ _edges.push(edge);
37642
37695
  });
37643
37696
  }
37644
37697
  const mapChildren = ({
@@ -37679,7 +37732,7 @@ function TradeRelationship({
37679
37732
  (ch.sources || []).forEach(source => {
37680
37733
  const hasPrev = _prevNodes.find(p => p.id === source);
37681
37734
  const isCustom = !hasPrev ? true : prevChildren.includes(source) ? false : true;
37682
- _edges.push({
37735
+ const edge = {
37683
37736
  id: `e-${ch.id}-${source}`,
37684
37737
  source: hasPrev ? source : ch?.id?.toString(),
37685
37738
  type: isCustom ? "verticalPath" : "defaultEdge",
@@ -37697,7 +37750,8 @@ function TradeRelationship({
37697
37750
  tooltipTitle,
37698
37751
  moreLeft: prevChildren.length > children.length
37699
37752
  }
37700
- });
37753
+ };
37754
+ _edges.push(edge);
37701
37755
  if (isCustom) {
37702
37756
  customIndex += 1;
37703
37757
  }
@@ -37736,11 +37790,24 @@ function TradeRelationship({
37736
37790
  prevChildren: !isBilateral ? [data.id] : [],
37737
37791
  maxHeight: 0
37738
37792
  });
37793
+
37794
+ // Check for potential ID mismatches
37795
+ const nodeIds = _nodes.map(n => n.id);
37796
+ _edges.filter(e => {
37797
+ const sourceExists = nodeIds.includes(e.source);
37798
+ const targetExists = nodeIds.includes(e.target);
37799
+ return !sourceExists || !targetExists;
37800
+ });
37801
+
37802
+ // Set nodes first
37739
37803
  setNodes(_nodes);
37740
- setEdges(_edges);
37741
- }, [data, activeNode]);
37804
+ const timeoutId = setTimeout(() => {
37805
+ setEdges(_edges);
37806
+ }, 100); // 100ms is imperceptible to users but ensures React Flow is ready
37742
37807
 
37743
- // Used to find associated nodes, when a node is selected
37808
+ // Cleanup to prevent memory leaks if component unmounts quickly
37809
+ return () => clearTimeout(timeoutId);
37810
+ }, [data, activeNode]);
37744
37811
  React.useEffect(() => {
37745
37812
  if (activeNode) {
37746
37813
  let _associatedNodesRight = [activeNode];
@@ -37806,7 +37873,7 @@ function TradeRelationship({
37806
37873
  });
37807
37874
  },
37808
37875
  ref: reactFlowWrapper
37809
- });
37876
+ }, JSON.stringify(nodes) + JSON.stringify(edges));
37810
37877
  }
37811
37878
  var TradeRelationship$1 = withProvider(TradeRelationship);
37812
37879
 
@@ -37821,6 +37888,7 @@ const TradeRelationships = ({
37821
37888
  APP
37822
37889
  }) => {
37823
37890
  const [filters, setFilters] = React.useState({});
37891
+ const [isFullyRendered, setIsFullyRendered] = React.useState(true);
37824
37892
  const onFilterChange = filters => {
37825
37893
  setFilters(p => ({
37826
37894
  ...p,
@@ -37869,7 +37937,7 @@ const TradeRelationships = ({
37869
37937
  return /*#__PURE__*/jsxRuntime.jsx(Widget, {
37870
37938
  title: t("Trade Relationships"),
37871
37939
  className: "flex flex-1 with-border-header no-p-body",
37872
- loading: loading,
37940
+ loading: loading || isFullyRendered,
37873
37941
  children: /*#__PURE__*/jsxRuntime.jsx("div", {
37874
37942
  className: "flex flex-1 flex-column justify-content-center",
37875
37943
  children: /*#__PURE__*/jsxRuntime.jsx("div", {
@@ -37892,7 +37960,10 @@ const TradeRelationships = ({
37892
37960
  minZoom: 0.4,
37893
37961
  tooltipTitle: "Trade",
37894
37962
  filtersConfig: filterConfig,
37895
- onFilterChange: onFilterChange
37963
+ onFilterChange: onFilterChange,
37964
+ onRenderComplete: data => {
37965
+ setIsFullyRendered(!data);
37966
+ }
37896
37967
  })
37897
37968
  })
37898
37969
  })
@@ -7085,7 +7085,12 @@ const defaultMapConfig = {
7085
7085
  maxZoom: 18,
7086
7086
  preferCanvas: true,
7087
7087
  zoomControl: false,
7088
- maxBounds: maxBounds
7088
+ maxBounds: maxBounds,
7089
+ zoomSnap: 0.5,
7090
+ zoomDelta: 0.5,
7091
+ wheelPxPerZoomLevel: 150,
7092
+ zoomAnimation: true,
7093
+ zoomAnimationThreshold: 10
7089
7094
  };
7090
7095
 
7091
7096
  styled__default["default"].div`
@@ -7724,6 +7729,11 @@ function getRedirectPath(user, fallback = '', app, isDatastake) {
7724
7729
  }
7725
7730
 
7726
7731
  const en = {
7732
+ "accounts": "Accounts",
7733
+ "users": "Users",
7734
+ "subjects": "Subjects",
7735
+ "active-users": "Active Users",
7736
+ "total-data-points": "Total Data Points",
7727
7737
  "Identified Customers": "Identified Customers",
7728
7738
  "Identified Suppliers": "Identified Suppliers",
7729
7739
  "Associated Mine Sites": "Associated Mine Sites",
@@ -9037,6 +9047,11 @@ const en = {
9037
9047
  };
9038
9048
 
9039
9049
  const fr = {
9050
+ "accounts": "Comptes",
9051
+ "users": "Utilisateurs",
9052
+ "subjects": "Sujets",
9053
+ "active-users": "Utilisateurs Actifs",
9054
+ "total-data-points": "Total Points de Données",
9040
9055
  "Identified Customers": "Clients identifiés",
9041
9056
  "Identified Suppliers": "Fournisseurs identifiés",
9042
9057
  "Associated Mine Sites": "Sites miniers associés",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "datastake-daf",
3
- "version": "0.6.766",
3
+ "version": "0.6.767",
4
4
  "dependencies": {
5
5
  "@ant-design/icons": "^5.2.5",
6
6
  "@antv/g2": "^5.1.1",
@@ -15,6 +15,9 @@
15
15
  "buffer": "^6.0.3",
16
16
  "countries-list": "^2.6.1",
17
17
  "country-city-location": "^1.0.13",
18
+ "d3-selection": "^3.0.0",
19
+ "d3-transition": "^3.0.1",
20
+ "d3-zoom": "^3.0.0",
18
21
  "dayjs": "^1.11.12",
19
22
  "deepmerge": "^4.3.1",
20
23
  "docx": "^9.5.1",
@@ -234,7 +234,7 @@ export default function StakeholderIcon({
234
234
  onClickLink(data);
235
235
  },
236
236
  })}
237
- getPopupContainer={() => document.getElementById(data.datastakeId)}
237
+ getPopupContainer={() => document.getElementById("map")}
238
238
  >
239
239
  <StakeholderMarker
240
240
  className={`${data.type} ${
@@ -213,8 +213,7 @@ export default function LocationIcon({
213
213
  // (!openPopupIdRef.current || openPopupIdRef.current === data.datastakeId) &&
214
214
  // isHovering
215
215
  // }
216
-
217
- getPopupContainer={() => document.getElementById(i)}
216
+ getPopupContainer={() => document.getElementById("map")}
218
217
  >
219
218
  <div style={{ position: "relative", display: "inline-block" }}>
220
219
  {(isSelected || selectedMarkersId.length === 0) && (
@@ -42,6 +42,7 @@ export default function StakeholderIcon({
42
42
  link,
43
43
  onClickLink: () => onClickLink(marker),
44
44
  })}
45
+ getPopupContainer={() => document.getElementById("map")}
45
46
  >
46
47
  <div
47
48
  key="single-marker"
@@ -91,6 +92,7 @@ export default function StakeholderIcon({
91
92
  link,
92
93
  onClickLink: () => onClickLink(marker),
93
94
  })}
95
+ getPopupContainer={() => document.getElementById("map")}
94
96
  >
95
97
  <div
96
98
  style={{
@@ -132,6 +134,7 @@ export default function StakeholderIcon({
132
134
  link,
133
135
  onClickLink: () => onClickLink(marker),
134
136
  })}
137
+ getPopupContainer={() => document.getElementById("map")}
135
138
  >
136
139
  <Style
137
140
  // onClick={toggleOpen}
@@ -283,7 +283,15 @@ export const useMap = ({
283
283
  useEffect(() => {
284
284
  if (mapRef) {
285
285
  L.control.scale().addTo(mapRef);
286
- L.tileLayer(TILE_LAYER_URL, { access_token: MAP_TOKEN }).addTo(mapRef);
286
+ L.tileLayer(TILE_LAYER_URL, {
287
+ access_token: MAP_TOKEN,
288
+ keepBuffer: 4,
289
+ updateWhenZooming: false,
290
+ updateInterval: 200,
291
+ maxNativeZoom: 18,
292
+ tileSize: 256,
293
+ fadeAnimation: true,
294
+ }).addTo(mapRef);
287
295
  }
288
296
  }, [mapRef]);
289
297
 
@@ -362,6 +370,28 @@ export const useMap = ({
362
370
  }
363
371
  }, [activeMarker, mapRef]);
364
372
 
373
+ useEffect(() => {
374
+ if (mapRef && type === "chain") {
375
+ const handleMapClick = (e) => {
376
+ const clickedElement = e.originalEvent.target;
377
+ const isMarkerClick = clickedElement.closest('.marker-chain') ||
378
+ clickedElement.closest('.leaflet-marker-icon');
379
+
380
+ if (!isMarkerClick && selectedMarkersId.length > 0) {
381
+ setSelectedMarkersId([]);
382
+ openPopupIdRef.current = null;
383
+ setMarkerWithPopup(null);
384
+ }
385
+ };
386
+
387
+ mapRef.on('click', handleMapClick);
388
+
389
+ return () => {
390
+ mapRef.off('click', handleMapClick);
391
+ };
392
+ }
393
+ }, [mapRef, type, selectedMarkersId]);
394
+
365
395
  return {
366
396
  container,
367
397
  activeMarker,
@@ -21,6 +21,7 @@ function TradeRelationship({
21
21
  onFilterChange = () => {},
22
22
  renderTooltipItems = () => [],
23
23
  getTotal = () => 0,
24
+ onRenderComplete = () => {},
24
25
  }) {
25
26
  const reactFlowWrapper = useRef(null);
26
27
  const [nodes, setNodes] = useNodesState([]);
@@ -30,6 +31,27 @@ function TradeRelationship({
30
31
  // const [initCenter, setInitCenter] = useState(true);
31
32
  const [associatedNodes, setAssociatedNodes] = useState(null);
32
33
 
34
+ const isFullyRenderedRef = useRef(false);
35
+ const [isFullyRendered, setIsFullyRendered] = useState(false);
36
+
37
+ useEffect(() => {
38
+ isFullyRenderedRef.current = false;
39
+ setIsFullyRendered(false);
40
+ setActiveNode(null);
41
+ }, [data]);
42
+
43
+ useEffect(() => {
44
+ if (nodes.length > 0 && edges.length > 0 && !isFullyRenderedRef.current) {
45
+ const timeoutId = setTimeout(() => {
46
+ isFullyRenderedRef.current = true;
47
+ setIsFullyRendered(true);
48
+ onRenderComplete(true);
49
+ }, 200);
50
+
51
+ return () => clearTimeout(timeoutId);
52
+ }
53
+ }, [nodes.length, edges.length, associatedNodes]);
54
+
33
55
  useEffect(() => {
34
56
  setActiveNode(null);
35
57
  }, [data]);
@@ -58,7 +80,7 @@ function TradeRelationship({
58
80
  [edges, activeNode, associatedNodes]
59
81
  );
60
82
 
61
- useEffect(() => {
83
+ useEffect(() => {
62
84
  let yInit = 0;
63
85
  let xInit = 0;
64
86
  const isBilateral = data?.sources?.length >= 1;
@@ -71,10 +93,9 @@ function TradeRelationship({
71
93
  setEdges([]);
72
94
  return;
73
95
  }
74
-
75
96
  const _nodes = [
76
97
  {
77
- id: data.id,
98
+ id: data.id?.toString(),
78
99
  type: "expandedNode",
79
100
  position: {
80
101
  x: xInit,
@@ -89,10 +110,10 @@ function TradeRelationship({
89
110
  if (isBilateral) {
90
111
  (data.sources || []).forEach((source) => {
91
112
  const hasPrev = false;
92
- _edges.push({
113
+ const edge = {
93
114
  id: `e-${data.id}-${source}`,
94
- source: source,
95
- type: "default",
115
+ source: source?.toString(),
116
+ type: "defaultEdge",
96
117
  target: data?.id?.toString(),
97
118
  sourceHandle: "left",
98
119
  targetHandle: "right",
@@ -106,7 +127,8 @@ function TradeRelationship({
106
127
  tooltipTitle,
107
128
  moreLeft: false,
108
129
  },
109
- });
130
+ };
131
+ _edges.push(edge);
110
132
  });
111
133
  }
112
134
 
@@ -164,7 +186,7 @@ function TradeRelationship({
164
186
  ? false
165
187
  : true;
166
188
 
167
- _edges.push({
189
+ const edge = {
168
190
  id: `e-${ch.id}-${source}`,
169
191
  source: hasPrev ? source : ch?.id?.toString(),
170
192
  type: isCustom ? "verticalPath" : "defaultEdge",
@@ -182,7 +204,8 @@ function TradeRelationship({
182
204
  tooltipTitle,
183
205
  moreLeft: prevChildren.length > children.length,
184
206
  },
185
- });
207
+ };
208
+ _edges.push(edge);
186
209
 
187
210
  if (isCustom) {
188
211
  customIndex += 1;
@@ -230,11 +253,25 @@ function TradeRelationship({
230
253
  maxHeight: 0,
231
254
  });
232
255
 
256
+ // Check for potential ID mismatches
257
+ const nodeIds = _nodes.map(n => n.id);
258
+ const edgeIssues = _edges.filter(e => {
259
+ const sourceExists = nodeIds.includes(e.source);
260
+ const targetExists = nodeIds.includes(e.target);
261
+ return !sourceExists || !targetExists;
262
+ });
263
+
264
+ // Set nodes first
233
265
  setNodes(_nodes);
234
- setEdges(_edges);
266
+
267
+ const timeoutId = setTimeout(() => {
268
+ setEdges(_edges);
269
+ }, 100); // 100ms is imperceptible to users but ensures React Flow is ready
270
+
271
+ // Cleanup to prevent memory leaks if component unmounts quickly
272
+ return () => clearTimeout(timeoutId);
235
273
  }, [data, activeNode]);
236
274
 
237
- // Used to find associated nodes, when a node is selected
238
275
  useEffect(() => {
239
276
  if (activeNode) {
240
277
  let _associatedNodesRight = [activeNode];
@@ -293,6 +330,7 @@ function TradeRelationship({
293
330
 
294
331
  return (
295
332
  <BaseGraph
333
+ key={JSON.stringify(nodes) + JSON.stringify(edges)}
296
334
  nodes={mappedNodes}
297
335
  edges={mappedEdges}
298
336
  maxZoom={maxZoom}
@@ -49,18 +49,22 @@ const BaseGraph = forwardRef(function BaseGraph(
49
49
  return result;
50
50
  }, [nodes.length, mandatoryNodesToFit?.length, mandatoryNodesToFit]);
51
51
 
52
+ // In BaseGraph.jsx, replace the useEffect with:
52
53
  useEffect(() => {
53
54
  if (nodesToFit.length === 0) return;
54
55
 
55
- requestAnimationFrame(() => {
56
+ // Use setTimeout instead of requestAnimationFrame to ensure nodes are rendered
57
+ const timer = setTimeout(() => {
56
58
  fitView({
57
59
  padding: 0.4,
58
60
  nodes: [...nodesToFit],
59
61
  // duration: withDuration ? 300 : undefined,
60
62
  maxZoom: 0.9,
61
63
  });
62
- });
63
- }, [JSON.stringify(nodesToFit), withDuration]);
64
+ }, 100); // Small delay to ensure nodes are rendered
65
+
66
+ return () => clearTimeout(timer);
67
+ }, [nodesToFit.length, nodesToFit.map(n => `${n.id}-${n.width}-${n.height}`).join(','), withDuration]);
64
68
 
65
69
  return (
66
70
  <ComponentWithFocus>
@@ -86,7 +90,7 @@ const BaseGraph = forwardRef(function BaseGraph(
86
90
  fitView={true} // zoom out on default
87
91
  fitViewOptions={{
88
92
  padding: 0.2, //zoom out on default
89
- // ...(zoomOutTransition ? { duration: withDuration ? 300 : undefined } : {}),
93
+ duration: withDuration ? 300 : undefined,
90
94
  }}
91
95
  {...props}
92
96
  >