datastake-daf 0.6.768 → 0.6.769

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 (40) hide show
  1. package/dist/components/index.js +767 -551
  2. package/dist/layouts/index.js +495 -459
  3. package/dist/pages/index.js +2808 -738
  4. package/dist/style/datastake/mapbox-gl.css +330 -0
  5. package/dist/utils/index.js +481 -457
  6. package/package.json +1 -1
  7. package/src/@daf/core/components/Charts/ColumnChart/index.jsx +10 -0
  8. package/src/@daf/core/components/Charts/LineChart/index.jsx +14 -0
  9. package/src/@daf/core/components/Dashboard/Widget/ActivityIndicators/index.jsx +2 -0
  10. package/src/@daf/core/components/Dashboard/Widget/StatCard/StatCard.stories.js +226 -0
  11. package/src/@daf/core/components/Dashboard/Widget/StatCard/index.js +103 -0
  12. package/src/@daf/core/components/Dashboard/Widget/StatCard/style.js +83 -0
  13. package/src/@daf/core/components/Icon/configs/Down.js +8 -0
  14. package/src/@daf/core/components/Icon/configs/Up.js +8 -0
  15. package/src/@daf/core/components/Icon/configs/index.js +4 -0
  16. package/src/@daf/core/components/Icon/configs/partnerIcon.js +1 -1
  17. package/src/@daf/core/components/Sidenav/Menu.jsx +4 -4
  18. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/AssociatedInformation/index.jsx +43 -0
  19. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CommunityParticipation/CommunityStats/helper.js +60 -0
  20. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CommunityParticipation/CommunityStats/index.jsx +36 -0
  21. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CommunityParticipation/GenderDistribution/helper.js +117 -0
  22. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CommunityParticipation/GenderDistribution/index.jsx +49 -0
  23. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CommunityParticipation/JobsTimeline/index.jsx +212 -0
  24. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CommunityParticipation/index.jsx +72 -0
  25. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleIndicators/CyclePartners/helper.js +91 -0
  26. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleIndicators/CyclePartners/index.jsx +50 -0
  27. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleIndicators/HealthAndSafety/helper.js +134 -0
  28. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleIndicators/HealthAndSafety/index.jsx +49 -0
  29. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleIndicators/index.jsx +112 -0
  30. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleOutcomes/index.jsx +498 -0
  31. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/KeyInformation/index.jsx +49 -0
  32. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/PlantingLocations/index.jsx +120 -0
  33. package/src/@daf/pages/Summary/Activities/PlantingCycle/config.js +5 -10
  34. package/src/@daf/pages/Summary/Activities/PlantingCycle/helper.js +218 -0
  35. package/src/@daf/pages/Summary/Activities/PlantingCycle/index.jsx +22 -32
  36. package/src/@daf/pages/Summary/Activities/Restoration/components/ActivityImagery/index.jsx +29 -0
  37. package/src/@daf/pages/Summary/Activities/Restoration/components/ActivityLocation/index.jsx +94 -0
  38. package/src/@daf/pages/Summary/Activities/Restoration/components/WorkersDistribution/index.jsx +49 -0
  39. package/src/@daf/pages/Summary/Activities/Restoration/index.jsx +16 -138
  40. package/src/index.js +1 -0
@@ -4,36 +4,31 @@ export const getKeyIndicatorsRowConfig = ({ t, data = {} }) => [
4
4
  {
5
5
  label: t('Region'),
6
6
  render: () => {
7
- // const region = data?.region;
8
- return <div>{ '-'}</div>;
7
+ return <div>{ data?.region || '-'}</div>;
9
8
  }
10
9
  },
11
10
  {
12
11
  label: t('Associated Plots'),
13
12
  render: () => {
14
- // const plotsCount = data?.associatedPlotsCount || '0';
15
- return <div>{'0'}</div>
13
+ return <div>{data?.associatedPlotsCount || '0'}</div>
16
14
  }
17
15
  },
18
16
  {
19
17
  label: t('Implementation Partners'),
20
18
  render: () => {
21
- // const partnersCount = data?.partnersCount || '0';
22
- return <div>{'0'}</div>
19
+ return <div>{data?.partnersCount || '0'}</div>
23
20
  }
24
21
  },
25
22
  {
26
23
  label: t('Total Activities'),
27
24
  render: () => {
28
- // const activitiesCount = data?.activitiesCount || '0';
29
- return <div>{'0'}</div>
25
+ return <div>{data?.activitiesCount || '0'}</div>
30
26
  }
31
27
  },
32
28
  {
33
29
  label: t('Information Sources'),
34
30
  render: () => {
35
- // const sourcesCount = data?.informationSourcesCount || '0';
36
- return <div>{'0'}</div>
31
+ return <div>{data?.informationSourcesCount || '0'}</div>
37
32
  }
38
33
  },
39
34
  ];
