datastake-daf 0.6.747 → 0.6.749

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 (58) hide show
  1. package/dist/components/index.js +4586 -3081
  2. package/dist/layouts/index.js +497 -440
  3. package/dist/pages/index.js +2006 -964
  4. package/dist/utils/index.js +497 -440
  5. package/package.json +1 -1
  6. package/src/@daf/core/components/Dashboard/Widget/ActivityIndicators/ActivityIndicators.stories.js +252 -0
  7. package/src/@daf/core/components/Dashboard/Widget/ActivityIndicators/config.js +76 -0
  8. package/src/@daf/core/components/Dashboard/Widget/ActivityIndicators/index.jsx +184 -0
  9. package/src/@daf/core/components/Dashboard/Widget/ActivityIndicators/style.js +119 -0
  10. package/src/@daf/core/components/Icon/configs/Aid.js +10 -0
  11. package/src/@daf/core/components/Icon/configs/Bear.js +8 -0
  12. package/src/@daf/core/components/Icon/configs/Minus.js +10 -0
  13. package/src/@daf/core/components/Icon/configs/Security.js +12 -0
  14. package/src/@daf/core/components/Icon/configs/index.js +8 -0
  15. package/src/@daf/core/components/Screens/Settings/Edit/components/Form/index.js +115 -0
  16. package/src/@daf/core/components/Screens/Settings/Edit/components/Form/style.js +35 -0
  17. package/src/@daf/core/components/Screens/Settings/Edit/components/Inputs/ImageUpload.js +67 -0
  18. package/src/@daf/core/components/Screens/Settings/Edit/components/Inputs/Phone.js +24 -0
  19. package/src/@daf/core/components/Screens/Settings/Edit/components/Inputs/ResetPassword.js +122 -0
  20. package/src/@daf/core/components/Screens/Settings/Edit/components/Inputs/Select.js +26 -0
  21. package/src/@daf/core/components/Screens/Settings/Edit/components/Inputs/Switch.js +56 -0
  22. package/src/@daf/core/components/Screens/Settings/Edit/components/Inputs/Text.js +19 -0
  23. package/src/@daf/core/components/Screens/Settings/Edit/components/Inputs/TransferRights.js +30 -0
  24. package/src/@daf/core/components/Screens/Settings/Edit/components/helper.js +13 -0
  25. package/src/@daf/core/components/Screens/Settings/Edit/components/style.js +56 -0
  26. package/src/@daf/core/components/Screens/Settings/Edit/index.js +63 -0
  27. package/src/@daf/core/components/Screens/Settings/View/components/Content/helper.js +142 -0
  28. package/src/@daf/core/components/Screens/Settings/View/components/Content/index.js +61 -0
  29. package/src/@daf/core/components/Screens/Settings/View/components/Content/style.js +338 -0
  30. package/src/@daf/core/components/Screens/Settings/View/index.js +46 -0
  31. package/src/@daf/core/components/Screens/Settings/components/Header/index.js +64 -0
  32. package/src/@daf/core/components/Screens/Settings/components/Menu/index.js +36 -0
  33. package/src/@daf/core/components/Screens/Settings/config.js +30 -0
  34. package/src/@daf/core/components/Screens/Settings/index.js +164 -0
  35. package/src/@daf/pages/Documents/columns.js +25 -8
  36. package/src/@daf/pages/Documents/config.js +7 -10
  37. package/src/@daf/pages/Events/Activities/columns.js +19 -9
  38. package/src/@daf/pages/Events/Activities/config.js +23 -13
  39. package/src/@daf/pages/Events/Incidents/columns.js +19 -9
  40. package/src/@daf/pages/Events/Incidents/config.js +23 -13
  41. package/src/@daf/pages/Events/columns.js +33 -8
  42. package/src/@daf/pages/Events/config.js +14 -22
  43. package/src/@daf/pages/Locations/MineSite/columns.js +53 -18
  44. package/src/@daf/pages/Locations/columns.js +54 -32
  45. package/src/@daf/pages/Locations/config.js +12 -2
  46. package/src/@daf/pages/Stakeholders/Operators/columns.js +57 -22
  47. package/src/@daf/pages/Stakeholders/columns.js +45 -23
  48. package/src/@daf/pages/Stakeholders/config.js +12 -2
  49. package/src/@daf/pages/Stakeholders/index.jsx +1 -0
  50. package/src/@daf/pages/Summary/Activities/Restoration/helper.js +86 -2
  51. package/src/@daf/pages/Summary/Activities/Restoration/index.jsx +20 -19
  52. package/src/@daf/services/BaseService.js +15 -0
  53. package/src/index.js +10 -0
  54. package/build/favicon.ico +0 -0
  55. package/build/logo192.png +0 -0
  56. package/build/logo512.png +0 -0
  57. package/build/manifest.json +0 -25
  58. package/build/robots.txt +0 -3
