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.
- package/build/favicon.ico +0 -0
- package/build/logo192.png +0 -0
- package/build/logo512.png +0 -0
- package/build/manifest.json +25 -0
- package/build/robots.txt +3 -0
- package/dist/components/index.js +1884 -1970
- package/dist/hooks/index.js +4 -6
- package/dist/layouts/index.js +6 -0
- package/dist/pages/index.js +166 -113
- package/package.json +1 -1
- package/src/@daf/core/components/DynamicForm/hook.js +2 -2
- package/src/@daf/core/components/DynamicForm/index.jsx +1 -1
- package/src/@daf/core/components/Filters/selectFilters/index.jsx +7 -1
- package/src/@daf/core/components/Graphs/TradeRelationship/index.jsx +1 -27
- package/src/@daf/core/components/Graphs/components/BaseGraph.jsx +22 -18
- package/src/@daf/core/components/Select/MultiSelect/style.js +1 -1
- package/src/@daf/core/components/ViewForm/components/DataLink/flat.js +7 -3
- package/src/@daf/core/components/ViewForm/components/DataLink/index.js +6 -2
- package/src/@daf/core/components/ViewForm/components/input.js +7 -7
- package/src/@daf/hooks/useWidgetFetch.js +26 -35
- package/src/@daf/pages/Dashboards/ConflictManagement/components/RisksWidget/components/IncidentsTime/index.js +1 -0
- package/src/@daf/pages/Summary/Operator/components/KeyInformation/config.js +0 -1
- package/src/@daf/pages/Summary/Operator/components/TradeRelationships/helper.js +7 -5
- package/src/@daf/pages/Summary/Operator/components/TradeRelationships/hook.js +29 -14
- package/src/@daf/pages/Summary/Operator/components/TradeRelationships/index.js +69 -20
- package/src/@daf/pages/Summary/Operator/index.jsx +1 -0
- package/src/@daf/pages/View/index.jsx +1 -1
- 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
|
|
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
|
-
//
|
|
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
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
67
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
80
|
+
<ReactFlow
|
|
77
81
|
nodes={nodes}
|
|
78
82
|
edges={edges}
|
|
79
83
|
zoomOnDoubleClick={false}
|
|
@@ -177,9 +177,13 @@ export default function DataLinkFlat({
|
|
|
177
177
|
}, [entity, values.linking])
|
|
178
178
|
|
|
179
179
|
const dataLinkFormData = useMemo(() => {
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
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
|
-
? [{ ...
|
|
116
|
-
: (Array.isArray(formData) ? formData : []).map((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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if(!stop) {
|
|
47
|
+
fetchData();
|
|
48
|
+
}
|
|
49
|
+
}, [JSON.stringify(config), stop]);
|
|
59
50
|
return {
|
|
60
51
|
data,
|
|
61
52
|
loading,
|
|
62
|
-
|
|
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
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
stop: !selectedPartners?.partners?.length
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
|
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(
|
|
29
|
+
setFilters(filters);
|
|
25
30
|
};
|
|
26
31
|
|
|
27
32
|
const filterConfig = useMemo(() => {
|
|
28
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
55
|
-
|
|
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
|
-
}, [
|
|
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={
|
|
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>
|
package/src/helpers/Forms.js
CHANGED
|
@@ -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
|
-
|
|
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
|
});
|