datastake-daf 0.6.809 → 0.6.811

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 (67) 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 +1513 -1795
  7. package/dist/hooks/index.js +4 -11
  8. package/dist/layouts/index.js +484 -460
  9. package/dist/pages/index.js +1746 -753
  10. package/dist/services/index.js +5 -19
  11. package/dist/style/datastake/mapbox-gl.css +330 -0
  12. package/dist/utils/index.js +489 -484
  13. package/package.json +1 -1
  14. package/public/Vegetation/damage-from-insects-default.svg +2 -0
  15. package/public/Vegetation/dry-or-dead-default.svg +2 -0
  16. package/public/Vegetation/healthy-default.svg +2 -0
  17. package/public/Vegetation/yellowing.svg +2 -0
  18. package/src/@daf/core/components/AuthForm/index.jsx +3 -12
  19. package/src/@daf/core/components/Icon/configs/Droplets.js +9 -0
  20. package/src/@daf/core/components/Icon/configs/TrendUp.js +8 -0
  21. package/src/@daf/core/components/Icon/configs/index.js +4 -0
  22. package/src/@daf/core/components/Screens/Admin/AdminDashboard/components/UserStatistics/TopContributors/index.jsx +1 -0
  23. package/src/@daf/core/components/Screens/Admin/AdminDashboard/components/UserStatistics/UserGrowth/hook.js +1 -0
  24. package/src/@daf/core/components/Screens/Admin/AdminDashboard/components/UserStatistics/UserGrowth/index.jsx +3 -1
  25. package/src/@daf/core/components/Screens/Admin/AdminModals/CombineLocation/index.jsx +51 -51
  26. package/src/@daf/core/components/Screens/Admin/AdminModals/CombineSubjects/index.jsx +1 -6
  27. package/src/@daf/core/components/Screens/Admin/AdminModals/NewAccount/index.jsx +12 -47
  28. package/src/@daf/core/components/Screens/Admin/AdminModals/NewUser/index.jsx +10 -36
  29. package/src/@daf/core/components/Screens/Admin/AdminModals/TransferRights/index.jsx +1 -1
  30. package/src/@daf/core/components/Screens/Admin/AdminScreens/Accounts.jsx +0 -1
  31. package/src/@daf/core/components/Screens/Admin/AdminScreens/Dashboard.jsx +2 -2
  32. package/src/@daf/core/components/Screens/Admin/AdminTables/AccountTable/helper.js +30 -22
  33. package/src/@daf/core/components/Screens/Admin/AdminTables/AccountTable/index.jsx +3 -4
  34. package/src/@daf/core/components/Screens/Admin/AdminTables/LocationTable/index.jsx +2 -6
  35. package/src/@daf/core/components/Screens/Admin/AdminTables/SubjectsTable/index.jsx +12 -14
  36. package/src/@daf/core/components/Screens/Admin/AdminTables/UserTable/index.jsx +1 -0
  37. package/src/@daf/core/components/Screens/Admin/AdminTables/hook.js +0 -3
  38. package/src/@daf/core/components/Screens/Admin/AdminViews/components/Edit/index.jsx +9 -12
  39. package/src/@daf/core/components/Screens/Admin/AdminViews/components/Users/index.jsx +4 -16
  40. package/src/@daf/core/components/Screens/Admin/AdminViews/components/View/helpers.js +17 -9
  41. package/src/@daf/core/components/Screens/Admin/AdminViews/index.jsx +8 -9
  42. package/src/@daf/core/components/Screens/Admin/AppInvitation/index.jsx +99 -124
  43. package/src/@daf/hooks/useAdminDashboard.js +4 -7
  44. package/src/@daf/pages/Locations/MineSite/columns.js +18 -1
  45. package/src/@daf/pages/Locations/MineSite/config.js +2 -2
  46. package/src/@daf/pages/Summary/Activities/MonitoringCampaign/components/BiodiversityHabitat/InvasiveSpecies.jsx +107 -0
  47. package/src/@daf/pages/Summary/Activities/MonitoringCampaign/components/BiodiversityHabitat/ObservedFauna.jsx +80 -0
  48. package/src/@daf/pages/Summary/Activities/MonitoringCampaign/components/BiodiversityHabitat/Stats.jsx +62 -0
  49. package/src/@daf/pages/Summary/Activities/MonitoringCampaign/components/BiodiversityHabitat/index.jsx +109 -0
  50. package/src/@daf/pages/Summary/Activities/MonitoringCampaign/components/KeyInformation/index.jsx +8 -4
  51. package/src/@daf/pages/Summary/Activities/MonitoringCampaign/components/MangroveGrowth/PlantedSpecies.jsx +21 -9
  52. package/src/@daf/pages/Summary/Activities/MonitoringCampaign/components/MangroveGrowth/SeedlingsHeight.jsx +36 -2
  53. package/src/@daf/pages/Summary/Activities/MonitoringCampaign/components/MangroveGrowth/Stats.jsx +3 -3
  54. package/src/@daf/pages/Summary/Activities/MonitoringCampaign/components/MangroveGrowth/VegetationHealth.jsx +19 -8
  55. package/src/@daf/pages/Summary/Activities/MonitoringCampaign/components/MangroveGrowth/index.jsx +13 -9
  56. package/src/@daf/pages/Summary/Activities/MonitoringCampaign/components/SoilWaterProfile/SalinityLevels.jsx +100 -0
  57. package/src/@daf/pages/Summary/Activities/MonitoringCampaign/components/SoilWaterProfile/SoilType.jsx +84 -0
  58. package/src/@daf/pages/Summary/Activities/MonitoringCampaign/components/SoilWaterProfile/Stats.jsx +72 -0
  59. package/src/@daf/pages/Summary/Activities/MonitoringCampaign/components/SoilWaterProfile/WaterQuality.jsx +84 -0
  60. package/src/@daf/pages/Summary/Activities/MonitoringCampaign/components/SoilWaterProfile/index.jsx +101 -0
  61. package/src/@daf/pages/Summary/Activities/MonitoringCampaign/components/chartHelpers.js +102 -0
  62. package/src/@daf/pages/Summary/Activities/MonitoringCampaign/index.jsx +8 -2
  63. package/src/@daf/services/AdminService.js +4 -15
  64. package/src/@daf/services/DashboardService.js +3 -3
  65. package/src/@daf/utils/filters.js +15 -13
  66. package/src/@daf/utils/numbers.js +1 -1
  67. package/src/constants/locales/en/translation.js +0 -9