@@ -120,3 +120,221 @@ export const getMapDataFromActivity = (activityData, t) => {
120
120
  return mapData;
121
121
  };
122
122
 
123
+ const PARTNER_COLORS = ['#016C6E', '#00AEB1', '#FF7A45', '#52C41A', '#1890FF', '#722ED1', '#FA8C16', '#EB2F96'];
124
+
125
+ export const getPartnersDistributionData = (activityData) => {
126
+ // Handle object format: { "Partner 1": count1, "Partner 2": count2 }
127
+ if (activityData?.partnersDistribution && typeof activityData.partnersDistribution === 'object') {
128
+ return activityData.partnersDistribution;
129
+ }
130
+
131
+ if (Array.isArray(activityData?.partners)) {
132
+ const distribution = {};
133
+ activityData.partners.forEach(partner => {
134
+ const name = partner?.name || partner?.nickName || partner?.label || 'Unknown';
135
+ const count = partner?.count || partner?.value || 1;
136
+ distribution[name] = (distribution[name] || 0) + count;
137
+ });
138
+ return distribution;
139
+ }
140
+
141
+ // Default empty object
142
+ return {};
143
+ };
144
+
145
+ export const isPartnersDistributionEmpty = (partnersDistributionData) => {
146
+ return Object.values(partnersDistributionData).every(val => !val || val === 0);
147
+ };
148
+
149
+ export const calculatePartnersPieData = (partnersDistributionData) => {
150
+ const total = Object.values(partnersDistributionData).reduce((all, val) => all + (val || 0), 0);
151
+
152
+ return Object.keys(partnersDistributionData).map((key, index) => {
153
+ const color = PARTNER_COLORS[index % PARTNER_COLORS.length];
154
+
155
+ return {
156
+ value: partnersDistributionData[key] || 0,
157
+ percent: total > 0 ? (partnersDistributionData[key] || 0) / total : 0,
158
+ color: color,
159
+ label: key,
160
+ key: key,
161
+ };
162
+ });
163
+ };
164
+
165
+ export const getPartnersTooltipChildren = (item, isEmpty, partnersDistributionData, t, renderTooltipJsx) => {
166
+ if (isEmpty) {
167
+ if (!Object.keys(partnersDistributionData).length) {
168
+ return null;
169
+ }
170
+
171
+ return renderTooltipJsx({
172
+ title: t("Partners"),
173
+ items: Object.keys(partnersDistributionData).map((k) => ({
174
+ label: k,
175
+ value: 0,
176
+ })),
177
+ });
178
+ }
179
+
180
+ return renderTooltipJsx({
181
+ title: t("Partners"),
182
+ items: [
183
+ {
184
+ color: item.color,
185
+ label: item.label,
186
+ value: `${item.value || 0}`,
187
+ },
188
+ ],
189
+ });
190
+ };
191
+
192
+ const HEALTH_SAFETY_COLORS = {
193
+ compliant: '#52C41A',
194
+ notCompliant: '#FF4D4F',
195
+ empty: '#D9D9D9',
196
+ };
197
+
198
+ const getIndicatorType = (value) => {
199
+ if (value === "yes" || value === true) return "compliant";
200
+ if (value === "no" || value === false) return "notCompliant";
201
+ if (value === null || value === undefined) return "empty";
202
+ return "empty";
203
+ };
204
+
205
+ export const getHealthAndSafetyDistributionData = (activityData) => {
206
+ // Define health and safety indicator fields
207
+ const indicators = [
208
+ { field: 'aidKitAccessible', label: 'Aid kit availability' },
209
+ { field: 'hsTrainingConfirmation', label: 'H&S training delivery' },
210
+ { field: 'duosFormed', label: 'Workers safe pairing' },
211
+ { field: 'presenceOfChildren', label: 'No children' },
212
+ { field: 'focalPointPresent', label: 'Security presence' },
213
+ { field: 'relayPresent', label: 'Relay presence' },
214
+ ];
215
+
216
+ const distribution = {
217
+ compliant: 0,
218
+ notCompliant: 0,
219
+ empty: 0,
220
+ };
221
+
222
+ indicators.forEach(({ field }) => {
223
+ const value = activityData?.[field];
224
+ const type = getIndicatorType(value);
225
+ distribution[type] = (distribution[type] || 0) + 1;
226
+ });
227
+
228
+ return distribution;
229
+ };
230
+
231
+ export const isHealthAndSafetyDistributionEmpty = (healthAndSafetyDistributionData) => {
232
+ return Object.values(healthAndSafetyDistributionData).every(val => !val || val === 0);
233
+ };
234
+
235
+ export const calculateHealthAndSafetyPieData = (healthAndSafetyDistributionData, t) => {
236
+ const total = Object.values(healthAndSafetyDistributionData).reduce((all, val) => all + (val || 0), 0);
237
+
238
+ const labels = {
239
+ compliant: t("Compliant"),
240
+ notCompliant: t("Not Compliant"),
241
+ empty: t("Not Set"),
242
+ };
243
+
244
+ return Object.keys(healthAndSafetyDistributionData).map((key) => {
245
+ const color = HEALTH_SAFETY_COLORS[key] || '#D9D9D9';
246
+
247
+ return {
248
+ value: healthAndSafetyDistributionData[key] || 0,
249
+ percent: total > 0 ? (healthAndSafetyDistributionData[key] || 0) / total : 0,
250
+ color: color,
251
+ label: labels[key] || key,
252
+ key: key,
253
+ };
254
+ });
255
+ };
256
+
257
+ export const getHealthAndSafetyTooltipChildren = (item, isEmpty, healthAndSafetyDistributionData, t, renderTooltipJsx) => {
258
+ if (isEmpty) {
259
+ if (!Object.keys(healthAndSafetyDistributionData).length) {
260
+ return null;
261
+ }
262
+
263
+ return renderTooltipJsx({
264
+ title: t("Health and Safety"),
265
+ items: Object.keys(healthAndSafetyDistributionData).map((k) => ({
266
+ label: k === 'compliant' ? t("Compliant") : k === 'notCompliant' ? t("Not Compliant") : t("Not Set"),
267
+ value: 0,
268
+ })),
269
+ });
270
+ }
271
+
272
+ return renderTooltipJsx({
273
+ title: t("Health and Safety"),
274
+ items: [
275
+ {
276
+ color: item.color,
277
+ label: item.label,
278
+ value: `${item.value || 0}`,
279
+ },
280
+ ],
281
+ });
282
+ };
283
+
284
+ /**
285
+ * Calculates stat change object for StatCard component based on current and previous values
286
+ * @param {Object} data - Object with current and previous values
287
+ * @param {number} data.current - Current value
288
+ * @param {number} data.previous - Previous value
289
+ * @param {Object} options - Optional configuration
290
+ * @param {string} options.tooltipText - Custom tooltip text
291
+ * @param {string} options.format - Format type: 'percentage' (default) or 'absolute'
292
+ * @param {number} options.decimalPlaces - Number of decimal places for percentage (default: 1)
293
+ * @returns {Object|null} Change object for StatCard or null if data is invalid
294
+ */
295
+ export const calculateStatChange = (data, options = {}) => {
296
+ if (!data || typeof data !== 'object') {
297
+ return null;
298
+ }
299
+
300
+ const { current, previous } = data;
301
+
302
+ // Validate that both values are numbers
303
+ if (typeof current !== 'number' || typeof previous !== 'number') {
304
+ return null;
305
+ }
306
+
307
+ // If previous is 0, we can't calculate percentage change
308
+ if (previous === 0) {
309
+ return null;
310
+ }
311
+
312
+ const {
313
+ tooltipText,
314
+ format = 'percentage',
315
+ decimalPlaces = 1,
316
+ } = options;
317
+
318
+ // Calculate the difference
319
+ const difference = current - previous;
320
+ const isPositive = difference >= 0;
321
+ const direction = isPositive ? 'up' : 'down';
322
+
323
+ // Format the value
324
+ let value;
325
+ if (format === 'absolute') {
326
+ // Show absolute difference
327
+ value = Math.abs(difference).toLocaleString();
328
+ } else {
329
+ // Show percentage change
330
+ const percentageChange = (Math.abs(difference) / previous) * 100;
331
+ value = `${percentageChange.toFixed(decimalPlaces)}%`;
332
+ }
333
+
334
+ return {
335
+ value,
336
+ direction,
337
+ tooltipText: tooltipText || undefined,
338
+ };
339
+ };
340
+
@@ -1,44 +1,34 @@
1
- import { useMemo } from 'react';
2
- import { DashboardLayout, Header, KeyIndicators, MineSiteMap, Widget } from '../../../../../../src/index.js'
3
- import { getKeyIndicatorsRowConfig } from './config';
1
+ import { DashboardLayout, Header } from '../../../../../../src/index.js'
2
+ import PlantingLocations from './components/PlantingLocations/index.jsx';
3
+ import CycleOutcomes from './components/CycleOutcomes/index.jsx';
4
+ import CycleIndicators from './components/CycleIndicators/index.jsx';
5
+ import CommunityParticipation from './components/CommunityParticipation/index.jsx';
6
+ import AssociatedInformation from './components/AssociatedInformation/index.jsx';
7
+ import KeyInformation from './components/KeyInformation/index.jsx';
4
8
 
