datastake-daf 0.6.767 → 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 +1007 -730
- package/dist/layouts/index.js +495 -459
- package/dist/pages/index.js +7914 -6836
- 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/Map/ChainIcon/Markers/StakeholderMarker.js +5 -2
- package/src/@daf/core/components/Dashboard/Map/ChainIcon/index.js +67 -27
- package/src/@daf/core/components/Dashboard/Map/hook.js +26 -32
- 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/Screens/BaseScreen/index.jsx +1 -1
- package/src/@daf/core/components/Screens/TableScreen/TablePageWithTabs/index.jsx +1 -1
- package/src/@daf/core/components/Sidenav/Menu.jsx +4 -4
- package/src/@daf/core/components/UI/MissingTagButton/index.jsx +36 -0
- package/src/@daf/pages/Dashboards/SupplyChain/components/SupplyChainMap/index.js +0 -2
- package/src/@daf/pages/Documents/config.js +0 -10
- package/src/@daf/pages/Documents/index.jsx +51 -108
- package/src/@daf/pages/Events/Activities/config.js +1 -11
- package/src/@daf/pages/Events/Activities/index.jsx +47 -105
- package/src/@daf/pages/Events/Incidents/config.js +1 -11
- package/src/@daf/pages/Events/Incidents/index.jsx +47 -105
- package/src/@daf/pages/Events/config.js +18 -34
- package/src/@daf/pages/Events/index.jsx +49 -111
- package/src/@daf/pages/Locations/MineSite/config.js +0 -10
- package/src/@daf/pages/Locations/MineSite/index.jsx +47 -105
- package/src/@daf/pages/Locations/config.js +4 -16
- package/src/@daf/pages/Locations/index.jsx +53 -110
- package/src/@daf/pages/Stakeholders/Operators/config.js +0 -10
- package/src/@daf/pages/Stakeholders/Operators/index.jsx +47 -105
- package/src/@daf/pages/Stakeholders/Workers/config.js +0 -10
- package/src/@daf/pages/Stakeholders/Workers/index.jsx +47 -105
- package/src/@daf/pages/Stakeholders/config.js +3 -15
- package/src/@daf/pages/Stakeholders/index.jsx +53 -109
- 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/@daf/pages/TablePage/config.js +78 -0
- package/src/@daf/{core/components/Screens/TableScreen/TableWithTabsAndCreate → pages/TablePage}/create.jsx +6 -5
- package/src/@daf/pages/TablePage/hook.js +123 -0
- package/src/@daf/pages/TablePage/index.jsx +142 -0
- package/src/index.js +2 -0
- package/src/@daf/core/components/Screens/TableScreen/TableWithTabsAndCreate/index.jsx +0 -115
- package/src/@daf/pages/Documents/create.jsx +0 -105
- package/src/@daf/pages/Events/Activities/create.jsx +0 -104
- package/src/@daf/pages/Events/Incidents/create.jsx +0 -104
- package/src/@daf/pages/Events/create.jsx +0 -104
- package/src/@daf/pages/Locations/MineSite/create.jsx +0 -104
- package/src/@daf/pages/Locations/create.jsx +0 -104
- package/src/@daf/pages/Stakeholders/Operators/create.jsx +0 -104
- package/src/@daf/pages/Stakeholders/Workers/create.jsx +0 -104
- package/src/@daf/pages/Stakeholders/create.jsx +0 -105
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
import React
|
|
2
|
-
import TablePageWithTabs from '../../core/components/Screens/TableScreen/TablePageWithTabs/index.jsx'
|
|
1
|
+
import React from 'react'
|
|
3
2
|
import { getColumns } from './columns.js';
|
|
4
|
-
import
|
|
5
|
-
import { useGetQueryParams } from '../../hooks/useGetQueryParams.js';
|
|
6
|
-
import StakeholdersCreate from './create.jsx';
|
|
7
|
-
import { displayMessage } from '../../../helpers/messages.js';
|
|
8
|
-
import DAFHeader from '../../core/components/Header/index.jsx';
|
|
9
|
-
import { useFetchData } from '../hook.js';
|
|
3
|
+
import TablePage from '../TablePage/index.jsx';
|
|
10
4
|
|
|
11
5
|
const StakeholdersTable = ({
|
|
12
6
|
t = () => {},
|
|
@@ -38,109 +32,59 @@ const StakeholdersTable = ({
|
|
|
38
32
|
subjectClear = () => {},
|
|
39
33
|
breadcrumbs = [],
|
|
40
34
|
extendingFilters = {},
|
|
35
|
+
createDefaultValues = {},
|
|
41
36
|
}) => {
|
|
42
|
-
const [selectOptions, setSelectOptions] = useState();
|
|
43
|
-
const [activeTab, setActiveTab] = useState("own");
|
|
44
|
-
const columns = useMemo(() => getColumns({
|
|
45
|
-
t,
|
|
46
|
-
goTo,
|
|
47
|
-
user,
|
|
48
|
-
options,
|
|
49
|
-
activeTab,
|
|
50
|
-
getRedirectLink,
|
|
51
|
-
theme,
|
|
52
|
-
subject: 'stakeholders',
|
|
53
|
-
applications,
|
|
54
|
-
}), [t, goTo, user, options, activeTab, getRedirectLink, theme, applications]);
|
|
55
|
-
|
|
56
|
-
useFetchData({
|
|
57
|
-
location,
|
|
58
|
-
getData,
|
|
59
|
-
activeTab,
|
|
60
|
-
extendingFilters,
|
|
61
|
-
subject: 'stakeholders',
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
const selectFiltersConfig = useMemo(() => {
|
|
65
|
-
return getFiltersConfig({t});
|
|
66
|
-
}, [t]);
|
|
67
|
-
|
|
68
|
-
useEffect(() => {
|
|
69
|
-
setSelectOptions((prev) => ({
|
|
70
|
-
...prev,
|
|
71
|
-
...getFilterOptions(options, t),
|
|
72
|
-
}))
|
|
73
|
-
}, [options, t])
|
|
74
|
-
|
|
75
|
-
const handleActiveTabChange = useCallback((value) => {
|
|
76
|
-
setActiveTab(value);
|
|
77
|
-
}, []);
|
|
78
|
-
|
|
79
|
-
useEffect(
|
|
80
|
-
() => () => {
|
|
81
|
-
subjectClear();
|
|
82
|
-
},
|
|
83
|
-
[],
|
|
84
|
-
);
|
|
85
|
-
|
|
86
37
|
return (
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
changeAjaxOptions={changeAjaxOptions}
|
|
138
|
-
formData={formData}
|
|
139
|
-
formValue={formValue}
|
|
140
|
-
form={form}
|
|
141
|
-
/>
|
|
142
|
-
)}
|
|
143
|
-
</TablePageWithTabs>
|
|
38
|
+
<TablePage
|
|
39
|
+
t={t}
|
|
40
|
+
goTo={goTo}
|
|
41
|
+
user={user}
|
|
42
|
+
options={options}
|
|
43
|
+
getRedirectLink={getRedirectLink}
|
|
44
|
+
theme={theme}
|
|
45
|
+
loading={loading}
|
|
46
|
+
data={data}
|
|
47
|
+
isMobile={isMobile}
|
|
48
|
+
APP={APP}
|
|
49
|
+
location={location}
|
|
50
|
+
applications={applications}
|
|
51
|
+
|
|
52
|
+
subject="stakeholders"
|
|
53
|
+
getData={getData}
|
|
54
|
+
getApiBaseUrl={getApiBaseUrl}
|
|
55
|
+
getAppHeader={getAppHeader}
|
|
56
|
+
subjectClear={subjectClear}
|
|
57
|
+
getColumns={getColumns}
|
|
58
|
+
|
|
59
|
+
viewConfig={{
|
|
60
|
+
title: "Stakeholders",
|
|
61
|
+
breadcrumbs: breadcrumbs,
|
|
62
|
+
createTitle: "Create Stakeholder",
|
|
63
|
+
}}
|
|
64
|
+
|
|
65
|
+
extendingFilters={extendingFilters}
|
|
66
|
+
|
|
67
|
+
formConfig={{
|
|
68
|
+
getFormData,
|
|
69
|
+
saveFormData,
|
|
70
|
+
formLoading,
|
|
71
|
+
query,
|
|
72
|
+
ajaxForms,
|
|
73
|
+
changeAjaxForms,
|
|
74
|
+
ajaxOptions,
|
|
75
|
+
changeAjaxOptions,
|
|
76
|
+
formData,
|
|
77
|
+
formValue,
|
|
78
|
+
form,
|
|
79
|
+
namespace: 'stakeholders',
|
|
80
|
+
view: 'scoping',
|
|
81
|
+
scope: 'create',
|
|
82
|
+
formType: 'stakeholder',
|
|
83
|
+
defaultValues: createDefaultValues,
|
|
84
|
+
}}
|
|
85
|
+
|
|
86
|
+
onDownload={() => console.log("download")}
|
|
87
|
+
/>
|
|
144
88
|
)
|
|
145
89
|
}
|
|
146
90
|
|
package/src/@daf/pages/Summary/Activities/PlantingCycle/components/AssociatedInformation/index.jsx
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Widget } from '../../../../../../../../src/index.js';
|
|
3
|
+
|
|
4
|
+
const ACTIVITIES_TAB = 'activities';
|
|
5
|
+
const PARTNERS_TAB = 'partners';
|
|
6
|
+
const INCIDENTS_TAB = 'incidents';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
const AssociatedInformation = ({
|
|
10
|
+
activityData,
|
|
11
|
+
loading = false,
|
|
12
|
+
t = (s) => s
|
|
13
|
+
}) => {
|
|
14
|
+
const [activeTab, setActiveTab] = useState(ACTIVITIES_TAB);
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<section>
|
|
18
|
+
|
|
19
|
+
<Widget
|
|
20
|
+
className="v2-widget no-px h-w-btn-header no-p-body"
|
|
21
|
+
title={t("Associated Information")}
|
|
22
|
+
tabsConfig={{
|
|
23
|
+
tabs: [
|
|
24
|
+
{ label: t("straatos::activities"), value: ACTIVITIES_TAB },
|
|
25
|
+
{ label: t("straatos::partners"), value: PARTNERS_TAB },
|
|
26
|
+
{ label: t("straatos::incidents"), value: INCIDENTS_TAB },
|
|
27
|
+
],
|
|
28
|
+
value: activeTab,
|
|
29
|
+
onChange: (value) => {
|
|
30
|
+
setActiveTab(value);
|
|
31
|
+
// setData([]);
|
|
32
|
+
},
|
|
33
|
+
}}
|
|
34
|
+
>
|
|
35
|
+
<div>
|
|
36
|
+
</div>
|
|
37
|
+
</Widget>
|
|
38
|
+
</section>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export default AssociatedInformation;
|
|
43
|
+
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { calculateStatChange } from '../../../helper.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Calculates the change for day jobs stat
|
|
5
|
+
* @param {Object} dayJobs - Object with current and previous day jobs values
|
|
6
|
+
* @param {Function} t - Translation function
|
|
7
|
+
* @returns {Object|null} Change object for StatCard or null if data is invalid
|
|
8
|
+
*/
|
|
9
|
+
export const calculateDayJobsChange = (dayJobs, t) => {
|
|
10
|
+
if (!dayJobs) return null;
|
|
11
|
+
return calculateStatChange(
|
|
12
|
+
{
|
|
13
|
+
current: Number(dayJobs.current) || 0,
|
|
14
|
+
previous: Number(dayJobs.previous) || 0,
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
tooltipText: t("In comparison to last cycle"),
|
|
18
|
+
format: 'absolute',
|
|
19
|
+
}
|
|
20
|
+
);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Calculates the change for employed women stat
|
|
25
|
+
* @param {Object} employedWomen - Object with current and previous employed women values
|
|
26
|
+
* @param {Function} t - Translation function
|
|
27
|
+
* @returns {Object|null} Change object for StatCard or null if data is invalid
|
|
28
|
+
*/
|
|
29
|
+
export const calculateEmployedWomenChange = (employedWomen, t) => {
|
|
30
|
+
if (!employedWomen) return null;
|
|
31
|
+
return calculateStatChange(
|
|
32
|
+
{
|
|
33
|
+
current: Number(employedWomen.current) || 0,
|
|
34
|
+
previous: Number(employedWomen.previous) || 0,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
tooltipText: t("In comparison to last cycle"),
|
|
38
|
+
format: 'absolute',
|
|
39
|
+
}
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Formats the day jobs value for display
|
|
45
|
+
* @param {Object} dayJobs - Object with current day jobs value
|
|
46
|
+
* @returns {string} Formatted value string
|
|
47
|
+
*/
|
|
48
|
+
export const formatDayJobsValue = (dayJobs) => {
|
|
49
|
+
return dayJobs?.current ? Number(dayJobs.current).toLocaleString() : 0;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Formats the employed women value for display
|
|
54
|
+
* @param {Object} employedWomen - Object with current employed women value
|
|
55
|
+
* @returns {string} Formatted value string
|
|
56
|
+
*/
|
|
57
|
+
export const formatEmployedWomenValue = (employedWomen) => {
|
|
58
|
+
return employedWomen?.current ? Number(employedWomen.current).toLocaleString() : 0;
|
|
59
|
+
};
|
|
60
|
+
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { StatCard } from '../../../../../../../../index.js';
|
|
3
|
+
import { calculateDayJobsChange, calculateEmployedWomenChange, formatDayJobsValue, formatEmployedWomenValue } from './helper.js';
|
|
4
|
+
|
|
5
|
+
const CommunityStats = ({
|
|
6
|
+
dayJobs,
|
|
7
|
+
employedWomen,
|
|
8
|
+
t = (s) => s
|
|
9
|
+
}) => {
|
|
10
|
+
const dayJobsChange = useMemo(() => calculateDayJobsChange(dayJobs, t), [dayJobs, t]);
|
|
11
|
+
const employedWomenChange = useMemo(() => calculateEmployedWomenChange(employedWomen, t), [employedWomen, t]);
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<div style={{ display: 'flex', gap: '24px', marginBottom: '24px' }}>
|
|
15
|
+
<section style={{ flex: 1 }}>
|
|
16
|
+
<StatCard
|
|
17
|
+
title={t("Total Jobs")}
|
|
18
|
+
value={formatDayJobsValue(dayJobs)}
|
|
19
|
+
icon="MineOperators"
|
|
20
|
+
change={dayJobsChange}
|
|
21
|
+
/>
|
|
22
|
+
</section>
|
|
23
|
+
<section style={{ flex: 1 }}>
|
|
24
|
+
<StatCard
|
|
25
|
+
title={t("Employed Women")}
|
|
26
|
+
value={formatEmployedWomenValue(employedWomen)}
|
|
27
|
+
icon="GenderFemale"
|
|
28
|
+
change={employedWomenChange}
|
|
29
|
+
/>
|
|
30
|
+
</section>
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export default CommunityStats;
|
|
36
|
+
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
const GENDER_COLORS = ['#a0ebec', '#00aeb1'];
|
|
2
|
+
|
|
3
|
+
export const getGenderDistributionData = (genderDistribution) => {
|
|
4
|
+
if (Array.isArray(genderDistribution) && genderDistribution.length > 0) {
|
|
5
|
+
const firstItem = genderDistribution[0];
|
|
6
|
+
|
|
7
|
+
if (firstItem && (firstItem.totalFemale !== undefined || firstItem.totalMale !== undefined)) {
|
|
8
|
+
const totalFemale = genderDistribution.reduce((sum, item) => sum + (Number(item.totalFemale) || 0), 0);
|
|
9
|
+
const totalMale = genderDistribution.reduce((sum, item) => sum + (Number(item.totalMale) || 0), 0);
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
Male: totalMale,
|
|
13
|
+
Female: totalFemale,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const distribution = {};
|
|
18
|
+
genderDistribution.forEach(item => {
|
|
19
|
+
const gender = item?.gender || item?.label || item?.name || 'Unknown';
|
|
20
|
+
const count = item?.count || item?.value || item?.total || 0;
|
|
21
|
+
const normalizedGender = gender.toLowerCase() === 'male' ? 'Male' :
|
|
22
|
+
gender.toLowerCase() === 'female' ? 'Female' : gender;
|
|
23
|
+
distribution[normalizedGender] = (distribution[normalizedGender] || 0) + count;
|
|
24
|
+
});
|
|
25
|
+
return distribution;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (genderDistribution && typeof genderDistribution === 'object' && !Array.isArray(genderDistribution)) {
|
|
29
|
+
if (genderDistribution.genderDistribution && typeof genderDistribution.genderDistribution === 'object') {
|
|
30
|
+
return genderDistribution.genderDistribution;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (genderDistribution.totalFemale !== undefined || genderDistribution.totalMale !== undefined) {
|
|
34
|
+
return {
|
|
35
|
+
Male: Number(genderDistribution.totalMale) || 0,
|
|
36
|
+
Female: Number(genderDistribution.totalFemale) || 0,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (genderDistribution.Male !== undefined || genderDistribution.Female !== undefined ||
|
|
41
|
+
genderDistribution.male !== undefined || genderDistribution.female !== undefined) {
|
|
42
|
+
return {
|
|
43
|
+
Male: genderDistribution.Male || genderDistribution.male || 0,
|
|
44
|
+
Female: genderDistribution.Female || genderDistribution.female || 0,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return genderDistribution;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {};
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Checks if the gender distribution data is empty
|
|
56
|
+
* @param {Object} genderDistributionData - Distribution object: { "Male": 10, "Female": 5 }
|
|
57
|
+
* @returns {boolean} True if all values are 0 or empty
|
|
58
|
+
*/
|
|
59
|
+
export const isGenderDistributionEmpty = (genderDistributionData) => {
|
|
60
|
+
return Object.values(genderDistributionData).every(val => !val || val === 0);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Calculates pie chart data from gender distribution
|
|
65
|
+
* @param {Object} genderDistributionData - Distribution object: { "Male": 10, "Female": 5 }
|
|
66
|
+
* @returns {Array} Array of pie chart data points with value, percent, color, label, and key
|
|
67
|
+
*/
|
|
68
|
+
export const calculateGenderPieData = (genderDistributionData) => {
|
|
69
|
+
const total = Object.values(genderDistributionData).reduce((all, val) => all + (val || 0), 0);
|
|
70
|
+
|
|
71
|
+
return Object.keys(genderDistributionData).map((key, index) => {
|
|
72
|
+
const color = GENDER_COLORS[index % GENDER_COLORS.length];
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
value: genderDistributionData[key] || 0,
|
|
76
|
+
percent: total > 0 ? (genderDistributionData[key] || 0) / total : 0,
|
|
77
|
+
color: color,
|
|
78
|
+
label: key,
|
|
79
|
+
key: key,
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export const getGenderTooltipChildren = (item, isEmpty, genderDistributionData, t, renderTooltipJsx) => {
|
|
85
|
+
if (isEmpty) {
|
|
86
|
+
if (!Object.keys(genderDistributionData).length) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return renderTooltipJsx({
|
|
91
|
+
title: t("Gender Distribution"),
|
|
92
|
+
items: Object.keys(genderDistributionData).map((k) => ({
|
|
93
|
+
color: GENDER_COLORS[Object.keys(genderDistributionData).indexOf(k) % GENDER_COLORS.length],
|
|
94
|
+
label: k,
|
|
95
|
+
value: '0%',
|
|
96
|
+
})),
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Calculate total for percentage calculation
|
|
101
|
+
const total = Object.values(genderDistributionData).reduce((all, val) => all + (val || 0), 0);
|
|
102
|
+
|
|
103
|
+
// Show all gender items with percentages (matching the image format)
|
|
104
|
+
return renderTooltipJsx({
|
|
105
|
+
title: t("Gender Distribution"),
|
|
106
|
+
items: Object.keys(genderDistributionData).map((k, index) => {
|
|
107
|
+
const value = genderDistributionData[k] || 0;
|
|
108
|
+
const percent = total > 0 ? Math.round((value / total) * 100) : 0;
|
|
109
|
+
return {
|
|
110
|
+
color: GENDER_COLORS[index % GENDER_COLORS.length],
|
|
111
|
+
label: k,
|
|
112
|
+
value: `${percent}%`,
|
|
113
|
+
};
|
|
114
|
+
}),
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React, { useMemo, useCallback } from 'react';
|
|
2
|
+
import { Widget, PieChart } from '../../../../../../../../index.js';
|
|
3
|
+
import { getGenderDistributionData, isGenderDistributionEmpty, calculateGenderPieData, getGenderTooltipChildren } from './helper';
|
|
4
|
+
import { renderTooltipJsx } from '../../../../../../../../utils.js';
|
|
5
|
+
|
|
6
|
+
const GenderDistribution = ({
|
|
7
|
+
genderDistribution,
|
|
8
|
+
loading = false,
|
|
9
|
+
t = (s) => s
|
|
10
|
+
}) => {
|
|
11
|
+
const genderDistributionData = useMemo(() => getGenderDistributionData(genderDistribution), [genderDistribution]);
|
|
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("Gender 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 GenderDistribution;
|
|
49
|
+
|