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.
- package/dist/components/index.js +767 -551
- package/dist/layouts/index.js +495 -459
- package/dist/pages/index.js +2808 -738
- package/dist/style/datastake/mapbox-gl.css +330 -0
- package/dist/utils/index.js +481 -457
- package/package.json +1 -1
- package/src/@daf/core/components/Charts/ColumnChart/index.jsx +10 -0
- package/src/@daf/core/components/Charts/LineChart/index.jsx +14 -0
- package/src/@daf/core/components/Dashboard/Widget/ActivityIndicators/index.jsx +2 -0
- package/src/@daf/core/components/Dashboard/Widget/StatCard/StatCard.stories.js +226 -0
- package/src/@daf/core/components/Dashboard/Widget/StatCard/index.js +103 -0
- package/src/@daf/core/components/Dashboard/Widget/StatCard/style.js +83 -0
- package/src/@daf/core/components/Icon/configs/Down.js +8 -0
- package/src/@daf/core/components/Icon/configs/Up.js +8 -0
- package/src/@daf/core/components/Icon/configs/index.js +4 -0
- package/src/@daf/core/components/Icon/configs/partnerIcon.js +1 -1
- package/src/@daf/core/components/Sidenav/Menu.jsx +4 -4
- package/src/@daf/pages/Summary/Activities/PlantingCycle/components/AssociatedInformation/index.jsx +43 -0
- package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CommunityParticipation/CommunityStats/helper.js +60 -0
- package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CommunityParticipation/CommunityStats/index.jsx +36 -0
- package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CommunityParticipation/GenderDistribution/helper.js +117 -0
- package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CommunityParticipation/GenderDistribution/index.jsx +49 -0
- package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CommunityParticipation/JobsTimeline/index.jsx +212 -0
- package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CommunityParticipation/index.jsx +72 -0
- package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleIndicators/CyclePartners/helper.js +91 -0
- package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleIndicators/CyclePartners/index.jsx +50 -0
- package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleIndicators/HealthAndSafety/helper.js +134 -0
- package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleIndicators/HealthAndSafety/index.jsx +49 -0
- package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleIndicators/index.jsx +112 -0
- package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleOutcomes/index.jsx +498 -0
- package/src/@daf/pages/Summary/Activities/PlantingCycle/components/KeyInformation/index.jsx +49 -0
- package/src/@daf/pages/Summary/Activities/PlantingCycle/components/PlantingLocations/index.jsx +120 -0
- package/src/@daf/pages/Summary/Activities/PlantingCycle/config.js +5 -10
- package/src/@daf/pages/Summary/Activities/PlantingCycle/helper.js +218 -0
- package/src/@daf/pages/Summary/Activities/PlantingCycle/index.jsx +22 -32
- package/src/@daf/pages/Summary/Activities/Restoration/components/ActivityImagery/index.jsx +29 -0
- package/src/@daf/pages/Summary/Activities/Restoration/components/ActivityLocation/index.jsx +94 -0
- package/src/@daf/pages/Summary/Activities/Restoration/components/WorkersDistribution/index.jsx +49 -0
- package/src/@daf/pages/Summary/Activities/Restoration/index.jsx +16 -138
- 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
|
+
|