datastake-daf 0.6.757 → 0.6.759
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 +3299 -2881
- package/dist/layouts/index.js +476 -452
- package/dist/pages/index.js +836 -662
- package/dist/style/datastake/mapbox-gl.css +330 -0
- package/dist/utils/index.js +481 -802
- package/package.json +1 -1
- package/rollup.config.js +0 -20
- package/src/@daf/core/components/Dashboard/Map/Map.stories.js +8 -0
- package/src/@daf/core/components/Dashboard/Map/helper.js +134 -3
- package/src/@daf/core/components/Dashboard/Map/hook.js +4 -0
- package/src/@daf/core/components/Dashboard/Map/index.jsx +19 -0
- package/src/@daf/core/components/Dashboard/Map/storyConfig.js +2 -1
- package/src/@daf/core/components/Dashboard/Map/storyConfig6.js +69 -0
- package/src/@daf/core/components/Dashboard/Widget/ActivityIndicators/config.js +2 -5
- package/src/@daf/core/components/Dashboard/Widget/KeyIndicators/index.jsx +1 -1
- package/src/@daf/core/components/EditForm/form.jsx +1 -1
- package/src/@daf/core/components/EditForm/storyConfig2.js +25028 -865
- package/src/@daf/core/components/Graphs/components/BaseGraph.jsx +1 -1
- package/src/@daf/core/components/Icon/configs/SpacingHeight.js +8 -0
- package/src/@daf/core/components/Icon/configs/SpacingWidth.js +8 -0
- package/src/@daf/core/components/Icon/configs/index.js +5 -1
- package/src/@daf/hooks/useMapHelper.js +15 -1
- package/src/@daf/pages/Dashboards/SupplyChain/index.jsx +2 -2
- package/src/@daf/pages/Documents/index.jsx +3 -2
- package/src/@daf/pages/Events/Activities/index.jsx +3 -2
- package/src/@daf/pages/Events/Incidents/index.jsx +3 -2
- package/src/@daf/pages/Events/index.jsx +3 -2
- package/src/@daf/pages/Locations/MineSite/columns.js +5 -7
- package/src/@daf/pages/Locations/MineSite/index.jsx +4 -5
- package/src/@daf/pages/Locations/index.jsx +3 -2
- package/src/@daf/pages/Stakeholders/Operators/index.jsx +2 -2
- package/src/@daf/pages/Stakeholders/Workers/index.jsx +3 -2
- package/src/@daf/pages/Stakeholders/index.jsx +3 -2
- package/src/@daf/pages/Summary/Activities/Restoration/helper.js +133 -79
- package/src/@daf/pages/Summary/Activities/Restoration/index.jsx +57 -60
- package/src/@daf/pages/Summary/Minesite/index.jsx +0 -2
- package/src/@daf/pages/Summary/Operator/index.jsx +1 -3
- package/src/@daf/utils/tooltip.js +5 -2
- package/src/utils.js +1 -3
- package/dist/constants/index.js +0 -26
- package/src/constants/breadCrumbs.js +0 -20
- package/src/constants.js +0 -1
- package/src/helpers/breadCrumbs.js +0 -347
|
@@ -86,7 +86,7 @@ const BaseGraph = forwardRef(function BaseGraph(
|
|
|
86
86
|
fitView={true} // zoom out on default
|
|
87
87
|
fitViewOptions={{
|
|
88
88
|
padding: 0.2, //zoom out on default
|
|
89
|
-
duration: withDuration ? 300 : undefined,
|
|
89
|
+
// duration: withDuration ? 300 : undefined,
|
|
90
90
|
}}
|
|
91
91
|
{...props}
|
|
92
92
|
>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
const config = {
|
|
2
|
+
viewBox: "0 0 12 12",
|
|
3
|
+
children: (
|
|
4
|
+
<path d="M5.6875 9.1875L5.6875 2.1875M5.6875 9.1875L3.9375 8.02083M5.6875 9.1875L7.4375 8.02083M5.6875 2.1875L3.9375 3.35417M5.6875 2.1875L7.4375 3.35417M10.9375 0.4375H0.4375M10.9375 10.9375H0.4375" stroke="currentColor" strokeWidth="0.875" strokeLinecap="round" strokeLinejoin="round"/>
|
|
5
|
+
),
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export default config;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
const config = {
|
|
2
|
+
viewBox: "0 0 12 12",
|
|
3
|
+
children: (
|
|
4
|
+
<path d="M2.1875 5.6875H9.1875M2.1875 5.6875L3.35417 3.9375M2.1875 5.6875L3.35417 7.4375M9.1875 5.6875L8.02083 3.9375M9.1875 5.6875L8.02083 7.4375M10.9375 10.9375V0.437501M0.4375 10.9375V0.4375" stroke="currentColor" strokeWidth="0.875" strokeLinecap="round" strokeLinejoin="round"/>
|
|
5
|
+
),
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export default config;
|
|
@@ -223,7 +223,9 @@ import Bear from "./Bear";
|
|
|
223
223
|
import Security from "./Security";
|
|
224
224
|
import Minus from "./Minus";
|
|
225
225
|
import ClockPlus from "./ClockPlus";
|
|
226
|
-
import WaziDarkIcon from "./WaziDarkIcon";
|
|
226
|
+
import WaziDarkIcon from "./WaziDarkIcon";
|
|
227
|
+
import SpacingHeight from "./SpacingHeight";
|
|
228
|
+
import SpacingWidth from "./SpacingWidth";
|
|
227
229
|
|
|
228
230
|
const config = {
|
|
229
231
|
AppAdmin,
|
|
@@ -452,6 +454,8 @@ const config = {
|
|
|
452
454
|
Minus,
|
|
453
455
|
ClockPlus,
|
|
454
456
|
WaziDarkIcon,
|
|
457
|
+
SpacingHeight,
|
|
458
|
+
SpacingWidth,
|
|
455
459
|
};
|
|
456
460
|
|
|
457
461
|
export default config;
|
|
@@ -15,9 +15,23 @@ export const filterValidGPS = (data) => {
|
|
|
15
15
|
const latCheck = (lat) => lat > -90 || lat < 90;
|
|
16
16
|
const lngCheck = (lng) => lng > -180 || lng < 180;
|
|
17
17
|
return data.filter(item => {
|
|
18
|
-
|
|
18
|
+
// Check if item has valid GPS coordinates
|
|
19
|
+
const hasValidGPS = item.marker &&
|
|
19
20
|
latCheck(Number(item.marker.lat)) &&
|
|
20
21
|
lngCheck(Number(item.marker.lng));
|
|
22
|
+
|
|
23
|
+
const hasValidArea = item.area &&
|
|
24
|
+
Array.isArray(item.area) &&
|
|
25
|
+
item.area.length >= 3 &&
|
|
26
|
+
item.area.every(coord =>
|
|
27
|
+
Array.isArray(coord) &&
|
|
28
|
+
coord.length >= 2 &&
|
|
29
|
+
!isNaN(coord[0]) && !isNaN(coord[1]) &&
|
|
30
|
+
isFinite(coord[0]) && isFinite(coord[1])
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
// Include item if it has either valid GPS or valid area
|
|
34
|
+
return hasValidGPS || hasValidArea;
|
|
21
35
|
});
|
|
22
36
|
}
|
|
23
37
|
|
|
@@ -20,7 +20,7 @@ function SupplyChain({
|
|
|
20
20
|
setSelectedPartners,
|
|
21
21
|
informationSources,
|
|
22
22
|
hardcodedData,
|
|
23
|
-
|
|
23
|
+
breadCrumbs = [],
|
|
24
24
|
}) {
|
|
25
25
|
|
|
26
26
|
const sourceOptions = useMemo(() => {
|
|
@@ -69,7 +69,7 @@ function SupplyChain({
|
|
|
69
69
|
/>
|
|
70
70
|
</div>
|
|
71
71
|
}
|
|
72
|
-
breadcrumbs={
|
|
72
|
+
breadcrumbs={breadCrumbs}
|
|
73
73
|
/>
|
|
74
74
|
}
|
|
75
75
|
>
|
|
@@ -34,7 +34,6 @@ const DocumentsTable = ({
|
|
|
34
34
|
form = {},
|
|
35
35
|
applications = [],
|
|
36
36
|
subjectClear = () => {},
|
|
37
|
-
breadcrumbs = [],
|
|
38
37
|
}) => {
|
|
39
38
|
const [selectOptions, setSelectOptions] = useState();
|
|
40
39
|
const [activeTab, setActiveTab] = useState("own");
|
|
@@ -50,6 +49,8 @@ const DocumentsTable = ({
|
|
|
50
49
|
applications,
|
|
51
50
|
}), [t, goTo, user, options, activeTab, getRedirectLink, theme, applications]);
|
|
52
51
|
|
|
52
|
+
const breadCrumbs = [];
|
|
53
|
+
|
|
53
54
|
const { paginationQuery, searchParams, otherParams, sortBy, sortDir } = useGetQueryParams({location});
|
|
54
55
|
|
|
55
56
|
useEffect(() => {
|
|
@@ -90,7 +91,7 @@ const DocumentsTable = ({
|
|
|
90
91
|
<TablePageWithTabs
|
|
91
92
|
t={t}
|
|
92
93
|
title={t("Documents")}
|
|
93
|
-
breadCrumbs={
|
|
94
|
+
breadCrumbs={breadCrumbs}
|
|
94
95
|
location={location}
|
|
95
96
|
loading={loading}
|
|
96
97
|
goTo={goTo}
|
|
@@ -35,7 +35,6 @@ const ActivitiesTable = ({
|
|
|
35
35
|
extendingFilters = {},
|
|
36
36
|
createDefaultValues = {},
|
|
37
37
|
applications = [],
|
|
38
|
-
breadcrumbs = [],
|
|
39
38
|
}) => {
|
|
40
39
|
const [selectOptions, setSelectOptions] = useState();
|
|
41
40
|
const [activeTab, setActiveTab] = useState("own");
|
|
@@ -53,6 +52,8 @@ const ActivitiesTable = ({
|
|
|
53
52
|
applications,
|
|
54
53
|
}), [t, goTo, user, options, activeTab, getRedirectLink, theme, data, applications]);
|
|
55
54
|
|
|
55
|
+
const breadCrumbs = [];
|
|
56
|
+
|
|
56
57
|
const { paginationQuery, searchParams, otherParams, sortBy, sortDir } = useGetQueryParams({location});
|
|
57
58
|
|
|
58
59
|
const filters = useMemo(() => {
|
|
@@ -94,7 +95,7 @@ const ActivitiesTable = ({
|
|
|
94
95
|
<TablePageWithTabs
|
|
95
96
|
t={t}
|
|
96
97
|
title={t("Activities")}
|
|
97
|
-
breadCrumbs={
|
|
98
|
+
breadCrumbs={breadCrumbs}
|
|
98
99
|
location={location}
|
|
99
100
|
loading={loading}
|
|
100
101
|
goTo={goTo}
|
|
@@ -35,7 +35,6 @@ const IncidentsTable = ({
|
|
|
35
35
|
extendingFilters = {},
|
|
36
36
|
createDefaultValues = {},
|
|
37
37
|
applications = [],
|
|
38
|
-
breadcrumbs = [],
|
|
39
38
|
}) => {
|
|
40
39
|
const [selectOptions, setSelectOptions] = useState();
|
|
41
40
|
const [activeTab, setActiveTab] = useState("own");
|
|
@@ -53,6 +52,8 @@ const IncidentsTable = ({
|
|
|
53
52
|
applications,
|
|
54
53
|
}), [t, goTo, user, options, activeTab, getRedirectLink, theme, data, applications]);
|
|
55
54
|
|
|
55
|
+
const breadCrumbs = [];
|
|
56
|
+
|
|
56
57
|
const { paginationQuery, searchParams, otherParams, sortBy, sortDir } = useGetQueryParams({location});
|
|
57
58
|
|
|
58
59
|
const filters = useMemo(() => {
|
|
@@ -94,7 +95,7 @@ const IncidentsTable = ({
|
|
|
94
95
|
<TablePageWithTabs
|
|
95
96
|
t={t}
|
|
96
97
|
title={t("Incidents")}
|
|
97
|
-
breadCrumbs={
|
|
98
|
+
breadCrumbs={breadCrumbs}
|
|
98
99
|
location={location}
|
|
99
100
|
loading={loading}
|
|
100
101
|
goTo={goTo}
|
|
@@ -35,7 +35,6 @@ const EventsTable = ({
|
|
|
35
35
|
extendingFilters = {},
|
|
36
36
|
applications = [],
|
|
37
37
|
subjectClear = () => {},
|
|
38
|
-
breadcrumbs = [],
|
|
39
38
|
}) => {
|
|
40
39
|
const [selectOptions, setSelectOptions] = useState();
|
|
41
40
|
const [activeTab, setActiveTab] = useState("own");
|
|
@@ -53,6 +52,8 @@ const EventsTable = ({
|
|
|
53
52
|
applications,
|
|
54
53
|
}), [t, goTo, user, options, activeTab, getRedirectLink, theme, data, applications]);
|
|
55
54
|
|
|
55
|
+
const breadCrumbs = [];
|
|
56
|
+
|
|
56
57
|
const { paginationQuery, searchParams, otherParams, sortBy, sortDir } = useGetQueryParams({location});
|
|
57
58
|
|
|
58
59
|
const filters = useMemo(() => {
|
|
@@ -100,7 +101,7 @@ const EventsTable = ({
|
|
|
100
101
|
<TablePageWithTabs
|
|
101
102
|
t={t}
|
|
102
103
|
title={t("events")}
|
|
103
|
-
breadCrumbs={
|
|
104
|
+
breadCrumbs={breadCrumbs}
|
|
104
105
|
location={location}
|
|
105
106
|
loading={loading}
|
|
106
107
|
goTo={goTo}
|
|
@@ -4,9 +4,7 @@ import { findOptions } from '../../../../helpers/StringHelper.js';
|
|
|
4
4
|
import { renderDateFormatted } from '../../../../helpers/Forms.js';
|
|
5
5
|
import CustomIcon from '../../../core/components/Icon/CustomIcon.jsx';
|
|
6
6
|
import AvatarGroup from '../../../core/components/AvatarGroup/index.jsx';
|
|
7
|
-
import sourceAvatarConfig from '../../../../helpers/sourceAvatarConfig.js';
|
|
8
7
|
import MoreMenu from '../../../core/components/Table/MoreMenu/index.jsx';
|
|
9
|
-
|
|
10
8
|
const getLinkValue = (value, linkingObject) => {
|
|
11
9
|
if(linkingObject && linkingObject?.[value]) {
|
|
12
10
|
return linkingObject?.[value]?.name;
|
|
@@ -14,7 +12,7 @@ const getLinkValue = (value, linkingObject) => {
|
|
|
14
12
|
return null;
|
|
15
13
|
}
|
|
16
14
|
|
|
17
|
-
export const getColumns = ({t, goTo, user, options, activeTab, getRedirectLink, theme, subject, data
|
|
15
|
+
export const getColumns = ({t, goTo, user, options, activeTab, getRedirectLink, theme, subject, data}) => [
|
|
18
16
|
{
|
|
19
17
|
dataIndex: 'datastakeId',
|
|
20
18
|
title: t('ID'),
|
|
@@ -131,15 +129,15 @@ export const getColumns = ({t, goTo, user, options, activeTab, getRedirectLink,
|
|
|
131
129
|
|
|
132
130
|
{
|
|
133
131
|
title: t("Sources"),
|
|
134
|
-
dataIndex: '
|
|
132
|
+
dataIndex: 'source',
|
|
135
133
|
ellipsis: true,
|
|
136
134
|
show: activeTab !== "own",
|
|
137
135
|
render: (v, all) => {
|
|
138
136
|
if (all.empty) {
|
|
139
137
|
return <div className="daf-default-cell" />
|
|
140
138
|
}
|
|
141
|
-
const
|
|
142
|
-
return <
|
|
139
|
+
const source = findOptions(v, data?.options?.sourceOptions);
|
|
140
|
+
return source ? <Tooltip title={source}>{source}</Tooltip> : '-';
|
|
143
141
|
},
|
|
144
142
|
},
|
|
145
143
|
{
|
|
@@ -151,7 +149,7 @@ export const getColumns = ({t, goTo, user, options, activeTab, getRedirectLink,
|
|
|
151
149
|
return <div className="daf-default-cell" />;
|
|
152
150
|
}
|
|
153
151
|
const onClick = () => {
|
|
154
|
-
|
|
152
|
+
const link = `/app/view/${subject}/${all.datastakeId}`;
|
|
155
153
|
if (activeTab === "shared") {
|
|
156
154
|
link += `?sourceId=${all?.authorId?.id}`;
|
|
157
155
|
}
|
|
@@ -34,8 +34,6 @@ const ProductionSitesTable = ({
|
|
|
34
34
|
form = {},
|
|
35
35
|
extendingFilters = {},
|
|
36
36
|
createDefaultValues = {},
|
|
37
|
-
applications = [],
|
|
38
|
-
breadcrumbs = [],
|
|
39
37
|
}) => {
|
|
40
38
|
const [selectOptions, setSelectOptions] = useState();
|
|
41
39
|
const [activeTab, setActiveTab] = useState('own');
|
|
@@ -50,8 +48,9 @@ const ProductionSitesTable = ({
|
|
|
50
48
|
theme,
|
|
51
49
|
subject: 'production-sites',
|
|
52
50
|
data,
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
}), [t, goTo, user, options, activeTab, getRedirectLink, theme, data]);
|
|
52
|
+
|
|
53
|
+
const breadCrumbs = [];
|
|
55
54
|
|
|
56
55
|
const { paginationQuery, searchParams, otherParams, sortBy, sortDir } = useGetQueryParams({location});
|
|
57
56
|
|
|
@@ -94,7 +93,7 @@ const ProductionSitesTable = ({
|
|
|
94
93
|
<TablePageWithTabs
|
|
95
94
|
t={t}
|
|
96
95
|
title={t("production-sites")}
|
|
97
|
-
breadCrumbs={
|
|
96
|
+
breadCrumbs={breadCrumbs}
|
|
98
97
|
location={location}
|
|
99
98
|
loading={loading}
|
|
100
99
|
goTo={goTo}
|
|
@@ -34,7 +34,6 @@ const LocationsTable = ({
|
|
|
34
34
|
form = {},
|
|
35
35
|
applications = [],
|
|
36
36
|
subjectClear = () => {},
|
|
37
|
-
breadcrumbs = [],
|
|
38
37
|
}) => {
|
|
39
38
|
const [selectOptions, setSelectOptions] = useState();
|
|
40
39
|
const [activeTab, setActiveTab] = useState("own");
|
|
@@ -52,6 +51,8 @@ const LocationsTable = ({
|
|
|
52
51
|
applications,
|
|
53
52
|
}), [t, goTo, user, options, activeTab, getRedirectLink, theme, data, applications]);
|
|
54
53
|
|
|
54
|
+
const breadCrumbs = [];
|
|
55
|
+
|
|
55
56
|
const { paginationQuery, searchParams, otherParams } = useGetQueryParams({location});
|
|
56
57
|
|
|
57
58
|
useEffect(() => {
|
|
@@ -91,7 +92,7 @@ const LocationsTable = ({
|
|
|
91
92
|
<TablePageWithTabs
|
|
92
93
|
t={t}
|
|
93
94
|
title={t("Locations")}
|
|
94
|
-
breadCrumbs={
|
|
95
|
+
breadCrumbs={breadCrumbs}
|
|
95
96
|
location={location}
|
|
96
97
|
loading={loading}
|
|
97
98
|
goTo={goTo}
|
|
@@ -35,7 +35,6 @@ const OperatorsTable = ({
|
|
|
35
35
|
extendingFilters = {},
|
|
36
36
|
createDefaultValues = {},
|
|
37
37
|
applications = [],
|
|
38
|
-
breadcrumbs = [],
|
|
39
38
|
}) => {
|
|
40
39
|
const [selectOptions, setSelectOptions] = useState();
|
|
41
40
|
const [activeTab, setActiveTab] = useState('own');
|
|
@@ -53,6 +52,7 @@ const OperatorsTable = ({
|
|
|
53
52
|
applications,
|
|
54
53
|
}), [t, goTo, user, options, activeTab, getRedirectLink, theme, data, applications]);
|
|
55
54
|
|
|
55
|
+
const breadCrumbs = [];
|
|
56
56
|
|
|
57
57
|
const { paginationQuery, searchParams, otherParams, sortBy, sortDir } = useGetQueryParams({location});
|
|
58
58
|
|
|
@@ -95,7 +95,7 @@ const OperatorsTable = ({
|
|
|
95
95
|
<TablePageWithTabs
|
|
96
96
|
t={t}
|
|
97
97
|
title={t("Operators")}
|
|
98
|
-
breadCrumbs={
|
|
98
|
+
breadCrumbs={breadCrumbs}
|
|
99
99
|
location={location}
|
|
100
100
|
loading={loading}
|
|
101
101
|
goTo={goTo}
|
|
@@ -35,7 +35,6 @@ const WorkersTable = ({
|
|
|
35
35
|
extendingFilters = {},
|
|
36
36
|
createDefaultValues = {},
|
|
37
37
|
applications = [],
|
|
38
|
-
breadcrumbs = [],
|
|
39
38
|
}) => {
|
|
40
39
|
const [selectOptions, setSelectOptions] = useState();
|
|
41
40
|
const [activeTab, setActiveTab] = useState('own');
|
|
@@ -53,6 +52,8 @@ const WorkersTable = ({
|
|
|
53
52
|
applications,
|
|
54
53
|
}), [t, goTo, user, options, activeTab, getRedirectLink, theme, data, applications]);
|
|
55
54
|
|
|
55
|
+
const breadCrumbs = [];
|
|
56
|
+
|
|
56
57
|
const { paginationQuery, searchParams, otherParams, sortBy, sortDir } = useGetQueryParams({location});
|
|
57
58
|
|
|
58
59
|
const filters = useMemo(() => {
|
|
@@ -94,7 +95,7 @@ const WorkersTable = ({
|
|
|
94
95
|
<TablePageWithTabs
|
|
95
96
|
t={t}
|
|
96
97
|
title={t("Workers")}
|
|
97
|
-
breadCrumbs={
|
|
98
|
+
breadCrumbs={breadCrumbs}
|
|
98
99
|
location={location}
|
|
99
100
|
loading={loading}
|
|
100
101
|
goTo={goTo}
|
|
@@ -35,7 +35,6 @@ const StakeholdersTable = ({
|
|
|
35
35
|
form = {},
|
|
36
36
|
applications = [],
|
|
37
37
|
subjectClear = () => {},
|
|
38
|
-
breadcrumbs = [],
|
|
39
38
|
}) => {
|
|
40
39
|
const [selectOptions, setSelectOptions] = useState();
|
|
41
40
|
const [activeTab, setActiveTab] = useState("own");
|
|
@@ -51,6 +50,8 @@ const StakeholdersTable = ({
|
|
|
51
50
|
applications,
|
|
52
51
|
}), [t, goTo, user, options, activeTab, getRedirectLink, theme, applications]);
|
|
53
52
|
|
|
53
|
+
const breadCrumbs = [];
|
|
54
|
+
|
|
54
55
|
const { paginationQuery, searchParams, otherParams } = useGetQueryParams({location});
|
|
55
56
|
|
|
56
57
|
useEffect(() => {
|
|
@@ -88,7 +89,7 @@ const StakeholdersTable = ({
|
|
|
88
89
|
<TablePageWithTabs
|
|
89
90
|
t={t}
|
|
90
91
|
title={t("Stakeholders")}
|
|
91
|
-
breadCrumbs={
|
|
92
|
+
breadCrumbs={breadCrumbs}
|
|
92
93
|
location={location}
|
|
93
94
|
loading={loading}
|
|
94
95
|
goTo={goTo}
|
|
@@ -1,16 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
// REGION: Photo/Image Extraction
|
|
3
|
-
// ============================================================================
|
|
1
|
+
import L from "leaflet";
|
|
4
2
|
|
|
5
|
-
/**
|
|
6
|
-
* Normalize URL by removing trailing colon if present
|
|
7
|
-
*/
|
|
8
3
|
const normalizeUrl = (url) => url?.endsWith(':') ? url.slice(0, -1) : url;
|
|
9
4
|
|
|
10
|
-
|
|
11
|
-
* Extract images from a photo document
|
|
12
|
-
* Handles both documents with pictures arrays and direct image objects
|
|
13
|
-
*/
|
|
5
|
+
|
|
14
6
|
export const extractFromPhotoDoc = (photoDoc, label, docIndex) => {
|
|
15
7
|
const photos = photoDoc?.pictures?.filter(p => p?.url) || (photoDoc?.url ? [photoDoc] : []);
|
|
16
8
|
|
|
@@ -20,13 +12,6 @@ export const extractFromPhotoDoc = (photoDoc, label, docIndex) => {
|
|
|
20
12
|
}));
|
|
21
13
|
};
|
|
22
14
|
|
|
23
|
-
/**
|
|
24
|
-
* Extract and process images from activity data
|
|
25
|
-
* Processes group photos and activity photos (start, during, end) into a flat array
|
|
26
|
-
*
|
|
27
|
-
* @param {Object} activityData - Activity data object containing photo arrays
|
|
28
|
-
* @returns {Array} - Flat array of image objects with src and alt properties
|
|
29
|
-
*/
|
|
30
15
|
export const getActivityImages = (activityData) => {
|
|
31
16
|
const photoArrays = [
|
|
32
17
|
{ data: activityData?.groupPhotos, label: 'Group Photo' },
|
|
@@ -43,17 +28,8 @@ export const getActivityImages = (activityData) => {
|
|
|
43
28
|
);
|
|
44
29
|
};
|
|
45
30
|
|
|
46
|
-
|
|
47
|
-
// REGION: Gender Distribution
|
|
48
|
-
// ============================================================================
|
|
49
|
-
|
|
50
|
-
// Gender distribution colors for pie chart
|
|
51
|
-
const GENDER_COLORS = ['#016C6E', '#00AEB1']; // Male (dark teal), Female (light teal)
|
|
31
|
+
const GENDER_COLORS = ['#016C6E', '#00AEB1'];
|
|
52
32
|
|
|
53
|
-
/**
|
|
54
|
-
* Get gender distribution data from activityData
|
|
55
|
-
* Maps genderDistributionMale and genderDistributionFemale to a structured object
|
|
56
|
-
*/
|
|
57
33
|
export const getGenderDistributionData = (activityData) => {
|
|
58
34
|
return {
|
|
59
35
|
Male: activityData?.genderDistributionMale || 0,
|
|
@@ -61,18 +37,11 @@ export const getGenderDistributionData = (activityData) => {
|
|
|
61
37
|
};
|
|
62
38
|
};
|
|
63
39
|
|
|
64
|
-
|
|
65
|
-
* Check if gender distribution is empty
|
|
66
|
-
* Returns true if all values are 0 or falsy
|
|
67
|
-
*/
|
|
40
|
+
|
|
68
41
|
export const isGenderDistributionEmpty = (genderDistributionData) => {
|
|
69
42
|
return Object.values(genderDistributionData).every(val => !val || val === 0);
|
|
70
43
|
};
|
|
71
44
|
|
|
72
|
-
/**
|
|
73
|
-
* Calculate pie chart data from gender distribution
|
|
74
|
-
* Computes percentages and assigns colors based on gender distribution values
|
|
75
|
-
*/
|
|
76
45
|
export const calculateGenderPieData = (genderDistributionData) => {
|
|
77
46
|
const total = Object.values(genderDistributionData).reduce((all, val) => all + (val || 0), 0);
|
|
78
47
|
|
|
@@ -89,10 +58,6 @@ export const calculateGenderPieData = (genderDistributionData) => {
|
|
|
89
58
|
});
|
|
90
59
|
};
|
|
91
60
|
|
|
92
|
-
/**
|
|
93
|
-
* Get tooltip children for gender distribution pie chart
|
|
94
|
-
* Generates tooltip content showing gender label and value
|
|
95
|
-
*/
|
|
96
61
|
export const getGenderTooltipChildren = (item, isEmpty, genderDistributionData, t, renderTooltipJsx) => {
|
|
97
62
|
if (isEmpty) {
|
|
98
63
|
if (!Object.keys(genderDistributionData).length) {
|
|
@@ -120,16 +85,6 @@ export const getGenderTooltipChildren = (item, isEmpty, genderDistributionData,
|
|
|
120
85
|
});
|
|
121
86
|
};
|
|
122
87
|
|
|
123
|
-
// ============================================================================
|
|
124
|
-
// REGION: Activity Indicators
|
|
125
|
-
// ============================================================================
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Maps activityData value to indicator type
|
|
129
|
-
* "yes" or true → "compliant"
|
|
130
|
-
* "no" or false → "notCompliant"
|
|
131
|
-
* null or undefined → "empty"
|
|
132
|
-
*/
|
|
133
88
|
export const getIndicatorType = (value) => {
|
|
134
89
|
if (value === "yes" || value === true) return "compliant";
|
|
135
90
|
if (value === "no" || value === false) return "notCompliant";
|
|
@@ -137,34 +92,7 @@ export const getIndicatorType = (value) => {
|
|
|
137
92
|
return "empty"; // default fallback
|
|
138
93
|
};
|
|
139
94
|
|
|
140
|
-
/**
|
|
141
|
-
* Special case: Children presence indicator configuration
|
|
142
|
-
* Children presence is compliant if answered 'no', not compliant if answered 'yes'
|
|
143
|
-
* Custom icons: "no" (compliant) → X icon with green badge, "yes" (notCompliant) → Check icon with red badge
|
|
144
|
-
*
|
|
145
|
-
* @param {string|boolean|null} value - The presenceOfChildren value from activityData
|
|
146
|
-
* @returns {Object} - Configuration object with type and optional statusIcon
|
|
147
|
-
*/
|
|
148
|
-
export const getChildrenPresenceConfig = (value) => {
|
|
149
|
-
if (value === "no" || value === false) {
|
|
150
|
-
return { type: "compliant", statusIcon: "Close" }; // X icon with green badge
|
|
151
|
-
}
|
|
152
|
-
if (value === "yes" || value === true) {
|
|
153
|
-
return { type: "notCompliant", statusIcon: "Check" }; // Check icon with red badge
|
|
154
|
-
}
|
|
155
|
-
return { type: "empty" }; // empty state
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Get activity indicators configuration from activityData
|
|
160
|
-
* Maps activityData fields to indicator config objects with icon, label, type, and optional statusIcon
|
|
161
|
-
*
|
|
162
|
-
* @param {Object} activityData - Activity data object
|
|
163
|
-
* @param {Function} t - Translation function
|
|
164
|
-
* @returns {Array} - Array of indicator config objects
|
|
165
|
-
*/
|
|
166
95
|
export const getActivityIndicatorsConfig = (activityData, t) => {
|
|
167
|
-
const childrenPresenceConfig = getChildrenPresenceConfig(activityData?.presenceOfChildren);
|
|
168
96
|
|
|
169
97
|
return [
|
|
170
98
|
{ icon: "Aid", label: t("Aid kit availability"), type: getIndicatorType(activityData?.aidKitAccessible) },
|
|
@@ -172,11 +100,137 @@ export const getActivityIndicatorsConfig = (activityData, t) => {
|
|
|
172
100
|
{ icon: "Users", label: t("Workers safe pairing"), type: getIndicatorType(activityData?.duosFormed) },
|
|
173
101
|
{
|
|
174
102
|
icon: "Bear",
|
|
175
|
-
label: t("
|
|
176
|
-
type:
|
|
177
|
-
...(childrenPresenceConfig.statusIcon && { statusIcon: childrenPresenceConfig.statusIcon })
|
|
103
|
+
label: t("No children"),
|
|
104
|
+
type: getIndicatorType(activityData?.presenceOfChildren),
|
|
178
105
|
},
|
|
179
106
|
{ icon: "Security", label: t("Security presence"), type: getIndicatorType(activityData?.focalPointPresent) },
|
|
180
107
|
{ icon: "UserCircle", label: t("Relay presence"), type: getIndicatorType(activityData?.relayPresent) },
|
|
181
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;
|
|
182
236
|
};
|