datastake-daf 0.6.842 → 0.6.843

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 (28) hide show
  1. package/build/favicon.ico +0 -0
  2. package/build/logo192.png +0 -0
  3. package/build/logo512.png +0 -0
  4. package/build/manifest.json +25 -0
  5. package/build/robots.txt +3 -0
  6. package/dist/components/index.js +1884 -1970
  7. package/dist/hooks/index.js +4 -6
  8. package/dist/layouts/index.js +6 -0
  9. package/dist/pages/index.js +166 -113
  10. package/package.json +1 -1
  11. package/src/@daf/core/components/DynamicForm/hook.js +2 -2
  12. package/src/@daf/core/components/DynamicForm/index.jsx +1 -1
  13. package/src/@daf/core/components/Filters/selectFilters/index.jsx +7 -1
  14. package/src/@daf/core/components/Graphs/TradeRelationship/index.jsx +1 -27
  15. package/src/@daf/core/components/Graphs/components/BaseGraph.jsx +22 -18
  16. package/src/@daf/core/components/Select/MultiSelect/style.js +1 -1
  17. package/src/@daf/core/components/ViewForm/components/DataLink/flat.js +7 -3
  18. package/src/@daf/core/components/ViewForm/components/DataLink/index.js +6 -2
  19. package/src/@daf/core/components/ViewForm/components/input.js +7 -7
  20. package/src/@daf/hooks/useWidgetFetch.js +26 -35
  21. package/src/@daf/pages/Dashboards/ConflictManagement/components/RisksWidget/components/IncidentsTime/index.js +1 -0
  22. package/src/@daf/pages/Summary/Operator/components/KeyInformation/config.js +0 -1
  23. package/src/@daf/pages/Summary/Operator/components/TradeRelationships/helper.js +7 -5
  24. package/src/@daf/pages/Summary/Operator/components/TradeRelationships/hook.js +29 -14
  25. package/src/@daf/pages/Summary/Operator/components/TradeRelationships/index.js +69 -20
  26. package/src/@daf/pages/Summary/Operator/index.jsx +1 -0
  27. package/src/@daf/pages/View/index.jsx +1 -1
  28. package/src/helpers/Forms.js +7 -5
@@ -21,7 +21,6 @@ function TradeRelationship({
21
21
  onFilterChange = () => {},
22
22
  renderTooltipItems = () => [],
23
23
  getTotal = () => 0,
24
- onRenderComplete = () => {},
25
24
  }) {
26
25
  const reactFlowWrapper = useRef(null);
27
26
  const [nodes, setNodes] = useNodesState([]);
@@ -31,31 +30,6 @@ function TradeRelationship({
31
30
  // const [initCenter, setInitCenter] = useState(true);
32
31
  const [associatedNodes, setAssociatedNodes] = useState(null);
33
32
 
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
- if(nodes.length === 0 || edges.length === 0) {
54
- setIsFullyRendered(true);
55
- onRenderComplete(true);
56
- }
57
- }, [nodes.length, edges.length, associatedNodes]);
58
-
59
33
  useEffect(() => {
60
34
  setActiveNode(null);
61
35
  }, [data]);
@@ -274,7 +248,7 @@ function TradeRelationship({
274
248
 
275
249
  // Cleanup to prevent memory leaks if component unmounts quickly
276
250
  return () => clearTimeout(timeoutId);
277
- }, [data, activeNode]);
251
+ }, [data]);
278
252
 
