datastake-daf 0.6.787 → 0.6.788

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 (62) 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 +579 -722
  7. package/dist/pages/index.js +774 -33
  8. package/dist/services/index.js +1 -10
  9. package/dist/style/datastake/mapbox-gl.css +330 -0
  10. package/dist/utils/index.js +12 -28
  11. package/package.json +1 -1
  12. package/src/@daf/core/components/Dashboard/Widget/ActivityIndicators/ActivityIndicators.stories.js +24 -0
  13. package/src/@daf/core/components/Dashboard/Widget/ActivityIndicators/index.jsx +1 -0
  14. package/src/@daf/core/components/Dashboard/Widget/ActivityIndicators/style.js +34 -0
  15. package/src/@daf/core/components/Dashboard/Widget/KeyIndicators/KeyIndicators.stories.js +39 -0
  16. package/src/@daf/core/components/Dashboard/Widget/KeyIndicators/LabelWithIcon.jsx +38 -0
  17. package/src/@daf/core/components/Dashboard/Widget/KeyIndicators/index.jsx +16 -3
  18. package/src/@daf/core/components/Dashboard/Widget/KeyIndicators/style.js +33 -0
  19. package/src/@daf/core/components/Screens/Admin/AdminDashboard/components/UserStatistics/TopContributors/hook.js +1 -0
  20. package/src/@daf/core/components/Screens/Admin/AdminDashboard/components/UserStatistics/TopContributors/index.jsx +1 -1
  21. package/src/@daf/core/components/Screens/Admin/AdminDashboard/components/UserStatistics/UserGrowth/hook.js +1 -1
  22. package/src/@daf/core/components/Screens/Admin/AdminDashboard/components/UserStatistics/UserGrowth/index.jsx +1 -1
  23. package/src/@daf/core/components/Screens/Admin/AdminModals/AddUser/index.jsx +1 -0
  24. package/src/@daf/core/components/Screens/Admin/AdminModals/CombineLocation/index.jsx +1 -1
  25. package/src/@daf/core/components/Screens/Admin/AdminModals/NewAccount/index.jsx +1 -1
  26. package/src/@daf/core/components/Screens/Admin/AdminScreens/Dashboard.jsx +2 -2
  27. package/src/@daf/core/components/Screens/Admin/AdminScreens/Location.jsx +5 -5
  28. package/src/@daf/core/components/Screens/Admin/AdminScreens/Subjects.jsx +2 -2
  29. package/src/@daf/core/components/Screens/Admin/AdminTables/LocationTable/index.jsx +5 -23
  30. package/src/@daf/core/components/Screens/Admin/AdminTables/SubjectsTable/columns.js +36 -36
  31. package/src/@daf/core/components/Screens/Admin/AdminTables/SubjectsTable/index.jsx +21 -12
  32. package/src/@daf/core/components/Screens/Admin/AdminViews/components/Users/helper.js +8 -13
  33. package/src/@daf/core/components/Screens/Admin/AdminViews/components/Users/index.jsx +2 -2
  34. package/src/@daf/core/components/Screens/Admin/adminRoutes.js +2 -2
  35. package/src/@daf/pages/Summary/Activities/Monitoring/components/ActivityImagery/index.jsx +29 -0
  36. package/src/@daf/pages/Summary/Activities/Monitoring/components/ActivityLocation/index.jsx +94 -0
  37. package/src/@daf/pages/Summary/Activities/Monitoring/components/MangroveGrowthAndSurvival/components/PlantedSpecies/index.jsx +56 -0
  38. package/src/@daf/pages/Summary/Activities/Monitoring/components/MangroveGrowthAndSurvival/components/SeedlingsHeight/index.jsx +121 -0
  39. package/src/@daf/pages/Summary/Activities/Monitoring/components/MangroveGrowthAndSurvival/components/SurvivalRate/index.jsx +94 -0
  40. package/src/@daf/pages/Summary/Activities/Monitoring/components/MangroveGrowthAndSurvival/index.jsx +54 -0
  41. package/src/@daf/pages/Summary/Activities/Monitoring/components/WorkersDistribution/index.jsx +49 -0
  42. package/src/@daf/pages/Summary/Activities/Monitoring/config.js +51 -0
  43. package/src/@daf/pages/Summary/Activities/Monitoring/helper.js +236 -0
  44. package/src/@daf/pages/Summary/Activities/Monitoring/index.jsx +66 -0
  45. package/src/@daf/services/AdminService.js +3 -12
  46. package/src/@daf/services/DashboardService.js +1 -2
  47. package/src/@daf/utils/filters.js +89 -89
  48. package/src/constants/locales/en/translation.js +0 -3
  49. package/src/constants/locales/fr/translation.js +1 -1
  50. package/src/constants/locales/sp/translation.js +1 -1
  51. package/src/pages.js +1 -0
  52. package/src/@daf/core/components/Screens/Admin/AdminTables/LocationTable/column.js +0 -224
  53. package/src/@daf/core/components/Screens/Admin/AdminTables/LocationTable/helper.js +0 -65
  54. package/src/@daf/pages/SelfAssesment/components/AssociatedInformationTable/columns.js +0 -180
  55. package/src/@daf/pages/SelfAssesment/components/AssociatedInformationTable/config.js +0 -0
  56. package/src/@daf/pages/SelfAssesment/components/AssociatedInformationTable/index.jsx +0 -27
  57. package/src/@daf/pages/SelfAssesment/components/OrgInformationTable/columns.js +0 -157
  58. package/src/@daf/pages/SelfAssesment/components/OrgInformationTable/config.js +0 -31
  59. package/src/@daf/pages/SelfAssesment/components/OrgInformationTable/index.js +0 -77
  60. package/src/@daf/pages/SelfAssesment/components/ProductionSiteTable/columns.js +0 -117
  61. package/src/@daf/pages/SelfAssesment/components/ProductionSiteTable/index.jsx +0 -54
  62. package/src/@daf/pages/SelfAssesment/index.jsx +0 -0
