datastake-daf 0.6.815 → 0.6.816
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/index.js +1703 -1578
- package/dist/pages/index.js +344 -685
- package/dist/style/datastake/mapbox-gl.css +330 -0
- package/dist/utils/index.js +0 -28
- package/package.json +1 -1
- package/src/@daf/core/components/Charts/RadarChart/index.jsx +12 -3
- package/src/@daf/core/components/Charts/style.js +2 -1
- package/src/@daf/core/components/Dashboard/Map/ChainIcon/index.js +104 -123
- package/src/@daf/core/components/Dashboard/Widget/VegetationWidget/index.jsx +0 -4
- package/src/@daf/core/components/Table/index.jsx +6 -11
- package/src/@daf/pages/Events/Activities/columns.js +11 -15
- package/src/@daf/pages/Events/Incidents/columns.js +11 -15
- package/src/@daf/pages/Events/columns.js +3 -7
- package/src/@daf/pages/Locations/MineSite/columns.js +11 -16
- package/src/@daf/pages/Locations/columns.js +3 -7
- package/src/@daf/pages/Stakeholders/Operators/columns.js +12 -16
- package/src/@daf/pages/Stakeholders/Workers/columns.js +12 -16
- package/src/@daf/pages/Stakeholders/columns.js +4 -8
- package/src/@daf/pages/Summary/Activities/MonitoringCampaign/components/BiodiversityHabitat/ObservedFauna.jsx +6 -11
- package/src/@daf/pages/Summary/Activities/MonitoringCampaign/components/MangroveGrowth/PlantedSpecies.jsx +25 -10
- package/src/@daf/pages/Summary/Activities/MonitoringCampaign/components/MangroveGrowth/SeedlingsHeight.jsx +10 -13
- package/src/@daf/pages/Summary/Activities/MonitoringCampaign/components/MangroveGrowth/VegetationHealth.jsx +19 -4
- package/src/@daf/pages/Summary/Activities/MonitoringCampaign/components/SoilWaterProfile/SoilType.jsx +22 -10
- package/src/@daf/pages/Summary/Activities/MonitoringCampaign/components/SoilWaterProfile/WaterQuality.jsx +26 -10
- package/src/@daf/pages/Summary/Activities/MonitoringCampaign/components/chartHelpers.js +74 -0
- package/src/@daf/pages/TablePage/helper.js +0 -15
- package/src/helpers/errorHandling.js +74 -142
- package/src/utils.js +1 -1
- package/src/@daf/pages/Events/Testimonials/columns.js +0 -173
- package/src/@daf/pages/Events/Testimonials/config.js +0 -175
|
@@ -2,7 +2,14 @@ import React, { useMemo, useCallback } from 'react';
|
|
|
2
2
|
import { Widget, PieChart } from '../../../../../../../../src/index.js';
|
|
3
3
|
import { renderTooltipJsx } from '../../../../../../utils/tooltip.js';
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
// Color mapping for planted species
|
|
6
|
+
const PLANTED_SPECIES_COLORS = {
|
|
7
|
+
rhyzophora_mangle: '#016C6E',
|
|
8
|
+
rhyzophora: '#00AEB1',
|
|
9
|
+
rhyzophora_sp: '#A0EBEC',
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const DEFAULT_COLOR = '#9E9E9E';
|
|
6
13
|
|
|
7
14
|
const PlantedSpecies = ({
|
|
8
15
|
plantedSpeciesChart,
|
|
@@ -27,7 +34,7 @@ const PlantedSpecies = ({
|
|
|
27
34
|
return data.map((item, index) => ({
|
|
28
35
|
value: Number(item?.count) || 0,
|
|
29
36
|
percent: total > 0 ? (Number(item?.count) || 0) / total : 0,
|
|
30
|
-
color:
|
|
37
|
+
color: PLANTED_SPECIES_COLORS[item?.name] || DEFAULT_COLOR,
|
|
31
38
|
label: optionsMap[item?.name] || item?.name || '',
|
|
32
39
|
key: item?.name || `item-${index}`,
|
|
33
40
|
}));
|
|
@@ -44,18 +51,26 @@ const PlantedSpecies = ({
|
|
|
44
51
|
return null;
|
|
45
52
|
}
|
|
46
53
|
|
|
54
|
+
// Calculate total from all items
|
|
55
|
+
const total = pieData.reduce((sum, dataItem) => sum + (dataItem.value || 0), 0);
|
|
56
|
+
|
|
57
|
+
// Show all items in the tooltip, sorted by value (descending)
|
|
58
|
+
const allItems = pieData
|
|
59
|
+
.filter(dataItem => dataItem.value > 0)
|
|
60
|
+
.sort((a, b) => b.value - a.value)
|
|
61
|
+
.map(dataItem => ({
|
|
62
|
+
color: dataItem.color,
|
|
63
|
+
label: dataItem.label || '',
|
|
64
|
+
value: dataItem.value || 0,
|
|
65
|
+
}));
|
|
66
|
+
|
|
47
67
|
return renderTooltipJsx({
|
|
48
68
|
title: t("Planted Species"),
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
color: item.color,
|
|
52
|
-
label: item.label || '',
|
|
53
|
-
value: item.value || 0,
|
|
54
|
-
},
|
|
55
|
-
],
|
|
69
|
+
subTitle: total.toLocaleString(),
|
|
70
|
+
items: allItems,
|
|
56
71
|
});
|
|
57
72
|
},
|
|
58
|
-
[t, isEmpty]
|
|
73
|
+
[t, isEmpty, pieData]
|
|
59
74
|
);
|
|
60
75
|
|
|
61
76
|
return (
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useMemo } from 'react';
|
|
2
2
|
import { Widget, ColumnChart } from '../../../../../../../../src/index.js';
|
|
3
|
-
import {
|
|
3
|
+
import { calculateNaturalAxisConfig, mergeDefaultCategories } from '../chartHelpers.js';
|
|
4
4
|
|
|
5
5
|
// Default height ranges to always display
|
|
6
6
|
const DEFAULT_HEIGHT_RANGES = [
|
|
@@ -26,16 +26,13 @@ const SeedlingsHeight = ({
|
|
|
26
26
|
}, [seedlingsHeightChart]);
|
|
27
27
|
|
|
28
28
|
const yAxisConfig = useMemo(() => {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
tickMethod: () => [0, 5, 10, 15, 20]
|
|
37
|
-
}
|
|
38
|
-
);
|
|
29
|
+
// Calculate max value from data
|
|
30
|
+
const maxValue = chartData && chartData.length > 0
|
|
31
|
+
? Math.max(...chartData.map(item => Number(item?.value) || 0))
|
|
32
|
+
: 0;
|
|
33
|
+
|
|
34
|
+
// Use the helper function to calculate natural number axis configuration
|
|
35
|
+
return calculateNaturalAxisConfig(maxValue);
|
|
39
36
|
}, [chartData]);
|
|
40
37
|
|
|
41
38
|
return (
|
|
@@ -58,10 +55,10 @@ const SeedlingsHeight = ({
|
|
|
58
55
|
const item = data[0]?.data || data[0];
|
|
59
56
|
return {
|
|
60
57
|
title: t("Seedlings Height"),
|
|
61
|
-
subTitle: title,
|
|
62
58
|
items: [
|
|
63
59
|
{
|
|
64
|
-
|
|
60
|
+
color: "#016C6E",
|
|
61
|
+
label: title,
|
|
65
62
|
value: item?.value || 0,
|
|
66
63
|
},
|
|
67
64
|
],
|
|
@@ -2,7 +2,22 @@ import React, { useMemo, useCallback } from 'react';
|
|
|
2
2
|
import { Widget, PieChart } from '../../../../../../../../src/index.js';
|
|
3
3
|
import { renderTooltipJsx } from '../../../../../../utils/tooltip.js';
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
// Color mapping for vegetation health conditions
|
|
6
|
+
const VEGETATION_HEALTH_COLORS = {
|
|
7
|
+
healthy_leaves: '#6AD99E',
|
|
8
|
+
white_spots: '#E8F0F0',
|
|
9
|
+
yellowing_leaves: '#CDC14F',
|
|
10
|
+
black_spots: '#2E3131',
|
|
11
|
+
reddish_spots: '#CB2525',
|
|
12
|
+
leaf_mosaic: '#B59E76',
|
|
13
|
+
spider_webs: '#F8F6EF',
|
|
14
|
+
damage_from_insects: '#DE8954',
|
|
15
|
+
dry_dead_leaves: '#767870',
|
|
16
|
+
no_leaves: '#F97066',
|
|
17
|
+
other: '#BDBDBD',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const DEFAULT_COLOR = '#9E9E9E';
|
|
6
21
|
|
|
7
22
|
const VegetationHealth = ({
|
|
8
23
|
vegetationHealthChart,
|
|
@@ -26,15 +41,15 @@ const VegetationHealth = ({
|
|
|
26
41
|
return data.map((item, index) => ({
|
|
27
42
|
value: Number(item?.count) || 0,
|
|
28
43
|
percent: total > 0 ? (Number(item?.count) || 0) / total : 0,
|
|
29
|
-
color:
|
|
44
|
+
color: VEGETATION_HEALTH_COLORS[item?.name] || DEFAULT_COLOR,
|
|
30
45
|
label: optionsMap[item?.name] || item?.name || '',
|
|
31
46
|
key: item?.name || `item-${index}`,
|
|
32
47
|
}));
|
|
33
48
|
}, [vegetationHealthChart, optionsMap]);
|
|
34
49
|
|
|
35
50
|
const isEmpty = useMemo(() => {
|
|
36
|
-
return !vegetationHealthChart || vegetationHealthChart.length === 0 ||
|
|
37
|
-
|
|
51
|
+
return !vegetationHealthChart || vegetationHealthChart.length === 0 ||
|
|
52
|
+
vegetationHealthChart.every(item => !item?.count || Number(item.count) === 0);
|
|
38
53
|
}, [vegetationHealthChart]);
|
|
39
54
|
|
|
40
55
|
const getTooltipChildren = useCallback(
|
|
@@ -3,7 +3,15 @@ import { Widget, PieChart } from '../../../../../../../../src/index.js';
|
|
|
3
3
|
import { renderTooltipJsx } from '../../../../../../utils/tooltip.js';
|
|
4
4
|
import { renderPercentage } from '../../../../../../utils/numbers.js';
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
// Color mapping for soil types
|
|
7
|
+
const SOIL_TYPE_COLORS = {
|
|
8
|
+
sandy: '#00AEB1',
|
|
9
|
+
mixed: '#016C6E',
|
|
10
|
+
clay: '#A0EBEC',
|
|
11
|
+
muddy: '#4FB3A1',
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const DEFAULT_COLOR = '#9E9E9E';
|
|
7
15
|
|
|
8
16
|
const SoilType = ({
|
|
9
17
|
soilTypeChart,
|
|
@@ -28,7 +36,7 @@ const SoilType = ({
|
|
|
28
36
|
return data.map((item, index) => ({
|
|
29
37
|
value: Number(item?.count) || 0,
|
|
30
38
|
percent: total > 0 ? (Number(item?.count) || 0) / total : 0,
|
|
31
|
-
color:
|
|
39
|
+
color: SOIL_TYPE_COLORS[item?.soilType] || DEFAULT_COLOR,
|
|
32
40
|
label: optionsMap[item?.soilType] || item?.soilType || '',
|
|
33
41
|
key: item?.soilType || `item-${index}`,
|
|
34
42
|
}));
|
|
@@ -45,17 +53,21 @@ const SoilType = ({
|
|
|
45
53
|
return null;
|
|
46
54
|
}
|
|
47
55
|
|
|
56
|
+
// Show all items in the tooltip, sorted by percentage (descending)
|
|
57
|
+
const allItems = pieData
|
|
58
|
+
.filter(dataItem => dataItem.value > 0)
|
|
59
|
+
.sort((a, b) => b.percent - a.percent)
|
|
60
|
+
.map(dataItem => ({
|
|
61
|
+
color: dataItem.color,
|
|
62
|
+
label: dataItem.label || '',
|
|
63
|
+
value: renderPercentage(Math.round(dataItem.percent * 100)),
|
|
64
|
+
}));
|
|
65
|
+
|
|
48
66
|
return renderTooltipJsx({
|
|
49
67
|
title: t("Soil Type"),
|
|
50
|
-
items:
|
|
51
|
-
{
|
|
52
|
-
color: item.color,
|
|
53
|
-
label: optionsMap[item.label] || item.label || '',
|
|
54
|
-
value: `${ renderPercentage(item.percent.toFixed(2) * 100)}`,
|
|
55
|
-
},
|
|
56
|
-
],
|
|
68
|
+
items: allItems,
|
|
57
69
|
});
|
|
58
|
-
}, [t, isEmpty,
|
|
70
|
+
}, [t, isEmpty, pieData]);
|
|
59
71
|
|
|
60
72
|
return (
|
|
61
73
|
<Widget
|
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
import React, { useMemo, useCallback } from 'react';
|
|
2
2
|
import { Widget, PieChart } from '../../../../../../../../src/index.js';
|
|
3
3
|
import { renderTooltipJsx } from '../../../../../../utils/tooltip.js';
|
|
4
|
+
import { renderPercentage } from '../../../../../../utils/numbers.js';
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
// Color mapping for water quality types
|
|
7
|
+
const WATER_QUALITY_COLORS = {
|
|
8
|
+
fairly_clear: '#32D583',
|
|
9
|
+
turbid: '#FFA940',
|
|
10
|
+
polluted: '#F97066',
|
|
11
|
+
oil_traces: '#2B3644',
|
|
12
|
+
no_water: '#DAD6CE',
|
|
13
|
+
clear: '#6AD99E',
|
|
14
|
+
other: '#9E9E9E',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const DEFAULT_COLOR = '#9E9E9E';
|
|
6
18
|
|
|
7
19
|
const WaterQuality = ({
|
|
8
20
|
waterQualityChart,
|
|
@@ -26,7 +38,7 @@ const WaterQuality = ({
|
|
|
26
38
|
return data.map((item, index) => ({
|
|
27
39
|
value: Number(item?.count) || 0,
|
|
28
40
|
percent: total > 0 ? (Number(item?.count) || 0) / total : 0,
|
|
29
|
-
color:
|
|
41
|
+
color: WATER_QUALITY_COLORS[item?.waterQuality] || DEFAULT_COLOR,
|
|
30
42
|
label: optionsMap[item?.waterQuality] || item?.waterQuality || '',
|
|
31
43
|
key: item?.waterQuality || `item-${index}`,
|
|
32
44
|
}));
|
|
@@ -43,18 +55,22 @@ const WaterQuality = ({
|
|
|
43
55
|
return null;
|
|
44
56
|
}
|
|
45
57
|
|
|
58
|
+
// Show all items in the tooltip, sorted by percentage (descending)
|
|
59
|
+
const allItems = pieData
|
|
60
|
+
.filter(dataItem => dataItem.value > 0)
|
|
61
|
+
.sort((a, b) => b.percent - a.percent)
|
|
62
|
+
.map(dataItem => ({
|
|
63
|
+
color: dataItem.color,
|
|
64
|
+
label: dataItem.label || '',
|
|
65
|
+
value: renderPercentage(Math.round(dataItem.percent * 100)),
|
|
66
|
+
}));
|
|
67
|
+
|
|
46
68
|
return renderTooltipJsx({
|
|
47
69
|
title: t("Water Quality"),
|
|
48
|
-
items:
|
|
49
|
-
{
|
|
50
|
-
color: item.color,
|
|
51
|
-
label: item.label || '',
|
|
52
|
-
value: `${Math.round(item.percent * 100)}%`,
|
|
53
|
-
},
|
|
54
|
-
],
|
|
70
|
+
items: allItems,
|
|
55
71
|
});
|
|
56
72
|
},
|
|
57
|
-
[t, isEmpty]
|
|
73
|
+
[t, isEmpty, pieData]
|
|
58
74
|
);
|
|
59
75
|
|
|
60
76
|
return (
|
|
@@ -59,6 +59,80 @@ export const calculateNiceAxisConfig = (data, valueField = 'value', multiplier =
|
|
|
59
59
|
};
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Calculate y-axis configuration with natural numbers (integers) only
|
|
64
|
+
* Always starts from 0 and extends slightly above max for better visualization
|
|
65
|
+
* @param {number} maxValue - Maximum value from the data
|
|
66
|
+
* @param {Object} options - Optional configuration
|
|
67
|
+
* @param {number} options.minTicks - Minimum number of ticks to show (default: 4)
|
|
68
|
+
* @param {number} options.maxTicks - Maximum number of ticks to show (default: 6)
|
|
69
|
+
* @returns {Object} Axis configuration with min, max, and tickMethod
|
|
70
|
+
*/
|
|
71
|
+
export const calculateNaturalAxisConfig = (maxValue, options = {}) => {
|
|
72
|
+
const { minTicks = 4, maxTicks = 6 } = options;
|
|
73
|
+
|
|
74
|
+
if (maxValue <= 0) {
|
|
75
|
+
return {
|
|
76
|
+
min: 0,
|
|
77
|
+
max: 4,
|
|
78
|
+
tickMethod: () => [0, 1, 2, 3, 4]
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// For very small values (max <= 1), always show 0, 1, 2, 3, 4
|
|
83
|
+
if (maxValue <= 1) {
|
|
84
|
+
return {
|
|
85
|
+
min: 0,
|
|
86
|
+
max: 4,
|
|
87
|
+
tickMethod: () => [0, 1, 2, 3, 4]
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Calculate appropriate step size based on max value
|
|
92
|
+
let step = 1;
|
|
93
|
+
let displayMax = maxValue;
|
|
94
|
+
|
|
95
|
+
if (maxValue <= 5) {
|
|
96
|
+
// For small values, use step of 1 and extend a bit above
|
|
97
|
+
step = 1;
|
|
98
|
+
displayMax = Math.max(5, Math.ceil(maxValue * 1.5));
|
|
99
|
+
} else if (maxValue <= 10) {
|
|
100
|
+
// For medium-small values, use step of 2
|
|
101
|
+
step = 2;
|
|
102
|
+
displayMax = Math.ceil((maxValue * 1.2) / step) * step;
|
|
103
|
+
} else if (maxValue <= 20) {
|
|
104
|
+
// For medium values, use step of 5
|
|
105
|
+
step = 5;
|
|
106
|
+
displayMax = Math.ceil((maxValue * 1.2) / step) * step;
|
|
107
|
+
} else if (maxValue <= 50) {
|
|
108
|
+
// For larger values, use step of 10
|
|
109
|
+
step = 10;
|
|
110
|
+
displayMax = Math.ceil((maxValue * 1.2) / step) * step;
|
|
111
|
+
} else {
|
|
112
|
+
// For very large values, calculate step to get 4-6 ticks
|
|
113
|
+
const targetTicks = Math.min(maxTicks, Math.max(minTicks, Math.ceil(maxValue / 20)));
|
|
114
|
+
step = Math.ceil(maxValue / targetTicks / 10) * 10; // Round to nearest 10
|
|
115
|
+
displayMax = Math.ceil((maxValue * 1.2) / step) * step;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Generate ticks from 0 to displayMax
|
|
119
|
+
const ticks = [];
|
|
120
|
+
for (let i = 0; i <= displayMax; i += step) {
|
|
121
|
+
ticks.push(i);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Ensure max value is included if it's close
|
|
125
|
+
if (ticks[ticks.length - 1] < maxValue && maxValue - ticks[ticks.length - 1] <= step / 2) {
|
|
126
|
+
ticks.push(maxValue);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
min: 0,
|
|
131
|
+
max: displayMax,
|
|
132
|
+
tickMethod: () => ticks
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
|
|
62
136
|
/**
|
|
63
137
|
* Merge default categories with backend data
|
|
64
138
|
* Ensures all categories are displayed even if they have no data
|
|
@@ -45,14 +45,6 @@ import {
|
|
|
45
45
|
viewConfig as viewConfigIncidents
|
|
46
46
|
} from '../Events/Incidents/config.js';
|
|
47
47
|
import { getColumns as getColumnsIncidents } from "../Events/Incidents/columns.js";
|
|
48
|
-
|
|
49
|
-
import {
|
|
50
|
-
getFiltersConfig as getFiltersConfigTestimonials,
|
|
51
|
-
getFilterOptions as getFilterOptionsTestimonials,
|
|
52
|
-
formConfig as formConfigTestimonials,
|
|
53
|
-
viewConfig as viewConfigTestimonials
|
|
54
|
-
} from '../Events/Testimonials/config.js';
|
|
55
|
-
import { getColumns as getColumnsTestimonials } from "../Events/Testimonials/columns.js";
|
|
56
48
|
|
|
57
49
|
import {
|
|
58
50
|
getFiltersConfig as getFiltersConfigLocations,
|
|
@@ -131,13 +123,6 @@ export const FILTER_REGISTRY = {
|
|
|
131
123
|
viewConfig: viewConfigIncidents,
|
|
132
124
|
columns: getColumnsIncidents,
|
|
133
125
|
},
|
|
134
|
-
testimonials: {
|
|
135
|
-
config: getFiltersConfigTestimonials,
|
|
136
|
-
options: getFilterOptionsTestimonials,
|
|
137
|
-
formConfig: formConfigTestimonials,
|
|
138
|
-
viewConfig: viewConfigTestimonials,
|
|
139
|
-
columns: getColumnsTestimonials,
|
|
140
|
-
},
|
|
141
126
|
locations: {
|
|
142
127
|
config: getFiltersConfigLocations,
|
|
143
128
|
options: getFilterOptionsLocations,
|
|
@@ -1,135 +1,67 @@
|
|
|
1
1
|
import { message } from 'antd';
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* Check if a successful response contains embedded error data
|
|
5
|
-
*/
|
|
6
|
-
export const isErrorResponse = (data) => {
|
|
7
|
-
if (!data) return false;
|
|
8
|
-
|
|
9
|
-
return (
|
|
10
|
-
// Check for Exception names
|
|
11
|
-
(data.name && data.name.includes('Exception')) ||
|
|
12
|
-
// Check for nested response with error status
|
|
13
|
-
(data.response?.statusCode && data.response.statusCode >= 400) ||
|
|
14
|
-
// Check for top-level error status
|
|
15
|
-
(data.statusCode && data.statusCode >= 400) ||
|
|
16
|
-
// Check for explicit error flag
|
|
17
|
-
data.error === true ||
|
|
18
|
-
// Check for nested error property
|
|
19
|
-
(data.response?.error && data.response.error !== null)
|
|
20
|
-
);
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Extract error message from various error response formats
|
|
25
|
-
*/
|
|
26
|
-
export const getErrorMessage = (data) => {
|
|
27
|
-
return (
|
|
28
|
-
data?.message ||
|
|
29
|
-
data?.response?.message ||
|
|
30
|
-
data?.response?.error ||
|
|
31
|
-
'An error occurred'
|
|
32
|
-
);
|
|
33
|
-
};
|
|
34
|
-
|
|
35
3
|
/**
|
|
36
4
|
* Generic error handler factory for axios requests
|
|
37
5
|
* Highly configurable to adapt to different project needs
|
|
38
6
|
*/
|
|
39
7
|
export const createErrorHandler = (config = {}) => {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
8
|
+
const {
|
|
9
|
+
onUnauthorized,
|
|
10
|
+
handleError,
|
|
11
|
+
getStorageManager,
|
|
12
|
+
checkTokenValidity = true,
|
|
13
|
+
unauthorizedRedirect = '/',
|
|
14
|
+
tokenStorageKey = 'token',
|
|
15
|
+
handleNetworkError = true,
|
|
16
|
+
} = config;
|
|
17
|
+
|
|
18
|
+
return (error, customOnUnauthorized) => {
|
|
19
|
+
// Handle cases where error.response doesn't exist (network errors)
|
|
20
|
+
if (!error.response) {
|
|
21
|
+
if (handleNetworkError && handleError) {
|
|
22
|
+
handleError({
|
|
23
|
+
status: 0,
|
|
24
|
+
statusText: "Network Error",
|
|
25
|
+
data: ["Please check your internet connection."],
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return Promise.reject(error);
|
|
59
29
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
window.location.href = unauthorizedRedirect;
|
|
30
|
+
|
|
31
|
+
const { status, statusText, data: { errors, message } = {} } = error.response || { data: {} };
|
|
32
|
+
|
|
33
|
+
// Handle 401 Unauthorized
|
|
34
|
+
if (status === 401) {
|
|
35
|
+
if (checkTokenValidity && getStorageManager) {
|
|
36
|
+
const token = getStorageManager().get(tokenStorageKey);
|
|
37
|
+
if (token) {
|
|
38
|
+
if (typeof customOnUnauthorized === 'function') {
|
|
39
|
+
customOnUnauthorized();
|
|
40
|
+
} else if (typeof onUnauthorized === 'function') {
|
|
41
|
+
onUnauthorized();
|
|
42
|
+
} else {
|
|
43
|
+
getStorageManager().clearOne(tokenStorageKey);
|
|
44
|
+
if (typeof window !== 'undefined') {
|
|
45
|
+
window.location.href = unauthorizedRedirect;
|
|
46
|
+
}
|
|
78
47
|
}
|
|
79
48
|
}
|
|
80
49
|
}
|
|
81
50
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return Promise.reject(error);
|
|
96
|
-
};
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Create axios response interceptor to catch embedded errors in successful responses
|
|
101
|
-
*/
|
|
102
|
-
export const createResponseInterceptor = (config = {}) => {
|
|
103
|
-
const { showNotification = true } = config;
|
|
104
|
-
|
|
105
|
-
return {
|
|
106
|
-
onSuccess: (response) => {
|
|
107
|
-
// Check if the response data contains an error
|
|
108
|
-
if (isErrorResponse(response.data)) {
|
|
109
|
-
const errorMessage = getErrorMessage(response.data);
|
|
110
|
-
|
|
111
|
-
if (showNotification) {
|
|
112
|
-
message.error(errorMessage);
|
|
51
|
+
|
|
52
|
+
// Handle 4xx and 5xx errors
|
|
53
|
+
if (status >= 400 && status <= 500) {
|
|
54
|
+
if (handleError) {
|
|
55
|
+
handleError({
|
|
56
|
+
status,
|
|
57
|
+
statusText: message || statusText,
|
|
58
|
+
data: errors || []
|
|
59
|
+
});
|
|
113
60
|
}
|
|
114
|
-
|
|
115
|
-
// Convert to a rejected promise so catch blocks handle it
|
|
116
|
-
const error = new Error(errorMessage);
|
|
117
|
-
error.response = {
|
|
118
|
-
data: response.data,
|
|
119
|
-
status: response.data.statusCode || response.data.response?.statusCode || 200,
|
|
120
|
-
statusText: errorMessage,
|
|
121
|
-
};
|
|
122
|
-
error.isEmbeddedError = true; // Flag to identify these special errors
|
|
123
|
-
|
|
124
|
-
return Promise.reject(error);
|
|
125
61
|
}
|
|
126
62
|
|
|
127
|
-
return response;
|
|
128
|
-
},
|
|
129
|
-
onError: (error) => {
|
|
130
63
|
return Promise.reject(error);
|
|
131
|
-
}
|
|
132
|
-
};
|
|
64
|
+
};
|
|
133
65
|
};
|
|
134
66
|
|
|
135
67
|
/**
|
|
@@ -137,29 +69,29 @@ export const createResponseInterceptor = (config = {}) => {
|
|
|
137
69
|
* Useful for quick error notifications
|
|
138
70
|
*/
|
|
139
71
|
export const handleError = (err) => {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
export const handleSuccess = (msg) => {
|
|
150
|
-
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
export const handleWarning = (msg) => {
|
|
157
|
-
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
export const handleInfo = (msg) => {
|
|
164
|
-
|
|
165
|
-
};
|
|
72
|
+
const errorMessage = err?.response?.data?.message ||
|
|
73
|
+
err?.message ||
|
|
74
|
+
'An error occurred';
|
|
75
|
+
message.error(errorMessage);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Success message handler
|
|
80
|
+
*/
|
|
81
|
+
export const handleSuccess = (msg) => {
|
|
82
|
+
message.success(msg || 'Operation successful');
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Warning message handler
|
|
87
|
+
*/
|
|
88
|
+
export const handleWarning = (msg) => {
|
|
89
|
+
message.warning(msg || 'Warning');
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Info message handler
|
|
94
|
+
*/
|
|
95
|
+
export const handleInfo = (msg) => {
|
|
96
|
+
message.info(msg);
|
|
97
|
+
};
|
package/src/utils.js
CHANGED
|
@@ -46,7 +46,7 @@ export { StorageManager } from './helpers/StorageManager.js';
|
|
|
46
46
|
|
|
47
47
|
export { assignParamsToUrl, buildQueryString } from './helpers/urlHelpers.js';
|
|
48
48
|
|
|
49
|
-
export { createErrorHandler, handleError, handleSuccess, handleWarning, handleInfo
|
|
49
|
+
export { createErrorHandler, handleError, handleSuccess, handleWarning, handleInfo } from './helpers/errorHandling.js';
|
|
50
50
|
|
|
51
51
|
export { createTheme, createAdminTheme } from './helpers/theme.js';
|
|
52
52
|
|