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.
Files changed (43) hide show
  1. package/dist/components/index.js +3299 -2881
  2. package/dist/layouts/index.js +476 -452
  3. package/dist/pages/index.js +836 -662
  4. package/dist/style/datastake/mapbox-gl.css +330 -0
  5. package/dist/utils/index.js +481 -802
  6. package/package.json +1 -1
  7. package/rollup.config.js +0 -20
  8. package/src/@daf/core/components/Dashboard/Map/Map.stories.js +8 -0
  9. package/src/@daf/core/components/Dashboard/Map/helper.js +134 -3
  10. package/src/@daf/core/components/Dashboard/Map/hook.js +4 -0
  11. package/src/@daf/core/components/Dashboard/Map/index.jsx +19 -0
  12. package/src/@daf/core/components/Dashboard/Map/storyConfig.js +2 -1
  13. package/src/@daf/core/components/Dashboard/Map/storyConfig6.js +69 -0
  14. package/src/@daf/core/components/Dashboard/Widget/ActivityIndicators/config.js +2 -5
  15. package/src/@daf/core/components/Dashboard/Widget/KeyIndicators/index.jsx +1 -1
  16. package/src/@daf/core/components/EditForm/form.jsx +1 -1
  17. package/src/@daf/core/components/EditForm/storyConfig2.js +25028 -865
  18. package/src/@daf/core/components/Graphs/components/BaseGraph.jsx +1 -1
  19. package/src/@daf/core/components/Icon/configs/SpacingHeight.js +8 -0
  20. package/src/@daf/core/components/Icon/configs/SpacingWidth.js +8 -0
  21. package/src/@daf/core/components/Icon/configs/index.js +5 -1
  22. package/src/@daf/hooks/useMapHelper.js +15 -1
  23. package/src/@daf/pages/Dashboards/SupplyChain/index.jsx +2 -2
  24. package/src/@daf/pages/Documents/index.jsx +3 -2
  25. package/src/@daf/pages/Events/Activities/index.jsx +3 -2
  26. package/src/@daf/pages/Events/Incidents/index.jsx +3 -2
  27. package/src/@daf/pages/Events/index.jsx +3 -2
  28. package/src/@daf/pages/Locations/MineSite/columns.js +5 -7
  29. package/src/@daf/pages/Locations/MineSite/index.jsx +4 -5
  30. package/src/@daf/pages/Locations/index.jsx +3 -2
  31. package/src/@daf/pages/Stakeholders/Operators/index.jsx +2 -2
  32. package/src/@daf/pages/Stakeholders/Workers/index.jsx +3 -2
  33. package/src/@daf/pages/Stakeholders/index.jsx +3 -2
  34. package/src/@daf/pages/Summary/Activities/Restoration/helper.js +133 -79
  35. package/src/@daf/pages/Summary/Activities/Restoration/index.jsx +57 -60
  36. package/src/@daf/pages/Summary/Minesite/index.jsx +0 -2
  37. package/src/@daf/pages/Summary/Operator/index.jsx +1 -3
  38. package/src/@daf/utils/tooltip.js +5 -2
  39. package/src/utils.js +1 -3
  40. package/dist/constants/index.js +0 -26
  41. package/src/constants/breadCrumbs.js +0 -20
  42. package/src/constants.js +0 -1
  43. 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
- return item.marker &&
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
- breadcrumbs = [],
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={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={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={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={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={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, applications}) => [
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: 'sources',
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 sources = sourceAvatarConfig(v, user, applications);
142
- return <AvatarGroup items={sources}></AvatarGroup>;
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
- let link = `/app/view/${subject}/${all.datastakeId}`;
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
- applications,
54
- }), [t, goTo, user, options, activeTab, getRedirectLink, theme, data, applications]);
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={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={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={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={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={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("Children presence"),
176
- type: childrenPresenceConfig.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
  };