@@ -4,7 +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
-
7
+ import MoreMenu from '../../../core/components/Table/MoreMenu/index.jsx';
8
8
  const getLinkValue = (value, linkingObject) => {
9
9
  if(linkingObject && linkingObject?.[value]) {
10
10
  return linkingObject?.[value]?.name;
@@ -86,7 +86,7 @@ export const getColumns = ({t, goTo, user, options, activeTab, getRedirectLink,
86
86
  return <AvatarGroup items={val}></AvatarGroup>;
87
87
  },
88
88
  },
89
- , {
89
+ {
90
90
  dataIndex: 'operator',
91
91
  title: t('Operator'),
92
92
  ellipsis: true,
@@ -129,22 +129,57 @@ export const getColumns = ({t, goTo, user, options, activeTab, getRedirectLink,
129
129
  return status ? <Tooltip title={status}>{status}</Tooltip> : '-';
130
130
  },
131
131
  },
132
- {
133
- id: 'actions',
134
- title: "",
135
- width: 60,
136
- render: (_, all) => {
137
- if (all.empty) {
138
- return <div className="daf-default-cell" />;
132
+
133
+ {
134
+ title: t("Sources"),
135
+ dataIndex: 'source',
136
+ ellipsis: true,
137
+ show: activeTab !== "own",
138
+ render: (v, all) => {
139
+ if (all.empty) {
140
+ return <div className="daf-default-cell" />
141
+ }
142
+ const source = findOptions(v, data?.options?.sourceOptions);
143
+ return source ? <Tooltip title={source}>{source}</Tooltip> : '-';
144
+ },
145
+ },
146
+ {
147
+ id: 'actions',
148
+ title: "",
149
+ width: 60,
150
+ render: (_, all) => {
151
+ if (all.empty) {
152
+ return <div className="daf-default-cell" />;
153
+ }
154
+ const onClick = () => {
155
+ const link = `/app/view/${subject}/${all.datastakeId}`;
156
+ if (activeTab === "shared") {
157
+ link += `?sourceId=${all?.authorId?.id}`;
158
+ }
159
+ goTo(getRedirectLink(link));
160
+ };
161
+ const moreMenuItems = [
162
+ {
163
+ label: t("Details"),
164
+ value: "details",
165
+ onClick: onClick,
166
+ },
167
+ {
168
+ label: t("Summary"),
169
+ value: "Summary",
170
+ onClick: () => {
171
+ let link = `/app/mine-summary/${all.datastakeId}`
172
+ if (activeTab === "shared") {
173
+ link += `?sourceId=${all?.authorId?.id}`;
174
+ }
175
+ goTo(getRedirectLink(link));
176
+ },
177
+ // disabled: true,
178
+ },
179
+ ];
180
+ return <div >
181
+ <MoreMenu items={moreMenuItems} />
182
+ </div>;
139
183
  }
140
-
141
- const link = `/app/view/${subject}/${all.datastakeId}`;
142
-
143
- return <div style={{ display: "flex", justifyContent: "center" }}>
144
- <a href={getRedirectLink(link)}>
145
- <CustomIcon name="Link" size={15} color={theme.baseGray70} />
146
- </a>
147
- </div>;
148
184
  }
149
- }
150
185
  ].filter((column) => column.show !== false);
@@ -5,7 +5,7 @@ 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
7
  import sourceAvatarConfig from '../../../helpers/sourceAvatarConfig.js';
8
-
8
+ import MoreMenu from '../../core/components/Table/MoreMenu/index.jsx';
9
9
  const getLinkValue = (value, linkingObject) => {
10
10
  if(linkingObject && linkingObject?.[value]) {
11
11
  return linkingObject?.[value]?.name;
@@ -71,8 +71,8 @@ export const getColumns = ({t, goTo, user, options, activeTab, getRedirectLink,
71
71
  },
72
72
  },
73
73
  {
74
- dataIndex: 'administrativeLevel1',
75
- title: t('Region'),
74
+ dataIndex: 'province',
75
+ title: t('Province'),
76
76
  ellipsis: true,
77
77
  show: true,
78
78
  render: (v, all) => {
@@ -80,14 +80,14 @@ export const getColumns = ({t, goTo, user, options, activeTab, getRedirectLink,
80
80
  return <div className="daf-default-cell" />
81
81
  }
82
82
 
83
- const region = getLinkValue(v, all?.linking?.SCL);
83
+ const province = getLinkValue(v, all?.linking?.SCL);
84
84
 
85
- return region ? <Tooltip title={region}>{region}</Tooltip> : '-';
85
+ return province ? <Tooltip title={province}>{province}</Tooltip> : '-';
86
86
  },
87
87
  },
88
88
  {
89
- dataIndex: 'administrativeLevel2',
90
- title: t('District'),
89
+ dataIndex: 'territory',
90
+ title: t('Territory'),
91
91
  ellipsis: true,
92
92
  show: true,
93
93
  render: (v, all) => {
@@ -95,11 +95,26 @@ export const getColumns = ({t, goTo, user, options, activeTab, getRedirectLink,
95
95
  return <div className="daf-default-cell" />
96
96
  }
97
97
 
98
- const district = getLinkValue(v, all?.linking?.SCL);
98
+ const territory = getLinkValue(v, all?.linking?.SCL);
99
99
 
100
- return district ? <Tooltip title={district}>{district}</Tooltip> : '-';
100
+ return territory ? <Tooltip title={territory}>{territory}</Tooltip> : '-';
101
101
  },
102
102
  },
103
+ {
104
+ title: t("Sources"),
105
+ dataIndex: "sources",
106
+ key: "sources",
107
+ show: activeTab !== "own",
108
+ render: (val, all) => {
109
+ if (all.empty) {
110
+ return <div className="daf-default-cell" />;
111
+ }
112
+
113
+ const sources = sourceAvatarConfig(val, user, applications);
114
+
115
+ return <AvatarGroup items={sources}></AvatarGroup>;
116
+ },
117
+ },
103
118
  {
104
119
  title: t("Last Update"),
105
120
  dataIndex: "updatedAt",
@@ -115,21 +130,7 @@ export const getColumns = ({t, goTo, user, options, activeTab, getRedirectLink,
115
130
  },
116
131
  ellipsis: true,
117
132
  },
118
- {
119
- title: t("Sources"),
120
- dataIndex: "sources",
121
- key: "sources",
122
- show: activeTab !== "own",
123
- render: (val, all) => {
124
- if (all.empty) {
125
- return <div className="daf-default-cell" />;
126
- }
127
-
128
- const sources = sourceAvatarConfig(val, user, applications);
129
-
130
- return <AvatarGroup items={sources}></AvatarGroup>;
131
- },
132
- },
133
+
133
134
  {
134
135
  id: 'actions',
135
136
  title: "",
@@ -138,14 +139,35 @@ export const getColumns = ({t, goTo, user, options, activeTab, getRedirectLink,
138
139
  if (all.empty) {
139
140
  return <div className="daf-default-cell" />;
140
141
  }
141
-
142
- const link = `/app/view/${subject}/${all.datastakeId}`;
143
-
144
- return <div style={{ display: "flex", justifyContent: "center" }}>
145
- <a href={getRedirectLink(link)}>
146
- <CustomIcon name="Link" size={15} color={theme.baseGray70} />
147
- </a>
148
- </div>;
142
+ const onClick = () => {
143
+ const link = `/app/view/${subject}/${all.datastakeId}`;
144
+ if (activeTab === "shared") {
145
+ link += `?sourceId=${all?.authorId?.id}`;
146
+ }
147
+ goTo(getRedirectLink(link));
148
+ };
149
+ const moreMenuItems = [
150
+ {
151
+ label: t("Details"),
152
+ value: "details",
153
+ onClick: onClick,
154
+ },
155
+ {
156
+ label: t("Summary"),
157
+ value: "Summary",
158
+ onClick: () => {
159
+ let link = `/app/summary/${subject}/${all.datastakeId}`
160
+ if (activeTab === "shared") {
161
+ link += `?sourceId=${all?.authorId?.id}`;
162
+ }
163
+ goTo(getRedirectLink(link));
164
+ },
165
+ // disabled: true,
166
+ },
167
+ ];
168
+ return <div >
169
+ <MoreMenu items={moreMenuItems} />
170
+ </div>;
149
171
  }
150
172
  }
151
173
  ].filter((column) => column.show !== false);
@@ -13,7 +13,16 @@ export const getFiltersConfig = ({t}) => {
13
13
  getLabel: (option) => option.label,
14
14
  getValue: (option) => option.value,
15
15
  },
16
- }
16
+ category:{
17
+ type: 'select',
18
+ label: 'Category',
19
+ placeholder: (t) => `${t('Filter by')} ${t('Category').toLowerCase()}`,
20
+ style: { flex: 1 },
21
+ labelStyle: { flex: 1 },
22
+ getLabel: (option) => option.label,
23
+ getValue: (option) => option.value,
24
+ }
25
+ }
17
26
  }
18
27
 
19
28
  export const filtersConfig = {
@@ -22,9 +31,10 @@ export const filtersConfig = {
22
31
  };
23
32
 
24
33
  export const getFilterOptions = (options, t) => {
25
- const { countries } = options || {};
34
+ const { countries = [], category = [] } = options || {};
26
35
  const _default = {
27
36
  country: countries,
37
+ category: category,
28
38
  }
29
39
 
30
40
  return _default;
@@ -5,7 +5,7 @@ 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
7
  import sourceAvatarConfig from '../../../../helpers/sourceAvatarConfig.js';
8
-
8
+ import MoreMenu from '../../../core/components/Table/MoreMenu/index.jsx';
9
9
  const getLinkValue = (value, linkingObject) => {
10
10
  if(linkingObject && linkingObject?.[value]) {
11
11
  return linkingObject?.[value]?.name;
@@ -99,7 +99,22 @@ export const getColumns = ({t, goTo, user, options, activeTab, getRedirectLink,
99
99
 
100
100
  return district ? <Tooltip title={district}>{district}</Tooltip> : '-';
101
101
  },
102
- },
102
+ },
103
+ {
104
+ title: t("Last Update"),
105
+ dataIndex: "updatedAt",
106
+ key: "updatedAt",
107
+ width: 125,
108
+ render: (date, all) => {
109
+ if (all.empty) {
110
+ return <div className="daf-default-cell" />;
111
+ }
112
+
113
+ const _date = date ? renderDateFormatted(date, "DD MMM YYYY", user?.language || 'en') : "-";
114
+ return <Tooltip title={_date}>{_date}</Tooltip>;
115
+ },
116
+ ellipsis: true,
117
+ },
103
118
  {
104
119
  title: t("Sources"),
105
120
  dataIndex: "sources",
@@ -116,20 +131,19 @@ export const getColumns = ({t, goTo, user, options, activeTab, getRedirectLink,
116
131
  },
117
132
  },
118
133
  {
119
- title: t("Last Update"),
120
- dataIndex: "updatedAt",
121
- key: "updatedAt",
122
- width: 125,
123
- render: (date, all) => {
124
- if (all.empty) {
125
- return <div className="daf-default-cell" />;
126
- }
127
-
128
- const _date = date ? renderDateFormatted(date, "DD MMM YYYY", user?.language || 'en') : "-";
129
- return <Tooltip title={_date}>{_date}</Tooltip>;
130
- },
134
+ title: t("Status"),
135
+ dataIndex: 'status',
131
136
  ellipsis: true,
137
+ show: activeTab == "own",
138
+ render: (v, all) => {
139
+ if (all.empty) {
140
+ return <div className="daf-default-cell" />
141
+ }
142
+ const status = findOptions(v, data?.options?.statusOptions);
143
+ return status ? <Tooltip title={status}>{status}</Tooltip> : '-';
144
+ },
132
145
  },
146
+
133
147
  {
134
148
  id: 'actions',
135
149
  title: "",
@@ -138,14 +152,35 @@ export const getColumns = ({t, goTo, user, options, activeTab, getRedirectLink,
138
152
  if (all.empty) {
139
153
  return <div className="daf-default-cell" />;
140
154
  }
141
-
142
- const link = `/app/view/${subject}/${all.datastakeId}`;
143
-
144
- return <div style={{ display: "flex", justifyContent: "center" }}>
145
- <a href={getRedirectLink(link)}>
146
- <CustomIcon name="Link" size={15} color={theme.baseGray70} />
147
- </a>
148
- </div>;
155
+ const onClick = () => {
156
+ const link = `/app/view/${subject}/${all.datastakeId}`;
157
+ if (activeTab === "shared") {
158
+ link += `?sourceId=${all?.authorId?.id}`;
159
+ }
160
+ goTo(getRedirectLink(link));
161
+ };
162
+ const moreMenuItems = [
163
+ {
164
+ label: t("Details"),
165
+ value: "details",
166
+ onClick: onClick,
167
+ },
168
+ {
169
+ label: t("Summary"),
170
+ value: "Summary",
171
+ onClick: () => {
172
+ let link = `/app/operator-summary/${all.datastakeId}`
173
+ if (activeTab === "shared") {
174
+ link += `?sourceId=${all?.authorId?.id}`;
175
+ }
176
+ goTo(getRedirectLink(link));
177
+ },
178
+ // disabled: true,
179
+ },
180
+ ];
181
+ return <div >
182
+ <MoreMenu items={moreMenuItems} />
183
+ </div>;
149
184
  }
150
185
  }
151
186
  ].filter((column) => column.show !== false);
@@ -5,7 +5,7 @@ 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
7
  import sourceAvatarConfig from '../../../helpers/sourceAvatarConfig.js';
8
-
8
+ import MoreMenu from '../../core/components/Table/MoreMenu/index.jsx';
9
9
  export const getColumns = ({t, goTo, user, options, activeTab, getRedirectLink, theme, subject, applications}) => [
10
10
  {
11
11
  dataIndex: 'datastakeId',
@@ -78,21 +78,7 @@ export const getColumns = ({t, goTo, user, options, activeTab, getRedirectLink,
78
78
  return country ? <Tooltip title={country}>{country}</Tooltip> : '-';
79
79
  },
80
80
  },
81
- {
82
- title: t("Last Update"),
83
- dataIndex: "updatedAt",
84
- key: "updatedAt",
85
- width: 125,
86
- render: (date, all) => {
87
- if (all.empty) {
88
- return <div className="daf-default-cell" />;
89
- }
90
81
 
91
- const _date = date ? renderDateFormatted(date, "DD MMM YYYY", user?.language || 'en') : "-";
92
- return <Tooltip title={_date}>{_date}</Tooltip>;
93
- },
94
- ellipsis: true,
95
- },
96
82
  {
97
83
  title: t("Sources"),
98
84
  dataIndex: "sources",
@@ -111,6 +97,21 @@ export const getColumns = ({t, goTo, user, options, activeTab, getRedirectLink,
111
97
  return <AvatarGroup items={sources}></AvatarGroup>;
112
98
  },
113
99
  },
100
+ {
101
+ title: t("Last Update"),
102
+ dataIndex: "updatedAt",
103
+ key: "updatedAt",
104
+ width: 125,
105
+ render: (date, all) => {
106
+ if (all.empty) {
107
+ return <div className="daf-default-cell" />;
108
+ }
109
+
110
+ const _date = date ? renderDateFormatted(date, "DD MMM YYYY", user?.language || 'en') : "-";
111
+ return <Tooltip title={_date}>{_date}</Tooltip>;
112
+ },
113
+ ellipsis: true,
114
+ },
114
115
  {
115
116
  id: 'actions',
116
117
  title: "",
@@ -119,14 +120,35 @@ export const getColumns = ({t, goTo, user, options, activeTab, getRedirectLink,
119
120
  if (all.empty) {
120
121
  return <div className="daf-default-cell" />;
121
122
  }
122
-
123
- const link = `/app/view/${subject}/${all.datastakeId}`;
124
-
125
- return <div style={{ display: "flex", justifyContent: "center" }}>
126
- <a href={getRedirectLink(link)}>
127
- <CustomIcon name="Link" size={15} color={theme.baseGray70} />
128
- </a>
129
- </div>;
123
+ const onClick = () => {
124
+ const link = `/app/view/${subject}/${all.datastakeId}`;
125
+ if (activeTab === "shared") {
126
+ link += `?sourceId=${all?.authorId?.id}`;
127
+ }
128
+ goTo(getRedirectLink(link));
129
+ };
130
+ const moreMenuItems = [
131
+ {
132
+ label: t("Details"),
133
+ value: "details",
134
+ onClick: onClick,
135
+ },
136
+ {
137
+ label: t("Summary"),
138
+ value: "Summary",
139
+ onClick: () => {
140
+ let link = `/app/summary/${subject}/${all.datastakeId}`
141
+ if (activeTab === "shared") {
142
+ link += `?sourceId=${all?.authorId?.id}`;
143
+ }
144
+ goTo(getRedirectLink(link));
145
+ },
146
+ // disabled: true,
147
+ },
148
+ ];
149
+ return <div >
150
+ <MoreMenu items={moreMenuItems} />
151
+ </div>;
130
152
  }
131
153
  }
132
154
  ].filter((column) => column.show !== false);
@@ -13,7 +13,16 @@ export const getFiltersConfig = ({t}) => {
13
13
  getLabel: (option) => option.label,
14
14
  getValue: (option) => option.value,
15
15
  },
16
- }
16
+ category:{
17
+ type: 'select',
18
+ label: 'Category',
19
+ placeholder: (t) => `${t('Filter by')} ${t('Category').toLowerCase()}`,
20
+ style: { flex: 1 },
21
+ labelStyle: { flex: 1 },
22
+ getLabel: (option) => option.label,
23
+ getValue: (option) => option.value,
24
+ }
25
+ }
17
26
  }
18
27
 
19
28
  export const filtersConfig = {
@@ -22,9 +31,10 @@ export const filtersConfig = {
22
31
  };
23
32
 
24
33
  export const getFilterOptions = (options, t) => {
25
- const { countries } = options || {};
34
+ const { countries = [], category = [] } = options || {};
26
35
  const _default = {
27
36
  country: countries,
37
+ category: category,
28
38
  }
29
39
 
30
40
  return _default;
@@ -5,6 +5,7 @@ import { checkboxConfig, getFiltersConfig, filtersConfig, getFilterOptions } fro
5
5
  import { useGetQueryParams } from '../../hooks/useGetQueryParams.js';
6
6
  import StakeholdersCreate from './create.jsx';
7
7
  import { displayMessage } from '../../../helpers/messages.js';
8
+ import DAFHeader from '../../core/components/Header/index.jsx';
8
9
 
9
10
  const StakeholdersTable = ({
10
11
  t = () => {},
@@ -2,6 +2,11 @@
2
2
  // REGION: Photo/Image Extraction
3
3
  // ============================================================================
4
4
 
5
+ /**
6
+ * Normalize URL by removing trailing colon if present
7
+ */
8
+ const normalizeUrl = (url) => url?.endsWith(':') ? url.slice(0, -1) : url;
9
+
5
10
  /**
6
11
  * Extract images from a photo document
7
12
  * Handles both documents with pictures arrays and direct image objects
@@ -16,9 +21,27 @@ export const extractFromPhotoDoc = (photoDoc, label, docIndex) => {
16
21
  };
17
22
 
18
23
  /**
19
- * Normalize URL by removing trailing colon if present
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
20
29
  */
21
- const normalizeUrl = (url) => url?.endsWith(':') ? url.slice(0, -1) : url;
30
+ export const getActivityImages = (activityData) => {
31
+ const photoArrays = [
32
+ { data: activityData?.groupPhotos, label: 'Group Photo' },
33
+ { data: activityData?.photosStartActivity, label: 'Start of Activity' },
34
+ { data: activityData?.photosDuringActivity, label: 'During Activity' },
35
+ { data: activityData?.photosEndActivity, label: 'End of Activity' }
36
+ ];
37
+
38
+ return photoArrays
39
+ .flatMap(({ data, label }) =>
40
+ Array.isArray(data)
41
+ ? data.flatMap((photoDoc, index) => extractFromPhotoDoc(photoDoc, label, index))
42
+ : []
43
+ );
44
+ };
22
45
 
23
46
  // ============================================================================
24
47
  // REGION: Gender Distribution
@@ -95,4 +118,65 @@ export const getGenderTooltipChildren = (item, isEmpty, genderDistributionData,
95
118
  },
96
119
  ],
97
120
  });
121
+ };
122
+
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
+ export const getIndicatorType = (value) => {
134
+ if (value === "yes" || value === true) return "compliant";
135
+ if (value === "no" || value === false) return "notCompliant";
136
+ if (value === null || value === undefined) return "empty";
137
+ return "empty"; // default fallback
138
+ };
139
+
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
+ export const getActivityIndicatorsConfig = (activityData, t) => {
167
+ const childrenPresenceConfig = getChildrenPresenceConfig(activityData?.presenceOfChildren);
168
+
169
+ return [
170
+ { icon: "Aid", label: t("Aid kit availability"), type: getIndicatorType(activityData?.aidKitAccessible) },
171
+ { icon: "MineOperators", label: t("H&S training delivery"), type: getIndicatorType(activityData?.hsTrainingConfirmation) },
172
+ { icon: "Users", label: t("Workers safe pairing"), type: getIndicatorType(activityData?.duosFormed) },
173
+ {
174
+ icon: "Bear",
175
+ label: t("Children presence"),
176
+ type: childrenPresenceConfig.type,
177
+ ...(childrenPresenceConfig.statusIcon && { statusIcon: childrenPresenceConfig.statusIcon })
178
+ },
179
+ { icon: "Security", label: t("Security presence"), type: getIndicatorType(activityData?.focalPointPresent) },
180
+ { icon: "UserCircle", label: t("Relay presence"), type: getIndicatorType(activityData?.relayPresent) },
181
+ ];
98
182
  };
@@ -1,29 +1,14 @@
1
1
  import { useMemo, useCallback } from 'react';
2
- import { DashboardLayout, Header, ImageCarousel, KeyIndicators, MineSiteMap, Widget, PieChart } from '../../../../../../src/index.js'
2
+ import { DashboardLayout, Header, ImageCarousel, KeyIndicators, MineSiteMap, Widget, PieChart, ActivityIndicators } from '../../../../../../src/index.js'
3
3
  import { getKeyIndicatorsRowConfig } from './config';
4
- import { extractFromPhotoDoc, getGenderDistributionData, isGenderDistributionEmpty, calculateGenderPieData, getGenderTooltipChildren } from './helper';
4
+ import { getActivityImages, getGenderDistributionData, isGenderDistributionEmpty, calculateGenderPieData, getGenderTooltipChildren, getActivityIndicatorsConfig } from './helper';
5
5
  import { renderTooltipJsx } from '../../../../../../src/utils';
6
6
  import { useResizeContext } from '../../../../../../src/context';
7
7
 
8
8
  const RestorationActivitySummary = ({ activityData, supportText, onDownload, downloadDisabled, actionButtons, breadcrumbs, goBackTo, loading, t = () => { } }) => {
9
9
  const { isCollapsed, isNestedSidebarCollapsed } = useResizeContext();
10
10
  const keyIndicatorsConfig = useMemo(() => getKeyIndicatorsRowConfig({ t, data: activityData }), [t, activityData]);
11
- const images = useMemo(() => {
12
-
13
- const photoArrays = [
14
- { data: activityData?.groupPhotos, label: 'Group Photo' },
15
- { data: activityData?.photosStartActivity, label: 'Start of Activity' },
16
- { data: activityData?.photosDuringActivity, label: 'During Activity' },
17
- { data: activityData?.photosEndActivity, label: 'End of Activity' }
18
- ];
19
-
20
- return photoArrays
21
- .flatMap(({ data, label }) =>
22
- Array.isArray(data)
23
- ? data.flatMap((photoDoc, index) => extractFromPhotoDoc(photoDoc, label, index))
24
- : []
25
- );
26
- }, [activityData]);
11
+ const images = useMemo(() => getActivityImages(activityData), [activityData]);
27
12
  const genderDistributionData = useMemo(() => getGenderDistributionData(activityData), [activityData]);
28
13
  const isEmpty = useMemo(() => isGenderDistributionEmpty(genderDistributionData), [genderDistributionData]);
29
14
  const pieData = useMemo(() => calculateGenderPieData(genderDistributionData), [genderDistributionData]);
@@ -32,6 +17,13 @@ const RestorationActivitySummary = ({ activityData, supportText, onDownload, dow
32
17
  (item) => getGenderTooltipChildren(item, isEmpty, genderDistributionData, t, renderTooltipJsx),
33
18
  [t, isEmpty, genderDistributionData],
34
19
  );
20
+
21
+ // Activity Indicators Config - mapped from activityData
22
+ const activityIndicatorsConfig = useMemo(() =>
23
+ getActivityIndicatorsConfig(activityData, t),
24
+ [activityData, t]
25
+ );
26
+
35
27
  return (
36
28
  <DashboardLayout
37
29
  header={
@@ -54,7 +46,7 @@ const RestorationActivitySummary = ({ activityData, supportText, onDownload, dow
54
46
  <section>
55
47
  <Widget
56
48
  title={t("Activity Location")}
57
- className="no-px no-pb-body"
49
+ className="no-px no-pt-body no-p-body no-pb-body"
58
50
  style={{ height: '100%', display: 'flex', flexDirection: 'column' }}
59
51
  >
60
52
  <div style={{ flex: 1, minHeight: 0 }}>
@@ -135,6 +127,15 @@ const RestorationActivitySummary = ({ activityData, supportText, onDownload, dow
135
127
  </Widget>
136
128
  </section>
137
129
 
130
+ <section>
131
+ <ActivityIndicators
132
+ config={activityIndicatorsConfig}
133
+ loading={loading}
134
+ title={t("Activity Indicators")}
135
+ className="small-content"
136
+ />
137
+ </section>
138
+
138
139
  <section>
139
140
  <div style={{ maxWidth: "70%", width: "calc(100% - 405px)" }}>
140
141
  <ImageCarousel