@@ -0,0 +1,107 @@
1
+ import React, { useMemo } from 'react';
2
+ import { Widget, RadarChart } from '../../../../../../../../src/index.js';
3
+
4
+ // Define the specific 5 categories to show in radar chart
5
+ const RADAR_CATEGORIES = ['spiders', 'scaleInsects', 'caterpillars', 'ants', 'unidentifiedPests'];
6
+
7
+ // Custom labels for the radar chart
8
+ const RADAR_LABELS = {
9
+ 'spiders': 'Spiders',
10
+ 'scaleInsects': 'Scale insects',
11
+ 'caterpillars': 'Caterpillars',
12
+ 'ants': 'Ants',
13
+ 'unidentifiedPests': 'Unidentified pests'
14
+ };
15
+
16
+ const InvasiveSpecies = ({
17
+ invasiveSpeciesChart,
18
+ t = (s) => s,
19
+ options = {}
20
+ }) => {
21
+
22
+ const chartData = useMemo(() => {
23
+ const speciesOptions = options?.invasiveSpecies || [];
24
+
25
+ // Create a map of existing data
26
+ const dataMap = new Map();
27
+ if (invasiveSpeciesChart && Array.isArray(invasiveSpeciesChart)) {
28
+ invasiveSpeciesChart.forEach(item => {
29
+ if (item?.invasiveSpecies) {
30
+ dataMap.set(item.invasiveSpecies, Number(item?.count) || 0);
31
+ }
32
+ });
33
+ }
34
+
35
+ // Create chart data for all 5 radar categories
36
+ const data = RADAR_CATEGORIES.map((categoryValue) => {
37
+ return {
38
+ item: RADAR_LABELS[categoryValue] || categoryValue,
39
+ score: dataMap.get(categoryValue) || 0,
40
+ invasiveSpecies: categoryValue
41
+ };
42
+ });
43
+
44
+ return data;
45
+ }, [invasiveSpeciesChart, options]);
46
+
47
+ const maxScore = useMemo(() => {
48
+ if (!chartData || chartData.length === 0) return 100;
49
+ const max = Math.max(...chartData.map(item => item.score || 0));
50
+ return max > 0 ? Math.ceil(max * 1.2) : 100;
51
+ }, [chartData]);
52
+
53
+ const isEmpty = useMemo(() => {
54
+ // Never empty since we always show 5 categories
55
+ return false;
56
+ }, []);
57
+
58
+ console.log('InvasiveSpecies - chartData:', chartData);
59
+ return (
60
+ <Widget
61
+ title={t("Invasive Species")}
62
+ className="with-border-header h-w-btn-header"
63
+ >
64
+
65
+ {isEmpty ? (
66
+ <div style={{
67
+ display: 'flex',
68
+ alignItems: 'center',
69
+ justifyContent: 'center',
70
+ height: '100%',
71
+ color: '#999'
72
+ }}>
73
+ {t("No data available")}
74
+ </div>
75
+ ) : (
76
+ <RadarChart
77
+ data={chartData}
78
+ xFieldKey="item"
79
+ yFieldKey="score"
80
+ animated={true}
81
+ height={400}
82
+ score={{
83
+ max: maxScore,
84
+ min: 0
85
+ }}
86
+ renderTooltipContent={(title, data) => {
87
+ if (!data || data.length === 0) return {};
88
+ const item = data[0]?.data || data[0];
89
+ return {
90
+ title: title,
91
+ items: [
92
+ {
93
+ label: t("Sightings"),
94
+ value: item?.score || 0,
95
+ },
96
+ ],
97
+ };
98
+ }}
99
+ />
100
+ )}
101
+
102
+ </Widget>
103
+ );
104
+ };
105
+
106
+ export default InvasiveSpecies;
107
+
@@ -0,0 +1,80 @@
1
+ import React, { useMemo } from 'react';
2
+ import { Widget, BarChart } from '../../../../../../../../src/index.js';
3
+ import { calculateNiceAxisConfig, mergeDefaultCategories } from '../chartHelpers.js';
4
+
5
+ const ObservedFauna = ({
6
+ observedFaunaChart,
7
+ t = (s) => s,
8
+ options = {}
9
+ }) => {
10
+ const chartData = useMemo(() => {
11
+ const allFaunaTypes = options?.faunaObserved || [];
12
+
13
+ if (allFaunaTypes.length === 0) {
14
+ return [];
15
+ }
16
+
17
+ const mergedData = mergeDefaultCategories(
18
+ observedFaunaChart,
19
+ allFaunaTypes,
20
+ 'fauna',
21
+ 'count',
22
+ 'label',
23
+ 'value'
24
+ );
25
+
26
+ // Rename 'label' to 'type' for BarChart compatibility
27
+ return mergedData.map(item => ({
28
+ type: item.label,
29
+ value: item.value,
30
+ fauna: item.fauna
31
+ }));
32
+ }, [observedFaunaChart, options]);
33
+
34
+ const xAxisConfig = useMemo(() => {
35
+ return calculateNiceAxisConfig(
36
+ chartData,
37
+ 'value',
38
+ 1.2, // multiplier: 20% padding
39
+ {
40
+ min: 0,
41
+ max: 10,
42
+ tickMethod: () => [0, 2, 4, 6, 8, 10]
43
+ }
44
+ );
45
+ }, [chartData]);
46
+
47
+ return (
48
+ <Widget
49
+ title={t("Observed Fauna")}
50
+ className="with-border-header h-w-btn-header"
51
+ >
52
+ <BarChart
53
+ data={chartData}
54
+ yFieldKey="type"
55
+ xFieldKey="value"
56
+ animated={true}
57
+ color="#00AEB1"
58
+ xAxisConfig={xAxisConfig}
59
+ renderTooltipContent={(title, data) => {
60
+ if (!data || data.length === 0) return {};
61
+ const item = data[0]?.data || data[0];
62
+ return {
63
+ title: title,
64
+ subTitle: t("Sightings"),
65
+ items: [
66
+ {
67
+ label: t("Count"),
68
+ value: item?.value || 0,
69
+ },
70
+ ],
71
+ };
72
+ }}
73
+ />
74
+
75
+ </Widget>
76
+ );
77
+ };
78
+
79
+ export default ObservedFauna;
80
+
@@ -0,0 +1,62 @@
1
+ import React, { useMemo } from 'react';
2
+ import { StatCard } from '../../../../../../../../src/index.js';
3
+ import { calculateStatChange } from '../../../../../../utils/numbers.js';
4
+
5
+ const Stats = ({
6
+ faunaSightings,
7
+ invasiveSpeciesSightings,
8
+ t = (s) => s
9
+ }) => {
10
+ const faunaSightingsChange = useMemo(() => {
11
+ if (!faunaSightings) return null;
12
+ return calculateStatChange(
13
+ {
14
+ current: Number(faunaSightings.current) || 0,
15
+ previous: Number(faunaSightings.previous) || 0,
16
+ },
17
+ {
18
+ tooltipText: t("In comparison to last period"),
19
+ format: 'absolute',
20
+ }
21
+ );
22
+ }, [faunaSightings, t]);
23
+
24
+ const invasiveSpeciesSightingsChange = useMemo(() => {
25
+ if (!invasiveSpeciesSightings) return null;
26
+ return calculateStatChange(
27
+ {
28
+ current: Number(invasiveSpeciesSightings.current) || 0,
29
+ previous: Number(invasiveSpeciesSightings.previous) || 0,
30
+ },
31
+ {
32
+ tooltipText: t("In comparison to last period"),
33
+ format: 'absolute',
34
+ }
35
+ );
36
+ }, [invasiveSpeciesSightings, t]);
37
+
38
+ return (
39
+ <div style={{ display: "flex", gap: "24px", marginBottom: "24px" }}>
40
+ <section style={{ flex: 1 }}>
41
+ <StatCard
42
+ title={t("Fauna Sightings")}
43
+ value={faunaSightings ? Number(faunaSightings.current).toLocaleString() : "0"}
44
+ icon="Fish"
45
+ change={faunaSightingsChange}
46
+ />
47
+ </section>
48
+
49
+ <section style={{ flex: 1 }}>
50
+ <StatCard
51
+ title={t("Invasive Species Sightings")}
52
+ value={invasiveSpeciesSightings ? Number(invasiveSpeciesSightings.current).toLocaleString() : "0"}
53
+ icon="Ant"
54
+ change={invasiveSpeciesSightingsChange}
55
+ />
56
+ </section>
57
+ </div>
58
+ );
59
+ };
60
+
61
+ export default Stats;
62
+
@@ -0,0 +1,109 @@
1
+ import React, { useMemo } from 'react';
2
+ import { Widget } from '../../../../../../../../src/index.js';
3
+ import { useWidgetFetch } from '../../../../../../hooks/useWidgetFetch.js';
4
+ import Stats from './Stats.jsx';
5
+ import ObservedFauna from './ObservedFauna.jsx';
6
+ import InvasiveSpecies from './InvasiveSpecies.jsx';
7
+
8
+ const BiodiversityHabitat = ({
9
+ id,
10
+ getSummaryDetail,
11
+ loading = false,
12
+ t = (s) => s,
13
+ options = {}
14
+ }) => {
15
+ const defaultConfig = useMemo(
16
+ () => ({
17
+ basepath: "events/monitoring-campaign-summary",
18
+ url: `/summary/${id}/biodiversity-habitat`,
19
+ stop: !id,
20
+ }),
21
+ [id],
22
+ );
23
+
24
+ const customGetData = useMemo(() => {
25
+ if (getSummaryDetail && id) {
26
+ return ({ url, params = {} }) => {
27
+ const match = url.match(/\/summary\/[^/]+\/(.+)/);
28
+ if (match) {
29
+ const [, type] = match;
30
+ return getSummaryDetail(id, type, params);
31
+ }
32
+ throw new Error(`Invalid URL format: ${url}`);
33
+ };
34
+ }
35
+ return undefined;
36
+ }, [getSummaryDetail, id]);
37
+
38
+ const { loading: outcomesLoading, data: outcomesData } = useWidgetFetch({
39
+ config: defaultConfig,
40
+ getData: customGetData
41
+ });
42
+
43
+ const {
44
+ faunaSightings,
45
+ invasiveSpecies,
46
+ previousCycleFaunaSightings,
47
+ previousCycleInvasiveSpecies,
48
+ observedFauna
49
+ } = outcomesData || {};
50
+
51
+
52
+ const transformedFaunaSightings = useMemo(() => {
53
+ const current = Array.isArray(faunaSightings) && faunaSightings.length > 0
54
+ ? faunaSightings.reduce((sum, item) => sum + (Number(item?.count) || 0), 0)
55
+ : 0;
56
+ const previous = Array.isArray(previousCycleFaunaSightings) && previousCycleFaunaSightings.length > 0
57
+ ? previousCycleFaunaSightings.reduce((sum, item) => sum + (Number(item?.count) || 0), 0)
58
+ : 0;
59
+ return { current, previous };
60
+ }, [faunaSightings, previousCycleFaunaSightings]);
61
+
62
+ const transformedInvasiveSpecies = useMemo(() => {
63
+ const current = Array.isArray(invasiveSpecies) && invasiveSpecies.length > 0
64
+ ? invasiveSpecies.reduce((sum, item) => sum + (Number(item?.count) || 0), 0)
65
+ : 0;
66
+ const previous = Array.isArray(previousCycleInvasiveSpecies) && previousCycleInvasiveSpecies.length > 0
67
+ ? previousCycleInvasiveSpecies.reduce((sum, item) => sum + (Number(item?.count) || 0), 0)
68
+ : 0;
69
+ return { current, previous };
70
+ }, [invasiveSpecies, previousCycleInvasiveSpecies]);
71
+
72
+
73
+ return (
74
+ <section>
75
+ <Widget
76
+ title={t("Biodiversity & Habitat")}
77
+ loading={loading || outcomesLoading}
78
+ className="with-border-header h-w-btn-header"
79
+ >
80
+ <Stats
81
+ faunaSightings={transformedFaunaSightings}
82
+ invasiveSpeciesSightings={transformedInvasiveSpecies}
83
+ t={t}
84
+ />
85
+
86
+ <div style={{ display: "flex", gap: "24px" }}>
87
+ <section style={{ flex: 1 }}>
88
+ <ObservedFauna
89
+ observedFaunaChart={observedFauna}
90
+ t={t}
91
+ options={options}
92
+ />
93
+ </section>
94
+
95
+ <InvasiveSpecies
96
+ invasiveSpeciesChart={invasiveSpecies}
97
+ t={t}
98
+ options={options}
99
+ />
100
+
101
+ </div>
102
+ </Widget>
103
+
104
+ </section>
105
+ );
106
+ };
107
+
108
+ export default BiodiversityHabitat;
109
+
@@ -7,8 +7,8 @@ const KeyInformation = ({ id, t = () => { }, getSummaryDetail, loading = false }
7
7
 
8
8
  const defaultConfig = useMemo(
9
9
  () => ({
10
- basepath: "events/monitoring-campaign",
11
- url: `/summary/${id}/key-information`,
10
+ basepath: "events/monitoring-campaign-summary",
11
+ url: `/${id}/key-information`,
12
12
  stop: !id,
13
13
  }),
14
14
  [id],
@@ -16,10 +16,14 @@ const KeyInformation = ({ id, t = () => { }, getSummaryDetail, loading = false }
16
16
 
17
17
  const customGetData = useMemo(() => {
18
18
  if (getSummaryDetail && id) {
19
- return ({ url, params = {} }) => {
20
- const match = url.match(/\/summary\/[^/]+\/(.+)/);
19
+ return (rest) => {
20
+ const { url, filters: restFilters } = rest;
21
+ const match = url.match(/\/[^/]+\/(.+)/);
21
22
  if (match) {
22
23
  const [, type] = match;
24
+ const params = {
25
+ ...(restFilters || {}),
26
+ };
23
27
  return getSummaryDetail(id, type, params);
24
28
  }
25
29
  throw new Error(`Invalid URL format: ${url}`);
@@ -2,28 +2,40 @@ 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
- const COLORS = ['#016C6E', '#F5C2AC', '#F0A888', '#DF571E', '#C04B19', '#9B3D14', '#7A2F0F'];
5
+ const COLORS = ['#6AD99E', '#F5C2AC', '#F0A888', '#DF571E', '#C04B19', '#9B3D14', '#7A2F0F'];
6
6
 
7
7
  const PlantedSpecies = ({
8
8
  plantedSpeciesChart,
9
- t = (s) => s
9
+ t = (s) => s,
10
+ options = {}
10
11
  }) => {
12
+
13
+ const optionsMap = useMemo(() => {
14
+ if (!options || !Array.isArray(options)) return {};
15
+ return options.reduce((acc, option) => {
16
+ if (option?.value) {
17
+ acc[option.value] = option.label;
18
+ }
19
+ return acc;
20
+ }, {});
21
+ }, [options]);
22
+
11
23
  const pieData = useMemo(() => {
12
24
  const data = plantedSpeciesChart || [];
13
- const total = data.reduce((sum, item) => sum + (Number(item?.value) || 0), 0);
25
+ const total = data.reduce((sum, item) => sum + (Number(item?.count) || 0), 0);
14
26
 
15
27
  return data.map((item, index) => ({
16
- value: Number(item?.value) || 0,
17
- percent: total > 0 ? (Number(item?.value) || 0) / total : 0,
28
+ value: Number(item?.count) || 0,
29
+ percent: total > 0 ? (Number(item?.count) || 0) / total : 0,
18
30
  color: COLORS[index % COLORS.length],
19
- label: item?.type || '',
20
- key: item?.type || `item-${index}`,
31
+ label: optionsMap[item?.name] || item?.name || '',
32
+ key: item?.name || `item-${index}`,
21
33
  }));
22
- }, [plantedSpeciesChart]);
34
+ }, [plantedSpeciesChart, optionsMap]);
23
35
 
24
36
  const isEmpty = useMemo(() => {
25
37
  return !plantedSpeciesChart || plantedSpeciesChart.length === 0 ||
26
- plantedSpeciesChart.every(item => !item?.value || Number(item.value) === 0);
38
+ plantedSpeciesChart.every(item => !item?.count || Number(item.count) === 0);
27
39
  }, [plantedSpeciesChart]);
28
40
 
29
41
  const getTooltipChildren = useCallback(
@@ -1,10 +1,43 @@
1
- import React from 'react';
1
+ import React, { useMemo } from 'react';
2
2
  import { Widget, ColumnChart } from '../../../../../../../../src/index.js';
3
+ import { calculateNiceAxisConfig, mergeDefaultCategories } from '../chartHelpers.js';
4
+
5
+ // Default height ranges to always display
6
+ const DEFAULT_HEIGHT_RANGES = [
7
+ { value: '0-10', label: '0-10 cm' },
8
+ { value: '11-20', label: '11-20 cm' },
9
+ { value: '21-30', label: '21-30 cm' },
10
+ { value: '30+', label: '30+ cm' }
11
+ ];
3
12
 
4
13
  const SeedlingsHeight = ({
5
14
  seedlingsHeightChart,
6
15
  t = (s) => s
7
16
  }) => {
17
+ const chartData = useMemo(() => {
18
+ return mergeDefaultCategories(
19
+ seedlingsHeightChart,
20
+ DEFAULT_HEIGHT_RANGES,
21
+ 'range',
22
+ 'count',
23
+ 'label',
24
+ 'value'
25
+ );
26
+ }, [seedlingsHeightChart]);
27
+
28
+ const yAxisConfig = useMemo(() => {
29
+ return calculateNiceAxisConfig(
30
+ chartData,
31
+ 'value',
32
+ 2, // multiplier: double the max value
33
+ {
34
+ min: 0,
35
+ max: 20,
36
+ tickMethod: () => [0, 5, 10, 15, 20]
37
+ }
38
+ );
39
+ }, [chartData]);
40
+
8
41
  return (
9
42
  <Widget
10
43
  title={t("Seedlings Height")}
@@ -13,12 +46,13 @@ const SeedlingsHeight = ({
13
46
  <div className="flex flex-1 flex-column justify-content-center">
14
47
  <div className="flex justify-content-center w-full">
15
48
  <ColumnChart
16
- data={seedlingsHeightChart || []}
49
+ data={chartData}
17
50
  xFieldKey="label"
18
51
  yFieldKey="value"
19
52
  animated={true}
20
53
  height={200}
21
54
  color="#016C6E"
55
+ yAxis={yAxisConfig}
22
56
  renderTooltipContent={(title, data) => {
23
57
  if (!data || data.length === 0) return {};
24
58
  const item = data[0]?.data || data[0];
@@ -56,7 +56,7 @@ const Stats = ({
56
56
  <StatCard
57
57
  title={t("Survival Rate")}
58
58
  value={survivalRate ? Number(survivalRate.current).toLocaleString() : "0"}
59
- icon="EventCalendar"
59
+ icon="TrendUp"
60
60
  change={survivalRateChange}
61
61
  />
62
62
  </section>
@@ -65,7 +65,7 @@ const Stats = ({
65
65
  <StatCard
66
66
  title={t("Average Height")}
67
67
  value={averageHeight ? Number(averageHeight.current).toLocaleString() + " cm" : "0 cm"}
68
- icon="ProjectLocation"
68
+ icon="SpacingHeight"
69
69
  change={averageHeightChange}
70
70
  />
71
71
  </section>
@@ -74,7 +74,7 @@ const Stats = ({
74
74
  <StatCard
75
75
  title={t("Average Diameter")}
76
76
  value={averageDiameter ? Number(averageDiameter.current).toLocaleString() + " mm" : "0 mm"}
77
- icon="Activity"
77
+ icon="Tree"
78
78
  change={averageDiameterChange}
79
79
  />
80
80
  </section>
@@ -6,24 +6,35 @@ const COLORS = ['#016C6E', '#F5C2AC', '#F0A888', '#DF571E', '#C04B19', '#9B3D14'
6
6
 
7
7
  const VegetationHealth = ({
8
8
  vegetationHealthChart,
9
- t = (s) => s
9
+ t = (s) => s,
10
+ options = {}
10
11
  }) => {
12
+ const optionsMap = useMemo(() => {
13
+ if (!options || !Array.isArray(options)) return {};
14
+ return options.reduce((acc, option) => {
15
+ if (option?.value) {
16
+ acc[option.value] = option.label;
17
+ }
18
+ return acc;
19
+ }, {});
20
+ }, [options]);
21
+
11
22
  const pieData = useMemo(() => {
12
23
  const data = vegetationHealthChart || [];
13
- const total = data.reduce((sum, item) => sum + (Number(item?.value) || 0), 0);
24
+ const total = data.reduce((sum, item) => sum + (Number(item?.count) || 0), 0);
14
25
 
15
26
  return data.map((item, index) => ({
16
- value: Number(item?.value) || 0,
17
- percent: total > 0 ? (Number(item?.value) || 0) / total : 0,
27
+ value: Number(item?.count) || 0,
28
+ percent: total > 0 ? (Number(item?.count) || 0) / total : 0,
18
29
  color: COLORS[index % COLORS.length],
19
- label: item?.type || '',
20
- key: item?.type || `item-${index}`,
30
+ label: optionsMap[item?.name] || item?.name || '',
31
+ key: item?.name || `item-${index}`,
21
32
  }));
22
- }, [vegetationHealthChart]);
33
+ }, [vegetationHealthChart, optionsMap]);
23
34
 
24
35
  const isEmpty = useMemo(() => {
25
36
  return !vegetationHealthChart || vegetationHealthChart.length === 0 ||
26
- vegetationHealthChart.every(item => !item?.value || Number(item.value) === 0);
37
+ vegetationHealthChart.every(item => !item?.count || Number(item.count) === 0);
27
38
  }, [vegetationHealthChart]);
28
39
 
29
40
  const getTooltipChildren = useCallback(
@@ -10,12 +10,13 @@ const MangroveGrowth = ({
10
10
  id,
11
11
  getSummaryDetail,
12
12
  loading = false,
13
- t = (s) => s
13
+ t = (s) => s,
14
+ options = {}
14
15
  }) => {
15
16
  const defaultConfig = useMemo(
16
17
  () => ({
17
- basepath: "events/monitoring-campaign",
18
- url: `/summary/${id}/outcomes`,
18
+ basepath: "events/monitoring-campaign-summary",
19
+ url: `/summary/${id}/mangrove-growth`,
19
20
  stop: !id,
20
21
  }),
21
22
  [id],
@@ -44,9 +45,9 @@ const MangroveGrowth = ({
44
45
  survivalRate,
45
46
  averageHeight,
46
47
  averageDiameter,
47
- vegetationHealthChart,
48
- seedlingsHeightChart,
49
- plantedSpeciesChart
48
+ vegetationHealth,
49
+ seedlingsTimelineChart,
50
+ plantedSpecies
50
51
  } = outcomesData || {};
51
52
 
52
53
  return (
@@ -66,20 +67,23 @@ const MangroveGrowth = ({
66
67
  <div style={{ display: "flex", gap: "24px" }}>
67
68
  <section style={{ flex: 1 }}>
68
69
  <VegetationHealth
69
- vegetationHealthChart={vegetationHealthChart}
70
+ vegetationHealthChart={vegetationHealth}
70
71
  t={t}
72
+ options={options.growthObservations}
71
73
  />
72
74
  </section>
73
75
  <section style={{ flex: 1 }}>
74
76
  <SeedlingsHeight
75
- seedlingsHeightChart={seedlingsHeightChart}
77
+ seedlingsHeightChart={seedlingsTimelineChart}
76
78
  t={t}
79
+ options={options.seedlingsHeight}
77
80
  />
78
81
  </section>
79
82
  <section style={{ flex: 1 }}>
80
83
  <PlantedSpecies
81
- plantedSpeciesChart={plantedSpeciesChart}
84
+ plantedSpeciesChart={plantedSpecies}
82
85
  t={t}
86
+ options={options.mangroveSpecies}
83
87
  />
84
88
  </section>
85
89
  </div>