5
- const PlantingCycleSummary = ({ activityData, supportText, onDownload, downloadDisabled, actionButtons, breadcrumbs, goBackTo, loading, t = () => { } }) => {
6
- const keyIndicatorsConfig = useMemo(() => getKeyIndicatorsRowConfig({ t, data: {} }), [t]);
9
+ const PlantingCycleSummary = ({ header, activityData, loading = false, id, t = () => { }, getSummaryDetail }) => {
7
10
 
8
11
  return (
9
12
  <DashboardLayout
10
13
  header={
11
14
  <Header
12
- title={'Planting Cycle Summary'}
13
- supportText={supportText || ''}
14
- onDownload={onDownload}
15
- downloadDisabled={downloadDisabled}
16
- actionButtons={actionButtons}
17
- breadcrumbs={breadcrumbs}
18
- goBackTo={goBackTo}
19
- loading={loading}
15
+ title={header?.title || ''}
16
+ supportText={header?.supportText || ''}
17
+ onDownload={header?.onDownload}
18
+ downloadDisabled={header?.downloadDisabled}
19
+ actionButtons={header?.actionButtons}
20
+ breadcrumbs={header?.breadcrumbs}
21
+ goBackTo={header?.goBackTo}
22
+ loading={header?.loading}
20
23
  />
21
24
  }
22
25
  >
23
- <section>
24
- <KeyIndicators title={t("Key Information")} config={keyIndicatorsConfig} loading={loading} />
25
- </section>
26
-
27
- <section>
28
- <Widget
29
- title={t("Planting Locations")}
30
- className="no-px h-w-btn-header no-pt-body no-p-body no-pb-body"
31
- style={{ height: '100%', display: 'flex', flexDirection: 'column' }}
32
- >
33
- <MineSiteMap
34
- loading={loading}
35
- t={t}
36
- isSatellite
37
- data={[]}
38
- />
39
- </Widget>
40
-
41
- </section>
26
+ <KeyInformation id={id} t={t} getSummaryDetail={getSummaryDetail} loading={loading} />
27
+ <PlantingLocations id={id} getSummaryDetail={getSummaryDetail} loading={loading} t={t} />
28
+ <CycleOutcomes id={id} getSummaryDetail={getSummaryDetail} loading={loading} t={t} />
29
+ <CycleIndicators id={id} getSummaryDetail={getSummaryDetail} loading={loading} t={t} />
30
+ <CommunityParticipation id={id} getSummaryDetail={getSummaryDetail} loading={loading} t={t} />
31
+ <AssociatedInformation id={id} getSummaryDetail={getSummaryDetail} loading={loading} t={t} />
42
32
  </DashboardLayout>
43
33
  )
44
34
  }
@@ -0,0 +1,29 @@
1
+ import React, { useMemo } from 'react';
2
+ import { ImageCarousel } from '../../../../../../../../src/index.js';
3
+ import { getActivityImages } from '../../helper';
4
+ import { useResizeContext } from '../../../../../../../../src/context';
5
+
6
+ const ActivityImagery = ({
7
+ activityData,
8
+ loading = false,
9
+ t = (s) => s
10
+ }) => {
11
+ const { isCollapsed, isNestedSidebarCollapsed } = useResizeContext();
12
+ const images = useMemo(() => getActivityImages(activityData), [activityData]);
13
+
14
+ return (
15
+ <div style={{ maxWidth: "70%", width: "calc(100% - 405px)" }}>
16
+ <ImageCarousel
17
+ loading={loading}
18
+ images={images}
19
+ title={t("straatos::activity-imagery")}
20
+ key={`${isCollapsed}-${isNestedSidebarCollapsed}`}
21
+ customArrows={true}
22
+ activeDotColor="#003435"
23
+ />
24
+ </div>
25
+ );
26
+ };
27
+
28
+ export default ActivityImagery;
29
+
@@ -0,0 +1,94 @@
1
+ import React, { useMemo } from 'react';
2
+ import { Widget, MineSiteMap } from '../../../../../../../../src/index.js';
3
+ import { getMapDataFromActivity } from '../../helper';
4
+ import { convertDMS } from '../../../../../../../../src/helpers/Map';
5
+ import CustomIcon from '../../../../../../../../src/@daf/core/components/Icon/CustomIcon.jsx';
6
+
7
+ const ActivityLocation = ({
8
+ activityData,
9
+ loading = false,
10
+ t = (s) => s
11
+ }) => {
12
+ const mapData = useMemo(() => getMapDataFromActivity(activityData, t), [activityData, t]);
13
+
14
+ return (
15
+ <section>
16
+ <Widget
17
+ title={t("Activity Location")}
18
+ className="no-px h-w-btn-header no-pt-body no-p-body no-pb-body"
19
+ style={{ height: '100%', display: 'flex', flexDirection: 'column' }}
20
+ >
21
+ <div style={{ flex: 1, minHeight: 0 }}>
22
+ <MineSiteMap
23
+ loading={loading}
24
+ t={t}
25
+ isSatellite={true}
26
+ app={"straatos"}
27
+ type={'location-territory'}
28
+ showSider={false}
29
+ user={null}
30
+ data={mapData}
31
+ maxZoom={18}
32
+ primaryLink={true}
33
+ style={{ height: '100%', width: '100%' }}
34
+ renderTooltipForLocation={(data) => {
35
+ const coordinates = data.gps?.latitude && data.gps?.longitude
36
+ ? convertDMS(data.gps.latitude, data.gps.longitude)
37
+ : null;
38
+
39
+ if (!coordinates) {
40
+ return [];
41
+ }
42
+
43
+ const isActivityEnd = data.name === t("Activity End") || data.id?.includes('-departure');
44
+ const iconColor = isActivityEnd ? "#FF7A45" : "#016C6E";
45
+
46
+ return [
47
+ {
48
+ label: t("Coordinates"),
49
+
50
+ value: (
51
+ <div style={{ display: 'flex', alignItems: 'center', gap: '6px', flexWrap: 'nowrap' }}>
52
+ {/* Latitude icon (vertical) */}
53
+ <div style={{ display: 'flex', alignItems: 'center' }}>
54
+ <CustomIcon
55
+ name="SpacingHeight"
56
+ width={14}
57
+ height={14}
58
+ color={iconColor}
59
+ />
60
+ <span style={{ fontWeight: 600, marginLeft: '4px' }}>{coordinates[0]}</span>
61
+ </div>
62
+ {/* Longitude icon (horizontal) */}
63
+ <div style={{ display: 'flex', alignItems: 'center' }}>
64
+ <CustomIcon
65
+ name="SpacingWidth"
66
+ width={14}
67
+ height={14}
68
+ color={iconColor}
69
+ />
70
+ <span style={{ fontWeight: 600, marginLeft: '4px' }}>{coordinates[1]}</span>
71
+ </div>
72
+ </div>
73
+ ),
74
+ },
75
+ ];
76
+ }}
77
+ renderTooltipForTerritory={(data) => {
78
+ return [
79
+ {
80
+ label: t("Plot Name"),
81
+ value: data.plotName || data.name || "--",
82
+ },
83
+ ];
84
+ }}
85
+ link={true}
86
+ />
87
+ </div>
88
+ </Widget>
89
+ </section>
90
+ );
91
+ };
92
+
93
+ export default ActivityLocation;
94
+
@@ -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
+