@@ -0,0 +1,121 @@
1
+ import React, { useMemo } from 'react';
2
+ import { Widget, ColumnChart } from '../../../../../../../../../../src/index.js';
3
+
4
+ const SeedlingsHeight = ({
5
+ activityData,
6
+ loading = false,
7
+ t = (s) => s
8
+ }) => {
9
+ const heightData = useMemo(() => {
10
+ // Extract seedlings height from activityData
11
+ // Assuming the data might have averageHeight, seedlingsHeight, or similar
12
+ const averageHeight = activityData?.averageHeight ||
13
+ activityData?.seedlingsHeight ||
14
+ activityData?.mangroveAverageHeight ||
15
+ 28; // Default from image
16
+
17
+ const heightValue = typeof averageHeight === 'number'
18
+ ? averageHeight
19
+ : parseFloat(averageHeight) || 28;
20
+
21
+ return [
22
+ {
23
+ label: t("Average Height"),
24
+ value: heightValue
25
+ }
26
+ ];
27
+ }, [activityData, t]);
28
+
29
+ const renderTooltipContent = useMemo(() => {
30
+ return (title, data) => {
31
+ if (!data || data.length === 0) {
32
+ return {
33
+ title: t("Average Height"),
34
+ items: [
35
+ {
36
+ label: t("No Data"),
37
+ value: '-'
38
+ }
39
+ ]
40
+ };
41
+ }
42
+
43
+ const item = data[0];
44
+ const value = item?.value || item?.data?.value || 0;
45
+
46
+ return {
47
+ title: t("Average Height"),
48
+ items: [
49
+ {
50
+ label: t("Average Height"),
51
+ value: `${value.toFixed(1)}cm`,
52
+ color: '#00AEB1'
53
+ }
54
+ ]
55
+ };
56
+ };
57
+ }, [t]);
58
+
59
+ const isEmpty = useMemo(() => {
60
+ return !activityData?.averageHeight &&
61
+ !activityData?.seedlingsHeight &&
62
+ !activityData?.mangroveAverageHeight;
63
+ }, [activityData]);
64
+
65
+ return (
66
+ <Widget
67
+ loading={loading}
68
+ title={t("Seedlings Height")}
69
+ className="with-border-header h-w-btn-header"
70
+ >
71
+ <div
72
+ style={{
73
+ display: 'flex',
74
+ flexDirection: 'column',
75
+ minHeight: '250px',
76
+ padding: '20px 0'
77
+ }}
78
+ >
79
+ <ColumnChart
80
+ data={isEmpty ? [] : heightData}
81
+ xFieldKey="label"
82
+ yFieldKey="value"
83
+ color="#00AEB1"
84
+ height={250}
85
+ animated={true}
86
+ renderTooltipContent={renderTooltipContent}
87
+ yAxis={{
88
+ min: 0,
89
+ max: 35,
90
+ tickMethod: () => [0, 5, 10, 15, 20, 25, 30, 35],
91
+ label: {
92
+ formatter: (v) => `${v}cm`
93
+ }
94
+ }}
95
+ xAxis={{
96
+ label: {
97
+ formatter: () => ''
98
+ }
99
+ }}
100
+ tooltipConfig={{
101
+ showTitle: true
102
+ }}
103
+ />
104
+ {!isEmpty && (
105
+ <div style={{
106
+ marginTop: '16px',
107
+ textAlign: 'center',
108
+ fontSize: '14px',
109
+ color: '#475467',
110
+ fontWeight: 500
111
+ }}>
112
+ {t("Average Height")}: {heightData[0]?.value.toFixed(0)}cm
113
+ </div>
114
+ )}
115
+ </div>
116
+ </Widget>
117
+ );
118
+ };
119
+
120
+ export default SeedlingsHeight;
121
+
@@ -0,0 +1,94 @@
1
+ import React, { useMemo } from 'react';
2
+ import { Widget, DonutPie } from '../../../../../../../../../../src/index.js';
3
+
4
+ const SurvivalRate = ({
5
+ activityData,
6
+ loading = false,
7
+ t = (s) => s
8
+ }) => {
9
+ const survivalData = useMemo(() => {
10
+ // Extract survival rate from activityData
11
+ // Assuming the data might have survivalRate as a percentage (0-100)
12
+ const survivalRate = activityData?.survivalRate || activityData?.mangroveSurvivalRate || 78;
13
+ const survivalPercent = typeof survivalRate === 'number' ? survivalRate : parseFloat(survivalRate) || 78;
14
+
15
+ return [
16
+ {
17
+ type: t("Survival Rate"),
18
+ value: survivalPercent
19
+ },
20
+ {
21
+ type: t("Non-survival"),
22
+ value: 100 - survivalPercent
23
+ }
24
+ ];
25
+ }, [activityData, t]);
26
+
27
+ const renderTooltipContent = useMemo(() => {
28
+ return (title, data) => {
29
+ if (!data || data.length === 0) {
30
+ return {
31
+ title: t("Survival Rate"),
32
+ items: [
33
+ {
34
+ label: t("No Data"),
35
+ value: '-'
36
+ }
37
+ ]
38
+ };
39
+ }
40
+
41
+ const item = data[0];
42
+ const value = item?.value || item?.data?.value || 0;
43
+ const itemType = item?.type || item?.data?.type || t("Survival Rate");
44
+
45
+ // Determine color based on item type
46
+ const itemColor = itemType === t("Survival Rate") ? '#00AEB1' : '#E8EDF3';
47
+
48
+ return {
49
+ title: t("Survival Rate"),
50
+ items: [
51
+ {
52
+ label: itemType,
53
+ value: `${value.toFixed(1)}%`,
54
+ color: itemColor
55
+ }
56
+ ]
57
+ };
58
+ };
59
+ }, [t]);
60
+
61
+ const isEmpty = useMemo(() => {
62
+ return !activityData?.survivalRate && !activityData?.mangroveSurvivalRate;
63
+ }, [activityData]);
64
+
65
+ return (
66
+ <Widget
67
+ loading={loading}
68
+ title={t("Survival Rate")}
69
+ className="with-border-header h-w-btn-header"
70
+ >
71
+
72
+ {/* <DonutPie
73
+ angleField="value"
74
+ color={[
75
+ '#E8EDF3'
76
+ ]}
77
+ colorField="type"
78
+ data={[]}
79
+ height={200}
80
+ innerRadius={0.78}
81
+ radius={1}
82
+ renderTooltipContent={() => { }}
83
+ tooltip
84
+ tooltipConfig={{
85
+ showTitle: false
86
+ }}
87
+ /> */}
88
+
89
+ </Widget>
90
+ );
91
+ };
92
+
93
+ export default SurvivalRate;
94
+
@@ -0,0 +1,54 @@
1
+ import React from 'react';
2
+ import PlantedSpecies from './components/PlantedSpecies/index.jsx';
3
+ import SurvivalRate from './components/SurvivalRate/index.jsx';
4
+ import SeedlingsHeight from './components/SeedlingsHeight/index.jsx';
5
+ import styled from 'styled-components';
6
+ import { Widget } from '../../../../../../../../src/index.js';
7
+
8
+ const Container = styled.div`
9
+ display: grid;
10
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
11
+ gap: 24px;
12
+ width: 100%;
13
+
14
+ @media (max-width: 768px) {
15
+ grid-template-columns: 1fr;
16
+ }
17
+ `;
18
+
19
+ const MangroveGrowthAndSurvival = ({
20
+ activityData,
21
+ loading = false,
22
+ t = (s) => s
23
+ }) => {
24
+ return (
25
+
26
+ <Widget
27
+ title={t("Mangrove Growth & Survival")}
28
+ className="with-border-header h-w-btn-header"
29
+ >
30
+ <section>
31
+ <Container>
32
+ <PlantedSpecies
33
+ activityData={activityData}
34
+ loading={loading}
35
+ t={t}
36
+ />
37
+ <SurvivalRate
38
+ activityData={activityData}
39
+ loading={loading}
40
+ t={t}
41
+ />
42
+ <SeedlingsHeight
43
+ activityData={activityData}
44
+ loading={loading}
45
+ t={t}
46
+ />
47
+ </Container>
48
+ </section>
49
+ </Widget>
50
+ );
51
+ };
52
+
53
+ export default MangroveGrowthAndSurvival;
54
+
@@ -0,0 +1,49 @@
1
+ import React, { useMemo, useCallback } from 'react';
2
+ import { Widget, PieChart } from '../../../../../../../../src/index.js';
3
+ import { getGenderDistributionData, isGenderDistributionEmpty, calculateGenderPieData, getGenderTooltipChildren } from '../../helper';
4
+ import { renderTooltipJsx } from '../../../../../../../../src/utils';
5
+
6
+ const WorkersDistribution = ({
7
+ activityData,
8
+ loading = false,
9
+ t = (s) => s
10
+ }) => {
11
+ const genderDistributionData = useMemo(() => getGenderDistributionData(activityData), [activityData]);
12
+ const isEmpty = useMemo(() => isGenderDistributionEmpty(genderDistributionData), [genderDistributionData]);
13
+ const pieData = useMemo(() => calculateGenderPieData(genderDistributionData), [genderDistributionData]);
14
+
15
+ const getTooltipChildren = useCallback(
16
+ (item) => getGenderTooltipChildren(item, isEmpty, genderDistributionData, t, renderTooltipJsx),
17
+ [t, isEmpty, genderDistributionData],
18
+ );
19
+
20
+ return (
21
+ <Widget
22
+ loading={loading}
23
+ title={<div>{t("Workers Distribution")}</div>}
24
+ className="with-border-header h-w-btn-header "
25
+ >
26
+ <div
27
+ style={{
28
+ marginTop: "auto",
29
+ marginBottom: "auto",
30
+ }}
31
+ >
32
+ <PieChart
33
+ mouseXOffset={10}
34
+ mouseYOffset={10}
35
+ changeOpacityOnHover={false}
36
+ data={pieData}
37
+ doConstraints={false}
38
+ isPie
39
+ t={t}
40
+ isEmpty={isEmpty}
41
+ getTooltipChildren={getTooltipChildren}
42
+ />
43
+ </div>
44
+ </Widget>
45
+ );
46
+ };
47
+
48
+ export default WorkersDistribution;
49
+
@@ -0,0 +1,51 @@
1
+ import React from "react";
2
+ import { camelCaseToTitle } from "../../../../../utils";
3
+
4
+
5
+
6
+ export const getKeyIndicatorsRowConfig = ({ t, data = {} }) => [
7
+
8
+ {
9
+ label: t('Primary Data Collection'),
10
+ render: () => {
11
+
12
+ return <div className="flex">{'-'}</div>;
13
+ }
14
+ },
15
+ {
16
+ label: t('Monitoring Organization'),
17
+ render: () => <div>{data?.technicalPartner?.name || '-'}</div>
18
+ },
19
+ {
20
+ label: t('Team Members'),
21
+ render: () => <div> {data?.teamMembers?.length || '0'}</div>
22
+ },
23
+ {
24
+ label: t('Monitored Area'),
25
+ render: () => <div>{data?.totalAreaParcelMonitored ? `${data?.totalAreaParcelMonitored} ha` : '0 ha'}</div>
26
+ },
27
+
28
+ ];
29
+
30
+ export const getSiteConditionsConfig = ({ t, data = {} }) => [
31
+ {
32
+ label: t('Season'),
33
+ icon: 'Tree',
34
+ render: () => <div>{camelCaseToTitle(data?.season)|| '-'}</div>
35
+ },
36
+ {
37
+ label: t('Weather conditions'),
38
+ icon: 'Globe',
39
+ render: () => <div>{ camelCaseToTitle(data?.weatherConditions )|| '-'}</div>
40
+ },
41
+ {
42
+ label: t('Tide level'),
43
+ icon: 'WaterDrop',
44
+ render: () => <div>{camelCaseToTitle(data?.tideLevel )|| '-'}</div>
45
+ },
46
+ {
47
+ label: t('Disturbances'),
48
+ icon: 'Lightning',
49
+ render: () => <div>{data?.hasDisturbances ? t('Yes') : t('No')}</div>
50
+ },
51
+ ];
@@ -0,0 +1,236 @@
1
+ import L from "leaflet";
2
+
3
+ const normalizeUrl = (url) => url?.endsWith(':') ? url.slice(0, -1) : url;
4
+
5
+
6
+ export const extractFromPhotoDoc = (photoDoc, label, docIndex) => {
7
+ const photos = photoDoc?.pictures?.filter(p => p?.url) || (photoDoc?.url ? [photoDoc] : []);
8
+
9
+ return photos.map((photo, photoIndex) => ({
10
+ src: normalizeUrl(photo.url),
11
+ alt: photo.name || photo.alt || `${label} - Image ${docIndex + 1}-${photoIndex + 1}`
12
+ }));
13
+ };
14
+
15
+ export const getActivityImages = (activityData) => {
16
+ const photoArrays = [
17
+ { data: activityData?.groupPhotos, label: 'Group Photo' },
18
+ { data: activityData?.photosStartActivity, label: 'Start of Activity' },
19
+ { data: activityData?.photosDuringActivity, label: 'During Activity' },
20
+ { data: activityData?.photosEndActivity, label: 'End of Activity' }
21
+ ];
22
+
23
+ return photoArrays
24
+ .flatMap(({ data, label }) =>
25
+ Array.isArray(data)
26
+ ? data.flatMap((photoDoc, index) => extractFromPhotoDoc(photoDoc, label, index))
27
+ : []
28
+ );
29
+ };
30
+
31
+ const GENDER_COLORS = ['#016C6E', '#00AEB1'];
32
+
33
+ export const getGenderDistributionData = (activityData) => {
34
+ return {
35
+ Male: activityData?.genderDistributionMale || 0,
36
+ Female: activityData?.genderDistributionFemale || 0,
37
+ };
38
+ };
39
+
40
+
41
+ export const isGenderDistributionEmpty = (genderDistributionData) => {
42
+ return Object.values(genderDistributionData).every(val => !val || val === 0);
43
+ };
44
+
45
+ export const calculateGenderPieData = (genderDistributionData) => {
46
+ const total = Object.values(genderDistributionData).reduce((all, val) => all + (val || 0), 0);
47
+
48
+ return Object.keys(genderDistributionData).map((key, index) => {
49
+ const color = GENDER_COLORS[index % GENDER_COLORS.length];
50
+
51
+ return {
52
+ value: genderDistributionData[key] || 0,
53
+ percent: total > 0 ? (genderDistributionData[key] || 0) / total : 0,
54
+ color: color,
55
+ label: key,
56
+ key: key,
57
+ };
58
+ });
59
+ };
60
+
61
+ export const getGenderTooltipChildren = (item, isEmpty, genderDistributionData, t, renderTooltipJsx) => {
62
+ if (isEmpty) {
63
+ if (!Object.keys(genderDistributionData).length) {
64
+ return null;
65
+ }
66
+
67
+ return renderTooltipJsx({
68
+ title: t("Gender"),
69
+ items: Object.keys(genderDistributionData).map((k) => ({
70
+ label: k,
71
+ value: 0,
72
+ })),
73
+ });
74
+ }
75
+
76
+ return renderTooltipJsx({
77
+ title: t("Gender"),
78
+ items: [
79
+ {
80
+ color: item.color,
81
+ label: t(item.label),
82
+ value: `${item.value || 0}`,
83
+ },
84
+ ],
85
+ });
86
+ };
87
+
88
+ export const getIndicatorType = (value) => {
89
+ if (value === "yes" || value === true) return "compliant";
90
+ if (value === "no" || value === false) return "notCompliant";
91
+ if (value === null || value === undefined) return "empty";
92
+ return "empty"; // default fallback
93
+ };
94
+
95
+ export const getActivityIndicatorsConfig = (activityData, t) => {
96
+
97
+ return [
98
+ { icon: "Aid", label: t("Aid kit availability"), type: getIndicatorType(activityData?.aidKitAccessible) },
99
+ { icon: "MineOperators", label: t("H&S training delivery"), type: getIndicatorType(activityData?.hsTrainingConfirmation) },
100
+ { icon: "Users", label: t("Workers safe pairing"), type: getIndicatorType(activityData?.duosFormed) },
101
+ {
102
+ icon: "Bear",
103
+ label: t("No children"),
104
+ type: getIndicatorType(activityData?.presenceOfChildren),
105
+ },
106
+ { icon: "Security", label: t("Security presence"), type: getIndicatorType(activityData?.focalPointPresent) },
107
+ { icon: "UserCircle", label: t("Relay presence"), type: getIndicatorType(activityData?.relayPresent) },
108
+ ];
109
+ };
110
+
111
+ export const getMapDataFromActivity = (activityData, t) => {
112
+
113
+ const location = activityData?.location;
114
+ const perimeter = location?.perimeter;
115
+
116
+ const area = Array.isArray(perimeter) && perimeter.length > 0
117
+ ? perimeter
118
+ .filter((coord) => Array.isArray(coord) && coord.length >= 2)
119
+ .map((coord) => {
120
+ const first = typeof coord[0] === 'number' ? coord[0] : parseFloat(coord[0]);
121
+ const second = typeof coord[1] === 'number' ? coord[1] : parseFloat(coord[1]);
122
+
123
+ if (isNaN(first) || isNaN(second) || !isFinite(first) || !isFinite(second)) {
124
+ return null;
125
+ }
126
+
127
+ // Try both formats and use Leaflet to validate
128
+ // First try as [lat, lng]
129
+ try {
130
+ const latLng1 = L.latLng(first, second);
131
+ if (latLng1.lat >= -90 && latLng1.lat <= 90 &&
132
+ latLng1.lng >= -180 && latLng1.lng <= 180) {
133
+ return [latLng1.lat, latLng1.lng];
134
+ }
135
+ } catch (e) {
136
+ // Not valid as [lat, lng], try swapping
137
+ }
138
+
139
+ // Try as [lng, lat] (GeoJSON format) - swap them
140
+ try {
141
+ const latLng2 = L.latLng(second, first);
142
+ if (latLng2.lat >= -90 && latLng2.lat <= 90 &&
143
+ latLng2.lng >= -180 && latLng2.lng <= 180) {
144
+ return [latLng2.lat, latLng2.lng];
145
+ }
146
+ } catch (e) {
147
+ // Invalid coordinates
148
+ }
149
+
150
+ return null;
151
+ })
152
+ .filter((coord) => coord !== null)
153
+ : null;
154
+
155
+ // Don't return early - we need to check for markers even if there's no perimeter
156
+
157
+ const mapData = [];
158
+ const baseColor = '#15FFFFB2';
159
+ const locationName = location?.name || activityData?.name || t("Activity Location");
160
+ const datastakeId = location?.datastakeId || activityData?.datastakeId;
161
+
162
+ // Helper to validate coordinates
163
+ const isValidCoordinate = (coord) => {
164
+ const num = typeof coord === 'number' ? coord : parseFloat(coord);
165
+ return !isNaN(num) && isFinite(num);
166
+ };
167
+
168
+ // Entry 1: Perimeter polygon (independent - show if it exists)
169
+ if (area && area.length >= 3) {
170
+ mapData.push({
171
+ _id: {},
172
+ id: `${activityData?.id || activityData?.datastakeId || 'perimeter'}-perimeter`,
173
+ area: area,
174
+ color: baseColor,
175
+ // No gps property - this will display only the polygon without markers
176
+ name: locationName,
177
+ plotName: locationName,
178
+ territoryTitle: t("Associated Plot"),
179
+ subTitle: t("Activity Location"),
180
+ type: t("Activity Location"),
181
+ datastakeId: datastakeId,
182
+ sources: null,
183
+ link: null,
184
+ });
185
+ }
186
+
187
+ // Entry 2: Arrival marker (independent - show if it exists)
188
+ const arrivalLat = activityData?.locationCheckArrival?.latitude;
189
+ const arrivalLng = activityData?.locationCheckArrival?.longitude;
190
+ if (isValidCoordinate(arrivalLat) && isValidCoordinate(arrivalLng)) {
191
+ mapData.push({
192
+ _id: {},
193
+ id: `${activityData?.id || activityData?.datastakeId || 'arrival'}-arrival`,
194
+ // Include area if it exists, so marker can show on top of polygon
195
+ area: area && area.length >= 3 ? area : null,
196
+ color: baseColor,
197
+ gps: {
198
+ latitude: typeof arrivalLat === 'number' ? arrivalLat : parseFloat(arrivalLat),
199
+ longitude: typeof arrivalLng === 'number' ? arrivalLng : parseFloat(arrivalLng),
200
+ },
201
+ name: t("Activity Start"),
202
+ plotName: locationName,
203
+ territoryTitle: t("Associated Plot"),
204
+ datastakeId: `${datastakeId}-arrival`,
205
+ sources: null,
206
+ link: null,
207
+ });
208
+ }
209
+
210
+ // Entry 3: Departure marker (independent - show if it exists)
211
+ const departureLat = activityData?.locationCheckDeparture?.latitude;
212
+ const departureLng = activityData?.locationCheckDeparture?.longitude;
213
+ if (isValidCoordinate(departureLat) && isValidCoordinate(departureLng)) {
214
+ mapData.push({
215
+ _id: {},
216
+ id: `${activityData?.id || activityData?.datastakeId || 'departure'}-departure`,
217
+ // Include area if it exists, so marker can show on top of polygon
218
+ area: area && area.length >= 3 ? area : null,
219
+ color: baseColor,
220
+ gps: {
221
+ latitude: typeof departureLat === 'number' ? departureLat : parseFloat(departureLat),
222
+ longitude: typeof departureLng === 'number' ? departureLng : parseFloat(departureLng),
223
+ },
224
+ name: t("Activity End"),
225
+ plotName: locationName,
226
+ territoryTitle: t("Associated Plot"),
227
+ datastakeId: `${datastakeId}-departure`,
228
+ markerColor: "#FF7A45",
229
+ sources: null,
230
+ link: null,
231
+ });
232
+ }
233
+
234
+ // Return mapData even if empty - let the map component handle empty arrays
235
+ return mapData;
236
+ };
@@ -0,0 +1,66 @@
1
+ import { useMemo } from 'react';
2
+ import { DashboardLayout, Header, KeyIndicators } from '../../../../../../src/index.js'
3
+ import { getKeyIndicatorsRowConfig, getSiteConditionsConfig } from './config';
4
+ import { getActivityIndicatorsConfig } from './helper';
5
+ import ActivityLocation from './components/ActivityLocation/index.jsx';
6
+ import ActivityImagery from './components/ActivityImagery/index.jsx';
7
+ import WorkersDistribution from './components/WorkersDistribution/index.jsx';
8
+ import MangroveGrowthAndSurvival from './components/MangroveGrowthAndSurvival/index.jsx';
9
+
10
+
11
+ const MonitoringActivitySummary = ({ activityData, supportText, onDownload, downloadDisabled, actionButtons, breadcrumbs, goBackTo, loading, t = () => { } }) => {
12
+ const keyIndicatorsConfig = useMemo(() => getKeyIndicatorsRowConfig({ t, data: activityData }), [t, activityData]);
13
+
14
+ // Activity Indicators Config - mapped from activityData
15
+ const activityIndicatorsConfig = useMemo(() =>
16
+ getActivityIndicatorsConfig(activityData, t),
17
+ [activityData, t]
18
+ );
19
+
20
+ // Site Conditions Config
21
+ const siteConditionsConfig = useMemo(() =>
22
+ getSiteConditionsConfig({ t, data: activityData }),
23
+ [t, activityData]
24
+ );
25
+
26
+
27
+ console.log({activityData});
28
+ return (
29
+ <DashboardLayout
30
+ header={
31
+ <Header
32
+ title={'Monitoring Activity Summary'}
33
+ supportText={supportText || ''}
34
+ onDownload={onDownload}
35
+ downloadDisabled={downloadDisabled}
36
+ actionButtons={actionButtons}
37
+ breadcrumbs={breadcrumbs}
38
+ goBackTo={goBackTo}
39
+ loading={loading}
40
+ />
41
+ }
42
+ >
43
+ <section>
44
+ <KeyIndicators title={t("Key Information")} config={keyIndicatorsConfig} loading={loading} />
45
+ </section>
46
+
47
+ <ActivityLocation activityData={activityData} loading={loading} t={t} />
48
+
49
+ <KeyIndicators
50
+ loading={loading}
51
+ config={siteConditionsConfig}
52
+ title={t("straatos::site-conditions")}
53
+ widgetClassName="h-w-btn-header"
54
+ />
55
+
56
+ <MangroveGrowthAndSurvival
57
+ activityData={activityData}
58
+ loading={loading}
59
+ t={t}
60
+ />
61
+
62
+ </DashboardLayout>
63
+ )
64
+ }
65
+
66
+ export default MonitoringActivitySummary;