datastake-daf 0.6.786 → 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 (22) hide show
  1. package/dist/components/index.js +226 -91
  2. package/dist/pages/index.js +774 -33
  3. package/dist/style/datastake/mapbox-gl.css +330 -0
  4. package/package.json +1 -1
  5. package/src/@daf/core/components/Dashboard/Widget/ActivityIndicators/ActivityIndicators.stories.js +24 -0
  6. package/src/@daf/core/components/Dashboard/Widget/ActivityIndicators/index.jsx +1 -0
  7. package/src/@daf/core/components/Dashboard/Widget/ActivityIndicators/style.js +34 -0
  8. package/src/@daf/core/components/Dashboard/Widget/KeyIndicators/KeyIndicators.stories.js +39 -0
  9. package/src/@daf/core/components/Dashboard/Widget/KeyIndicators/LabelWithIcon.jsx +38 -0
  10. package/src/@daf/core/components/Dashboard/Widget/KeyIndicators/index.jsx +16 -3
  11. package/src/@daf/core/components/Dashboard/Widget/KeyIndicators/style.js +33 -0
  12. package/src/@daf/pages/Summary/Activities/Monitoring/components/ActivityImagery/index.jsx +29 -0
  13. package/src/@daf/pages/Summary/Activities/Monitoring/components/ActivityLocation/index.jsx +94 -0
  14. package/src/@daf/pages/Summary/Activities/Monitoring/components/MangroveGrowthAndSurvival/components/PlantedSpecies/index.jsx +56 -0
  15. package/src/@daf/pages/Summary/Activities/Monitoring/components/MangroveGrowthAndSurvival/components/SeedlingsHeight/index.jsx +121 -0
  16. package/src/@daf/pages/Summary/Activities/Monitoring/components/MangroveGrowthAndSurvival/components/SurvivalRate/index.jsx +94 -0
  17. package/src/@daf/pages/Summary/Activities/Monitoring/components/MangroveGrowthAndSurvival/index.jsx +54 -0
  18. package/src/@daf/pages/Summary/Activities/Monitoring/components/WorkersDistribution/index.jsx +49 -0
  19. package/src/@daf/pages/Summary/Activities/Monitoring/config.js +51 -0
  20. package/src/@daf/pages/Summary/Activities/Monitoring/helper.js +236 -0
  21. package/src/@daf/pages/Summary/Activities/Monitoring/index.jsx +66 -0
  22. package/src/pages.js +1 -0
@@ -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;
package/src/pages.js CHANGED
@@ -21,6 +21,7 @@ export { default as RestorationActivitySummary } from './@daf/pages/Summary/Acti
21
21
  export { default as PlantingCycleSummary } from './@daf/pages/Summary/Activities/PlantingCycle/index.jsx';
22
22
  export { default as MonitoringCampaignSummary } from './@daf/pages/Summary/Activities/MonitoringCampaign/index.jsx';
23
23
  export { default as MineSummary } from './@daf/pages/Summary/Minesite/index.jsx';
24
+ export { default as MonitoringActivitySummary } from './@daf/pages/Summary/Activities/Monitoring/index.jsx';
24
25
 
25
26
  // View
26
27
  export { default as View } from './@daf/pages/View/index.jsx';