279
253
  useEffect(() => {
280
254
  if (activeNode) {
@@ -2,7 +2,7 @@ import { ReactFlow, Controls, ControlButton, useReactFlow } from "@xyflow/react"
2
2
  import { UpOutlined, DownOutlined, AimOutlined } from "@ant-design/icons";
3
3
 
4
4
  import ComponentWithFocus from "../../Dashboard/ComponentWithFocus/index.jsx";
5
- import { forwardRef } from "react";
5
+ import { forwardRef, useRef } from "react";
6
6
  import { PrimaryNode, IconNode, NameNode, ExpandedNode } from "./Nodes/index.jsx";
7
7
  import { ToolTipEdge, VerticalPathEdge, DefaultEdge } from "./Edges/index.jsx";
8
8
  import Filters from "../../Filters/FloatingFilters/index.js";
@@ -38,6 +38,7 @@ const BaseGraph = forwardRef(function BaseGraph(
38
38
  ref,
39
39
  ) {
40
40
  const { setViewport, getViewport, fitView } = useReactFlow();
41
+ const hasInitialFitRef = useRef(false);
41
42
 
42
43
  const nodesToFit = useMemo(() => {
43
44
  let result;
@@ -49,31 +50,34 @@ const BaseGraph = forwardRef(function BaseGraph(
49
50
  return result;
50
51
  }, [nodes.length, mandatoryNodesToFit?.length, mandatoryNodesToFit]);
51
52
 
52
- // In BaseGraph.jsx, replace the useEffect with:
53
+ // Only auto-fit on initial render or when node count changes, not on property changes
53
54
  useEffect(() => {
54
55
  if (nodesToFit.length === 0) return;
55
56
 
56
- // Use setTimeout instead of requestAnimationFrame to ensure nodes are rendered
57
- const timer = setTimeout(() => {
58
- fitView({
59
- padding: 0.4,
60
- nodes: [...nodesToFit],
61
- // duration: withDuration ? 300 : undefined,
62
- maxZoom: 0.9,
63
- });
64
- }, 100); // Small delay to ensure nodes are rendered
57
+ // Only auto-fit on initial render or when node count changes
58
+ if (!hasInitialFitRef.current) {
59
+ hasInitialFitRef.current = true;
60
+
61
+ const timer = setTimeout(() => {
62
+ fitView({
63
+ padding: 0.4,
64
+ nodes: [...nodesToFit],
65
+ maxZoom: 0.9,
66
+ });
67
+ }, 100);
65
68
 
66
- return () => clearTimeout(timer);
67
- }, [nodesToFit.length, nodesToFit.map(n => `${n.id}-${n.width}-${n.height}`).join(','), withDuration]);
69
+ return () => clearTimeout(timer);
70
+ }
71
+ }, [nodesToFit.length, withDuration]);
68
72
 
69
73
  return (
70
74
  <ComponentWithFocus>
71
- <div style={{ height: "100%", width: "100%" }} ref={ref}>
72
- {filtersConfig ? (
73
- <Filters t={t} filtersConfig={filtersConfig} onFilterChange={onFilterChange} />
74
- ) : null}
75
+ <div style={{ height: "100%", width: "100%", position: "relative" }} ref={ref}>
76
+ {filtersConfig ? (
77
+ <Filters t={t} filtersConfig={filtersConfig} onFilterChange={onFilterChange} />
78
+ ) : null}
75
79
 
76
- <ReactFlow
80
+ <ReactFlow
77
81
  nodes={nodes}
78
82
  edges={edges}
79
83
  zoomOnDoubleClick={false}
@@ -5,7 +5,7 @@ const MultiSelectStyled = styled(Select)`
5
5
  &.ant-select-single {
6
6
  .ant-select-selector {
7
7
  padding-inline-end: 24px !important;
8
- padding: 0 !important;
8
+ padding: 3px !important;
9
9
  max-width: 56px !important;
10
10
  width: 56px !important;
11
11
  }
@@ -177,9 +177,13 @@ export default function DataLinkFlat({
177
177
  }, [entity, values.linking])
178
178
 
179
179
  const dataLinkFormData = useMemo(() => {
180
- const _formData = isSingle ? formData && !Array.isArray(formData)
181
- ? [{ ...form?.meta?.prefilledValues, ...formData }] : []
182
- : (Array.isArray(formData) ? formData : []).map((f) => ({ ...form?.meta?.prefilledValues, ...f }));
180
+ const filteredPrefilledValues = Object.entries(form?.meta?.prefilledValues || {})
181
+ .filter(([_, value]) => !(value && typeof value === 'object' && 'combine' in value))
182
+ .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {});
183
+
184
+ const _formData = isSingle ? formData && !Array.isArray(formData)
185
+ ? [{ ...filteredPrefilledValues, ...formData }] : []
186
+ : (Array.isArray(formData) ? formData : []).map((f) => ({ ...filteredPrefilledValues, ...f }));
183
187
 
184
188
  return (_formData || []).map((f, i) => {
185
189
  if (isAjaxModal && typeof f === 'string' && linkingData[f]) {
@@ -111,9 +111,13 @@ export default function DataLink({
111
111
  })
112
112
 
113
113
  const data = useMemo(() => {
114
+ const filteredPrefilledValues = Object.entries(form?.meta?.prefilledValues || {})
115
+ .filter(([_, value]) => !(value && typeof value === 'object' && 'combine' in value))
116
+ .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {});
117
+
114
118
  const _formData = isSingle ? formData && !Array.isArray(formData)
115
- ? [{ ...form?.meta?.prefilledValues, ...formData }] : []
116
- : (Array.isArray(formData) ? formData : []).map((f) => ({ ...form?.meta?.prefilledValues, ...f }));
119
+ ? [{ ...filteredPrefilledValues, ...formData }] : []
120
+ : (Array.isArray(formData) ? formData : []).map((f) => ({ ...filteredPrefilledValues, ...f }));
117
121
 
118
122
  return (_formData || []).map((f, i) => {
119
123
  if (isAjaxModal && typeof f === 'string' && linkingData[f]) {
@@ -361,13 +361,13 @@ export const BasicInput = ({
361
361
  case 'phoneNumber':
362
362
  case 'textarea':
363
363
  return item
364
- ? (propHasValue(item[inputName]) ? item[inputName] : placeholder(item, inputName, withPlaceholder))
365
- : (propHasValue(data[inputName]) ? data[inputName] : placeholder(data, inputName, withPlaceholder));
364
+ ? (propHasValue(item[inputName]) && item[inputName] !== '' ? item[inputName] : placeholder(item, inputName, withPlaceholder))
365
+ : (propHasValue(data[inputName]) && data[inputName] !== '' ? data[inputName] : placeholder(data, inputName, withPlaceholder));
366
366
  case 'website':
367
367
  case 'link':
368
368
  const v = item
369
- ? (propHasValue(item[inputName]) ? item[inputName] : placeholder(item, inputName, withPlaceholder))
370
- : (propHasValue(data[inputName]) ? data[inputName] : placeholder(data, inputName, withPlaceholder))
369
+ ? (propHasValue(item[inputName]) && item[inputName] !== '' ? item[inputName] : placeholder(item, inputName, withPlaceholder))
370
+ : (propHasValue(data[inputName]) && data[inputName] !== '' ? data[inputName] : placeholder(data, inputName, withPlaceholder))
371
371
 
372
372
  if (isModal) {
373
373
  return (
@@ -1023,7 +1023,7 @@ const Input = ({
1023
1023
  >
1024
1024
  </BasicInput>
1025
1025
  })}
1026
- {isRepeatable && Array.isArray(modalData) && !modalData.length ? (
1026
+ {isRepeatable && Array.isArray(modalData) && !modalData.length || modalData === "" ? (
1027
1027
  <span className="text-muted">{t('Not answered')}</span>
1028
1028
  ) : null}
1029
1029
  </React.Fragment>
@@ -1068,7 +1068,7 @@ const Input = ({
1068
1068
  })
1069
1069
  }
1070
1070
  {
1071
- (!modalData || (Array.isArray(modalData) && !modalData.length)) ? (
1071
+ (!modalData || (Array.isArray(modalData) && !modalData.length) || modalData === "") ? (
1072
1072
  <span className="text-muted">{t('Not answered')}</span>
1073
1073
  ) : (
1074
1074
  (Array.isArray(modalData) ? modalData : [modalData]).map((item, itemId) => (
@@ -1174,7 +1174,7 @@ const Input = ({
1174
1174
  >
1175
1175
  </BasicInput>
1176
1176
  })}
1177
- {(!formData || (Array.isArray(formData) && !formData.length)) ? (
1177
+ {(!formData || (Array.isArray(formData) && !formData.length) || formData === "") ? (
1178
1178
  <span className="text-muted">{t('Not answered')}</span>
1179
1179
  ) : (
1180
1180
  (Array.isArray(formData) ? formData : [formData])?.map((item, itemId) => (
@@ -2,7 +2,6 @@ import { useState, useEffect, useRef } from "react";
2
2
  import DashboardService from "../services/DashboardService.js";
3
3
  import { isErrorResponse, getErrorMessage } from "../../helpers/errorHandling.js";
4
4
  import { message } from "antd";
5
-
6
5
  // config: {
7
6
  // stop: boolean,
8
7
  // defaultData: object,
@@ -10,56 +9,48 @@ import { message } from "antd";
10
9
  // url: string
11
10
  // basePath: string,
12
11
  // }
13
-
14
12
  export const useWidgetFetch = ({config, getData = DashboardService.getWidget, onFetch = () => {}}) => {
15
13
  const { stop, defaultData, ...rest} = config;
16
14
  const [ data, setData ] = useState(defaultData || {});
17
15
  const [ loading, setLoading ] = useState(false);
18
16
  const [ initFetchDone, setInitFetchDone ] = useState(false);
19
17
  const isMounted = useRef(true);
20
-
21
- const fetchData = async () => {
22
- if (stop) {
23
- return;
24
- }
25
-
26
- setLoading(true);
27
-
28
- try {
29
- const { data } = await getData(rest);
30
- if (!isMounted.current) return;
31
- setData(data || defaultData);
32
- if (isErrorResponse(data)) {
33
- const errorMessage = getErrorMessage(data);
34
- message.error(errorMessage);
35
- return;
36
- }
18
+
19
+ const fetchData = async () => {
20
+ setLoading(true);
21
+ try {
22
+ const { data } = await getData(rest);
23
+ if (!isMounted.current) return;
24
+ setData(data || defaultData);
25
+ if (isErrorResponse(data)) {
26
+ const errorMessage = getErrorMessage(data);
27
+ message.error(errorMessage);
28
+ return;
29
+ }
37
30
  onFetch();
38
31
  setInitFetchDone(true);
39
- } catch (err) {
40
- console.log(err);
41
- }
42
-
43
- if (isMounted.current) {
44
- setLoading(false);
45
- }
46
- };
47
-
32
+ } catch (err) {
33
+ console.log(err);
34
+ }
35
+ if (isMounted.current) {
36
+ setLoading(false);
37
+ }
38
+ };
48
39
  useEffect(() => {
49
40
  isMounted.current = true;
50
41
  return () => {
51
42
  isMounted.current = false;
52
43
  };
53
44
  }, []);
54
-
55
- useEffect(() => {
56
- fetchData();
57
- }, [JSON.stringify(config)]);
58
-
45
+ useEffect(() => {
46
+ if(!stop) {
47
+ fetchData();
48
+ }
49
+ }, [JSON.stringify(config), stop]);
59
50
  return {
60
51
  data,
61
52
  loading,
62
- setData,
53
+ setData,
63
54
  initFetchDone
64
55
  }
65
- }
56
+ }
@@ -92,6 +92,7 @@ export default function IncidentsTimeline({
92
92
  if (selectedRange === "12") return baseMonths.map((m) => m.label);
93
93
  return baseWeeks.map((w) => w.label);
94
94
  }, [selectedRange, baseMonths, baseWeeks]);
95
+ console.log({filtersConfig})
95
96
 
96
97
  const getTooltipContent = useCallback(
97
98
  ({ item }) => {
@@ -43,7 +43,6 @@ export const getKeyIndicatorConfig = ({ t, data = {}, options = {} }) => [
43
43
  label: t("Legal Form"),
44
44
  render: () => {
45
45
  const subCategory = findOptions(data?.subCategory, options?.subCategoriesOptions || options?.subCategory);
46
- console.log({subCategory, initialSub: data?.subCategory, options: options?.subCategoriesOptions || options?.subCategory})
47
46
  if(subCategory?.length > 22) {
48
47
  const _subCategory = truncateString(subCategory, 22);
49
48
  return <Tooltip title={subCategory}>
@@ -2,6 +2,7 @@ import { leftIcons, leftBackgroundColorLocation, leftBackgroundColorStakeholder,
2
2
  import { findOptions } from '../../../../../../helpers/StringHelper.js';
3
3
 
4
4
  export const getFilterConfig = ({operatorData = {}, options = {}, filters = {}, t = () => {}, APP}) => {
5
+ console.log({operatorData})
5
6
  const productSet = new Set();
6
7
  const allTradeMineralOptions = [
7
8
  ...(operatorData?.suppliers || []),
@@ -33,17 +34,18 @@ const label = options?.[
33
34
  return option && index === self.findIndex((o) => o.value === option.value);
34
35
  });
35
36
 
36
- const value = filters.products ? { value: filters.products } : {};
37
- return [
38
- {
37
+ const value = filters?.products ? { value: filters?.products } : {};
38
+ return {
39
+ products: {
39
40
  label: t("Products"),
40
41
  placeholder: t("Select"),
41
42
  key: "products",
42
43
  type: "select",
43
- ...value,
44
+ // ...value,
45
+ style: { flex: 1 },
44
46
  options: combinedMineralOptions,
45
47
  },
46
- ];
48
+ };
47
49
  }
48
50
 
49
51
  export const getLeft = (data = {}, mapChildren = () => {}, supplierLocations = []) => {
@@ -1,4 +1,4 @@
1
- import { useEffect, useState, useMemo } from "react";
1
+ import { useEffect, useState, useMemo, useRef } from "react";
2
2
  import { useWidgetFetch } from "../../../../../hooks/useWidgetFetch.js";
3
3
  import { leftIcons, leftBackgroundColorLocation, leftBackgroundColorStakeholder, topIcons } from './config.js';
4
4
  import { findOptions } from '../../../../../../helpers/StringHelper.js';
@@ -8,6 +8,8 @@ import { getLeft, mapItem as _mapItem } from './helper.js';
8
8
  export const useTradeRelationship = ({
9
9
  id,
10
10
  selectedPartners,
11
+ isProductsFilterReady,
12
+ hasProducts,
11
13
  options,
12
14
  goTo,
13
15
  getRedirectLink,
@@ -19,26 +21,38 @@ export const useTradeRelationship = ({
19
21
  const [graphData, setGraphData] = useState({});
20
22
  const [loading, setLoading] = useState(false);
21
23
  const [fetchedProducts, setFetchedProducts] = useState(false);
22
-
23
- const config = useMemo(() => ({
24
- basepath: "analytics",
25
- url: `/widgets/trade-relationship-map`,
26
- filters: {
24
+ const initFetchDoneRef = useRef(false);
25
+
26
+ const product = useMemo(() => {
27
+ return filters?.products
28
+ }, [filters?.products]);
29
+
30
+ console.log({hasProducts, product, isProductsFilterReady, stop: !selectedPartners?.partners?.length
31
+ || selectedPartners?.loading || !isProductsFilterReady || (isProductsFilterReady && hasProducts && !product)},
32
+ !selectedPartners?.partners?.length,
33
+ selectedPartners?.loading,
34
+ !isProductsFilterReady,
35
+ (isProductsFilterReady && hasProducts && !product))
36
+ const config = useMemo(() => ({
37
+ basepath: "analytics",
38
+ url: `/widgets/trade-relationship-map`,
39
+ filters: {
27
40
  datastakeId: id,
28
- product: filters?.products,
29
- sources: selectedPartners?.partners || [],
30
- },
31
- stop: !selectedPartners?.partners?.length || selectedPartners?.loading,
32
- }), [id, filters?.products, selectedPartners?.partners, selectedPartners?.loading]);
33
-
34
- const { data } = useWidgetFetch({config: config});
41
+ ...(product && {product: product}),
42
+ sources: selectedPartners?.partners || [],
43
+ },
44
+ stop: !selectedPartners?.partners?.length
45
+ || selectedPartners?.loading || !isProductsFilterReady || (isProductsFilterReady && hasProducts && !product)
46
+ }), [id, product, selectedPartners?.partners, selectedPartners?.loading, isProductsFilterReady, hasProducts]);
47
+
48
+ const { data, loading: dataLoading } = useWidgetFetch({config: config});
35
49
 
36
50
  const mapItem = (data) => {
37
51
  return _mapItem(data, options, goTo, getRedirectLink, operatorData, APP);
38
52
  }
39
53
 
40
54
  useEffect(() => {
41
- if (id && selectedPartners?.partners?.length > 0 && fetchedProducts) {
55
+ if (id && selectedPartners?.partners?.length > 0) {
42
56
  const _fetch = async () => {
43
57
  setLoading(true);
44
58
 
@@ -156,5 +170,6 @@ export const useTradeRelationship = ({
156
170
  setGraphData,
157
171
  setLoading,
158
172
  data,
173
+ dataLoading
159
174
  }
160
175
  }
@@ -1,4 +1,4 @@
1
- import React, { useState, useMemo, useEffect } from 'react'
1
+ import React, { useState, useMemo, useEffect, useRef } from 'react'
2
2
  import { getFilterConfig } from './helper.js';
3
3
  import { useTradeRelationship } from './hook.js';
4
4
  import Widget from '../../../../../core/components/Dashboard/Widget/index.jsx';
@@ -16,19 +16,33 @@ const TradeRelationships = ({
16
16
  options = {},
17
17
  getRedirectLink = () => {},
18
18
  APP,
19
+ user = {}
19
20
  }) => {
20
21
  const [filters, setFilters] = useState({});
21
22
  const [isFullyRendered, setIsFullyRendered] = useState(true);
23
+ const [isProductsFilterReady, setIsProductsFilterReady] = useState(false);
24
+ const [hasProducts, setHasProducts] = useState(true);
25
+ const [delayedLoading, setDelayedLoading] = useState(false);
26
+ const [delayedDataLoading, setDelayedDataLoading] = useState(false);
22
27
 
23
28
  const onFilterChange = (filters) => {
24
- setFilters((p) => ({ ...p, ...filters }));
29
+ setFilters(filters);
25
30
  };
26
31
 
27
32
  const filterConfig = useMemo(() => {
28
- return getFilterConfig({operatorData, options, filters, t, APP});
33
+ const filterConfig = Object.keys(operatorData).length > 0 ? getFilterConfig({operatorData, options, filters, t, APP}) : {};
34
+ return filterConfig;
29
35
  }, [filters.products, t, options?.mineralOptions, operatorData, options?.minerals, APP]);
30
36
 
31
- const { graphData, loading, fetchedProducts, setFetchedProducts } = useTradeRelationship({
37
+ useEffect(() => {
38
+ if (filterConfig?.products?.options?.length >= 0 && !isProductsFilterReady) {
39
+ console.log('setting has products', filterConfig?.products?.options?.length)
40
+ setHasProducts(filterConfig?.products?.options?.length > 0);
41
+ setIsProductsFilterReady(true);
42
+ }
43
+ }, [filterConfig, isProductsFilterReady]);
44
+
45
+ const { graphData, loading, fetchedProducts, setFetchedProducts, dataLoading } = useTradeRelationship({
32
46
  id,
33
47
  selectedPartners,
34
48
  options,
@@ -36,31 +50,68 @@ const TradeRelationships = ({
36
50
  getRedirectLink,
37
51
  filters,
38
52
  operatorData,
39
- APP
53
+ APP,
54
+ isProductsFilterReady,
55
+ hasProducts,
40
56
  });
41
57
 
42
- console.log({graphData});
58
+ const defaultProduct = useMemo(() => {
59
+ if (filterConfig?.products?.options?.length) {
60
+ return filterConfig?.products?.options?.[0]?.value || null;
61
+ }
62
+ }, [filterConfig]);
43
63
 
44
- useEffect(() => {
45
- const defaultProduct = filterConfig?.[0]?.options?.[0]?.value;
46
64
 
47
- if (!filters.products && defaultProduct) {
48
- setFilters((prev) => {
49
- const data = { ...prev, products: defaultProduct };
50
- return data;
51
- });
65
+ useEffect(() => {
66
+ if (defaultProduct) {
67
+ setFilters({ products: defaultProduct });
52
68
  }
69
+ }, [defaultProduct]);
70
+
71
+
72
+ const _filtersConfig = useMemo(() => ({
73
+ filtersConfig: {
74
+ ...filterConfig,
75
+ },
76
+ options: Object.fromEntries(
77
+ Object.keys(filterConfig).map(key => [key, filterConfig[key].options])
78
+ ),
79
+ language: user?.language,
80
+ selectedFilters: filters,
81
+ onApply: onFilterChange,
82
+ t: t,
83
+ }), [filterConfig, user?.language, filters, t]);
53
84
 
54
- if (!fetchedProducts) {
55
- setFetchedProducts(true);
85
+ useEffect(() => {
86
+ if (loading) {
87
+ setDelayedLoading(true);
88
+ } else {
89
+ const timer = setTimeout(() => {
90
+ setDelayedLoading(false);
91
+ }, 500);
92
+
93
+ return () => clearTimeout(timer);
56
94
  }
57
- }, [filterConfig]);
95
+ }, [loading]);
96
+
97
+ useEffect(() => {
98
+ if (dataLoading) {
99
+ setDelayedDataLoading(true);
100
+ } else {
101
+ const timer = setTimeout(() => {
102
+ setDelayedDataLoading(false);
103
+ }, 500);
104
+
105
+ return () => clearTimeout(timer);
106
+ }
107
+ }, [dataLoading]);
58
108
 
59
109
  return (
60
110
  <Widget
61
111
  title={t("Trade Relationships")}
62
112
  className="flex flex-1 with-border-header no-p-body"
63
- loading={loading || isFullyRendered}
113
+ loading={delayedLoading || delayedDataLoading}
114
+ filtersConfig={_filtersConfig}
64
115
  >
65
116
  <div className="flex flex-1 flex-column justify-content-center">
66
117
  <div style={{ height: 600 }} className="flex flex-column">
@@ -84,12 +135,10 @@ const TradeRelationships = ({
84
135
  maxZoom={1.2}
85
136
  minZoom={0.4}
86
137
  tooltipTitle="Trade"
87
- filtersConfig={filterConfig}
88
- onFilterChange={onFilterChange}
89
138
  onRenderComplete={(data) => {
90
- console.log("onRenderComplete");
91
139
  setIsFullyRendered(!data);
92
140
  }}
141
+ filters={filters}
93
142
  />
94
143
  </div>
95
144
  </div>
@@ -160,6 +160,7 @@ const OperatorSummary = ({
160
160
  getRedirectLink={getRedirectLink}
161
161
  operatorData={singleItemData}
162
162
  APP={APP}
163
+ user={user}
163
164
  />
164
165
  </section>
165
166
  <section>
@@ -260,7 +260,7 @@ const View = ({
260
260
  <Multiselect
261
261
  options={[...sourceOptions]}
262
262
  isAvatarGroup
263
- selectionType="checkbox"
263
+ selectionType="radio"
264
264
  key={partners?.length}
265
265
  canUnselectLast={false}
266
266
  isSingle
@@ -6,6 +6,7 @@ import dayjs from "dayjs";
6
6
  import customParseFormat from "dayjs/plugin/customParseFormat";
7
7
  import utc from "dayjs/plugin/utc";
8
8
  import localizedFormat from "dayjs/plugin/localizedFormat";
9
+ import { findOptions } from "./StringHelper";
9
10
  import "dayjs/locale/fr"; // import desired locales
10
11
  import "dayjs/locale/es";
11
12
  import "dayjs/locale/en";
@@ -897,7 +898,7 @@ export const transformPayload = (payload) => {
897
898
  * @param {object} obj - Object to traverse
898
899
  * @returns {any} - Resolved value or undefined
899
900
  */
900
- export const resolveValuePath = (path, obj) => {
901
+ export const resolveValuePath = (path, obj, form) => {
901
902
  if (!path || !obj) return undefined;
902
903
 
903
904
  // Split by / and traverse the object
@@ -918,7 +919,8 @@ export const resolveValuePath = (path, obj) => {
918
919
  }
919
920
  }
920
921
 
921
- return current;
922
+ const value = Object.keys(form || {})?.length > 0 ? findOptions(current, form?.options || []) : current;
923
+ return value;
922
924
  };
923
925
 
924
926
  /**
@@ -989,7 +991,7 @@ export const getCombinedPrefilledValues = (form) => {
989
991
  * @param {object} parentValues - Parent form values
990
992
  * @returns {string|null} - Resolved string with placeholders replaced by actual values, or null if not ready
991
993
  */
992
- export const resolveCombinedPrefilledValue = (template, values = {}, parentValues = {}) => {
994
+ export const resolveCombinedPrefilledValue = (template, values = {}, parentValues = {}, form) => {
993
995
  if (!template || typeof template !== 'string') return '';
994
996
 
995
997
  // Regular expression to match placeholders like {path} or {path^subpath}
@@ -1005,7 +1007,7 @@ export const resolveCombinedPrefilledValue = (template, values = {}, parentValue
1005
1007
  if (nonParentPaths.length > 0) {
1006
1008
  // Check if at least one non-parent value has actual data
1007
1009
  hasNonParentValue = nonParentPaths.some(path => {
1008
- const value = resolveValuePath(path, values);
1010
+ const value = resolveValuePath(path, values, form?.inputs?.[path]);
1009
1011
  return value !== undefined && value !== null && value !== '';
1010
1012
  });
1011
1013
 
@@ -1027,7 +1029,7 @@ export const resolveCombinedPrefilledValue = (template, values = {}, parentValue
1027
1029
  return formatValue(resolved);
1028
1030
  } else {
1029
1031
  // Regular path in current values
1030
- const resolved = resolveValuePath(path, values);
1032
+ const resolved = resolveValuePath(path, values, form?.inputs?.[path]);
1031
1033
  return formatValue(resolved);
1032
1034
  }
1033
1035
  });