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
@@ -0,0 +1,50 @@
1
+ import React, { useMemo, useCallback } from 'react';
2
+ import { Widget, PieChart } from '../../../../../../../../index.js';
3
+ import { getCyclePartners, isPartnersDistributionEmpty, calculatePartnersPieData, getPartnersTooltipChildren } from './helper';
4
+ import { renderTooltipJsx } from '../../../../../../../../utils.js';
5
+
6
+ const CyclePartners = ({
7
+ cyclePartners,
8
+ loading = false,
9
+ t = (s) => s
10
+ }) => {
11
+
12
+ const partnersDistributionData = useMemo(() => getCyclePartners(cyclePartners), [cyclePartners]);
13
+ const isEmpty = useMemo(() => isPartnersDistributionEmpty(partnersDistributionData), [partnersDistributionData]);
14
+ const pieData = useMemo(() => calculatePartnersPieData(partnersDistributionData), [partnersDistributionData]);
15
+
16
+ const getTooltipChildren = useCallback(
17
+ (item) => getPartnersTooltipChildren(item, isEmpty, partnersDistributionData, t, renderTooltipJsx),
18
+ [t, isEmpty, partnersDistributionData],
19
+ );
20
+
21
+ return (
22
+ <Widget
23
+ loading={loading}
24
+ title={<div>{t("Cycle Partners")}</div>}
25
+ className="with-border-header h-w-btn-header "
26
+ >
27
+ <div
28
+ style={{
29
+ marginTop: "auto",
30
+ marginBottom: "auto",
31
+ }}
32
+ >
33
+ <PieChart
34
+ mouseXOffset={10}
35
+ mouseYOffset={10}
36
+ changeOpacityOnHover={false}
37
+ data={pieData}
38
+ doConstraints={false}
39
+ isPie
40
+ t={t}
41
+ isEmpty={isEmpty}
42
+ getTooltipChildren={getTooltipChildren}
43
+ />
44
+ </div>
45
+ </Widget>
46
+ );
47
+ };
48
+
49
+ export default CyclePartners;
50
+
@@ -0,0 +1,134 @@
1
+ const HEALTH_SAFETY_COLORS = {
2
+ compliant: '#52C41A',
3
+ notCompliant: '#FF4D4F',
4
+ empty: '#D9D9D9',
5
+ };
6
+
7
+ const getIndicatorType = (value) => {
8
+ if (value === "yes" || value === true) return "compliant";
9
+ if (value === "no" || value === false) return "notCompliant";
10
+ if (value === null || value === undefined) return "empty";
11
+ return "empty";
12
+ };
13
+
14
+ /**
15
+ * Gets health and safety distribution data from activity data
16
+ * @param {Object} activityData - Activity data object
17
+ * @returns {Object} Distribution object with compliant, notCompliant, and empty counts
18
+ */
19
+ export const getHealthAndSafetyDistributionData = (activityData) => {
20
+ // Define health and safety indicator fields
21
+ const indicators = [
22
+ { field: 'aidKitAccessible', label: 'Aid kit availability' },
23
+ { field: 'hsTrainingConfirmation', label: 'H&S training delivery' },
24
+ { field: 'duosFormed', label: 'Workers safe pairing' },
25
+ { field: 'presenceOfChildren', label: 'No children' },
26
+ { field: 'focalPointPresent', label: 'Security presence' },
27
+ { field: 'relayPresent', label: 'Relay presence' },
28
+ ];
29
+
30
+ const distribution = {
31
+ compliant: 0,
32
+ notCompliant: 0,
33
+ empty: 0,
34
+ };
35
+
36
+ indicators.forEach(({ field }) => {
37
+ const value = activityData?.[field];
38
+ const type = getIndicatorType(value);
39
+ distribution[type] = (distribution[type] || 0) + 1;
40
+ });
41
+
42
+ return distribution;
43
+ };
44
+
45
+ /**
46
+ * Checks if the health and safety distribution data is empty
47
+ * @param {Object} healthAndSafetyDistributionData - Distribution object
48
+ * @returns {boolean} True if all values are 0 or empty
49
+ */
50
+ export const isHealthAndSafetyDistributionEmpty = (healthAndSafetyDistributionData) => {
51
+ return Object.values(healthAndSafetyDistributionData).every(val => !val || val === 0);
52
+ };
53
+
54
+ /**
55
+ * Calculates pie chart data from health and safety distribution
56
+ * @param {Object} healthAndSafetyDistributionData - Distribution object
57
+ * @param {Function} t - Translation function
58
+ * @returns {Array} Array of pie chart data points with value, percent, color, label, and key
59
+ */
60
+ export const calculateHealthAndSafetyPieData = (healthAndSafetyDistributionData, t) => {
61
+ const total = Object.values(healthAndSafetyDistributionData).reduce((all, val) => all + (val || 0), 0);
62
+
63
+ const labels = {
64
+ compliant: t("Compliant"),
65
+ notCompliant: t("Not Compliant"),
66
+ };
67
+
68
+ return Object.keys(healthAndSafetyDistributionData).map((key) => {
69
+ const color = HEALTH_SAFETY_COLORS[key] || '#D9D9D9';
70
+
71
+ return {
72
+ value: healthAndSafetyDistributionData[key] || 0,
73
+ percent: total > 0 ? (healthAndSafetyDistributionData[key] || 0) / total : 0,
74
+ color: color,
75
+ label: labels[key] || key,
76
+ key: key,
77
+ };
78
+ });
79
+ };
80
+
81
+ /**
82
+ * Generates tooltip content for health and safety pie chart
83
+ * Shows all statuses with their percentages
84
+ * @param {Object} item - The pie chart item being hovered (not used, but kept for compatibility)
85
+ * @param {boolean} isEmpty - Whether the distribution is empty
86
+ * @param {Object} healthAndSafetyDistributionData - Distribution object
87
+ * @param {Function} t - Translation function
88
+ * @param {Function} renderTooltipJsx - Function to render tooltip JSX
89
+ * @returns {JSX.Element|null} Tooltip content or null
90
+ */
91
+ export const getHealthAndSafetyTooltipChildren = (item, isEmpty, healthAndSafetyDistributionData, t, renderTooltipJsx) => {
92
+ // If empty or no data, return null to display nothing
93
+ if (isEmpty || !Object.keys(healthAndSafetyDistributionData).length) {
94
+ return null;
95
+ }
96
+
97
+ // Calculate total for percentage calculation
98
+ const total = Object.values(healthAndSafetyDistributionData).reduce((all, val) => all + (val || 0), 0);
99
+
100
+ // If total is 0, return null to display nothing
101
+ if (total === 0) {
102
+ return null;
103
+ }
104
+
105
+ const labels = {
106
+ compliant: t("Compliant"),
107
+ notCompliant: t("Not Compliant"),
108
+ };
109
+
110
+ // Filter items with values > 0
111
+ const itemsWithData = Object.keys(healthAndSafetyDistributionData)
112
+ .filter(key => healthAndSafetyDistributionData[key] > 0)
113
+ .map((key) => {
114
+ const value = healthAndSafetyDistributionData[key] || 0;
115
+ const percent = total > 0 ? ((value / total) * 100).toFixed(0) : 0;
116
+ return {
117
+ color: HEALTH_SAFETY_COLORS[key] || '#D9D9D9',
118
+ label: labels[key] || key,
119
+ value: `${percent}%`,
120
+ };
121
+ });
122
+
123
+ // If no items with data, return null
124
+ if (itemsWithData.length === 0) {
125
+ return null;
126
+ }
127
+
128
+ // Show all items with their percentages
129
+ return renderTooltipJsx({
130
+ title: t("Health and Safety"),
131
+ items: itemsWithData,
132
+ });
133
+ };
134
+
@@ -0,0 +1,49 @@
1
+ import React, { useMemo, useCallback } from 'react';
2
+ import { Widget, PieChart } from '../../../../../../../../index.js';
3
+ import { getHealthAndSafetyDistributionData, isHealthAndSafetyDistributionEmpty, calculateHealthAndSafetyPieData, getHealthAndSafetyTooltipChildren } from './helper';
4
+ import { renderTooltipJsx } from '../../../../../../../../utils';
5
+
6
+ const HealthAndSafety = ({
7
+ activityData,
8
+ loading = false,
9
+ t = (s) => s
10
+ }) => {
11
+ const healthAndSafetyDistributionData = useMemo(() => getHealthAndSafetyDistributionData(activityData), [activityData]);
12
+ const isEmpty = useMemo(() => isHealthAndSafetyDistributionEmpty(healthAndSafetyDistributionData), [healthAndSafetyDistributionData]);
13
+ const pieData = useMemo(() => calculateHealthAndSafetyPieData(healthAndSafetyDistributionData, t), [healthAndSafetyDistributionData, t]);
14
+
15
+ const getTooltipChildren = useCallback(
16
+ (item) => getHealthAndSafetyTooltipChildren(item, isEmpty, healthAndSafetyDistributionData, t, renderTooltipJsx),
17
+ [t, isEmpty, healthAndSafetyDistributionData],
18
+ );
19
+
20
+ return (
21
+ <Widget
22
+ loading={loading}
23
+ title={<div>{t("Health and Safety")}</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 HealthAndSafety;
49
+
@@ -0,0 +1,112 @@
1
+ import React, { useMemo } from 'react';
2
+ import { Widget, StatCard } from '../../../../../../../index.js';
3
+ import CyclePartners from './CyclePartners/index.jsx';
4
+ import HealthAndSafety from './HealthAndSafety/index.jsx';
5
+ import { useWidgetFetch } from '../../../../../../hooks/useWidgetFetch.js';
6
+ import { calculateStatChange } from '../../helper.js';
7
+
8
+ const CycleIndicators = ({
9
+ id,
10
+ getSummaryDetail,
11
+ loading = false,
12
+ t = (s) => s
13
+ }) => {
14
+
15
+ const defaultConfig = useMemo(
16
+ () => ({
17
+ basepath: "planting-cycle",
18
+ url: `/summary/${id}/indicators`,
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: indicatorsLoading, data: indicatorsData } = useWidgetFetch({
39
+ config: defaultConfig,
40
+ getData: customGetData
41
+ });
42
+
43
+ const { participantsCount, eventIncidents, cyclePartners } = indicatorsData;
44
+
45
+ const participantsCountChange = useMemo(() => {
46
+ if (!participantsCount) return null;
47
+ return calculateStatChange(
48
+ {
49
+ current: Number(participantsCount.current) || 0,
50
+ previous: Number(participantsCount.previous) || 0,
51
+ },
52
+ {
53
+ tooltipText: t("In comparison to last cycle"),
54
+ format: 'absolute',
55
+ }
56
+ );
57
+ }, [participantsCount, t]);
58
+
59
+ const eventIncidentsChange = useMemo(() => {
60
+ if (!eventIncidents) return null;
61
+ return calculateStatChange(
62
+ {
63
+ current: Number(eventIncidents.current) || 0,
64
+ previous: Number(eventIncidents.previous) || 0,
65
+ },
66
+ {
67
+ tooltipText: t("In comparison to last cycle"),
68
+ format: 'absolute',
69
+ }
70
+ );
71
+ }, [eventIncidents, t]);
72
+
73
+ return (
74
+ <section>
75
+ <Widget
76
+ title={t("Cycle Indicators")}
77
+ loading={loading}
78
+ className="with-border-header h-w-btn-header"
79
+ >
80
+ <div style={{ display: 'flex', gap: '24px', marginBottom: '24px' }}>
81
+ <section style={{ flex: 1 }}>
82
+ <StatCard
83
+ title={t("Activity Participants")}
84
+ value={ participantsCount?.current ? Number(participantsCount?.current).toLocaleString() : 0}
85
+ icon="partnerIcon"
86
+ change={participantsCountChange}
87
+ />
88
+ </section>
89
+ <section style={{ flex: 1 }}>
90
+ <StatCard
91
+ title={t("Reported Incidents")}
92
+ value={eventIncidents?.current || 0}
93
+ icon="ReportIncidents"
94
+ change={eventIncidentsChange}
95
+ />
96
+ </section>
97
+ </div>
98
+ <div style={{ display: 'flex', gap: '24px' }}>
99
+ <section style={{ flex: 1 }}>
100
+ <CyclePartners cyclePartners={cyclePartners} loading={indicatorsLoading} t={t} />
101
+ </section>
102
+ <section style={{ flex: 1 }}>
103
+ <HealthAndSafety activityData={indicatorsData} loading={indicatorsLoading} t={t} />
104
+ </section>
105
+ </div>
106
+ </Widget>
107
+ </section>
108
+ );
109
+ };
110
+
111
+ export default CycleIndicators;
112
+