@plusscommunities/pluss-maintenance-web-forms 1.1.36 → 1.1.37-beta.1

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.
@@ -1,166 +1,343 @@
1
- import React, { useState, useEffect } from 'react';
2
- import moment from 'moment';
3
- import { faCircleCheck, faComment, faWrench } from '@fortawesome/free-solid-svg-icons';
4
- import { connect } from 'react-redux';
5
- import { analyticsActions } from '../apis';
6
- import { PlussCore } from '../feature.config';
7
- import { values } from '../values.config';
1
+ import React, { useState, useEffect } from "react";
2
+ import moment from "moment";
3
+ import {
4
+ faCircleCheck,
5
+ faComment,
6
+ faWrench,
7
+ } from "@fortawesome/free-solid-svg-icons";
8
+ import { connect } from "react-redux";
9
+ import { analyticsActions } from "../apis";
10
+ import { PlussCore } from "../feature.config";
11
+ import { values } from "../values.config";
8
12
 
9
- const { Analytics, Session, Components } = PlussCore;
13
+ const { Analytics, Session, Components, Helper } = PlussCore;
10
14
 
11
15
  const getInitialState = () => ({
12
- requests: 0,
13
- prevRequests: 0,
14
- completedRequests: 0,
15
- prevCompletedRequests: 0,
16
- comments: 0,
17
- prevComments: 0,
18
- isLoading: true,
16
+ requests: 0,
17
+ prevRequests: 0,
18
+ completedRequests: 0,
19
+ prevCompletedRequests: 0,
20
+ comments: 0,
21
+ prevComments: 0,
22
+ isLoading: true,
19
23
  });
20
24
 
21
25
  // AnalyticsHub Component
22
- const AnalyticsHub = ({ startTime, endTime, auth, prevText, dayCount, strings }) => {
23
- const [analyticsData, setAnalyticsData] = useState(getInitialState());
24
- const [isExportOpen, setIsExportOpen] = useState(false);
25
-
26
- const hasAccess = Session.validateAccess(auth.site, values.permissionMaintenanceTracking, auth);
27
- if (!hasAccess) {
28
- return null;
29
- }
30
-
31
- const featureTitle = ((key) => {
32
- if (!strings || !strings.sideNav || !strings.sideNav[key]) {
33
- return values.textMenuTitle;
34
- }
35
- return strings.sideNav[key];
36
- })();
37
-
38
- const exportColumns = [
39
- { label: 'Select All', key: '' },
40
- { label: 'Start Date', key: 'startDate' },
41
- { label: 'End Date', key: 'endDate' },
42
- { label: `${values.textSingularName}s`, key: 'requests' },
43
- { label: `Completed ${values.textSingularName}s`, key: 'completedRequests' },
44
- { label: 'Comments', key: 'comments' },
45
- ];
46
-
47
- useEffect(() => {
48
- getData();
49
- }, [startTime, endTime]);
50
-
51
- const getData = async () => {
52
- setAnalyticsData(getInitialState());
53
- // Load analytics data here using startTime and endTime
54
- const timeDifference = endTime - startTime;
55
- const [currentStatsResponse, prevStatsResponse] = await Promise.all([
56
- analyticsActions.getAggregateEntityStats(auth.site, values.analyticsKey, startTime, endTime, true),
57
- analyticsActions.getAggregateEntityStats(auth.site, values.analyticsKey, startTime - timeDifference, startTime, true),
58
- ]);
59
-
60
- const data = {
61
- requests: Analytics.countActivities(currentStatsResponse.data, 'Request', 'total'),
62
- prevRequests: Analytics.countActivities(prevStatsResponse.data, 'Request', 'total'),
63
- completedRequests: Analytics.countActivities(currentStatsResponse.data, 'RequestCompleted', 'unique'),
64
- prevCompletedRequests: Analytics.countActivities(prevStatsResponse.data, 'RequestCompleted', 'unique'),
65
- comments: Analytics.countActivities(currentStatsResponse.data, 'Comment', 'total'),
66
- prevComments: Analytics.countActivities(prevStatsResponse.data, 'Comment', 'total'),
67
- isLoading: false,
68
- };
69
- setAnalyticsData(data);
70
- };
71
-
72
- const isReadyToOpenCSV = () => {
73
- return !analyticsData.isLoading;
74
- };
75
-
76
- const getExportSource = () => {
77
- return [
78
- {
79
- startDate: moment(startTime + 1).format('D-MM-YYYY'),
80
- endDate: moment(endTime).format('D-MM-YYYY'),
81
- requests: analyticsData.requests,
82
- completedRequests: analyticsData.completedRequests,
83
- comments: analyticsData.comments,
84
- },
85
- ];
86
- };
87
-
88
- const csvPopup = () => {
89
- if (!isExportOpen) {
90
- return null;
91
- }
92
- const source = getExportSource();
93
- return (
94
- <Components.ExportCsvPopup
95
- onClose={() => {
96
- setIsExportOpen(false);
97
- }}
98
- columns={exportColumns}
99
- source={source}
100
- filename={`${values.analyticsKey}analytics_${source[0].startDate}_${source[0].endDate}.csv`}
101
- />
102
- );
103
- };
104
-
105
- return (
106
- <div className="dashboardSection">
107
- {csvPopup()}
108
- <div>
109
- <Components.Text type="h4" className="inlineBlock marginRight-40">
110
- {featureTitle}
111
- </Components.Text>
112
- <Components.Button
113
- inline
114
- buttonType="primaryAction"
115
- onClick={() => {
116
- if (!isReadyToOpenCSV()) return;
117
- setIsExportOpen(true);
118
- }}
119
- isActive={isReadyToOpenCSV()}
120
- leftIcon="file-code-o"
121
- >
122
- Export CSV
123
- </Components.Button>
124
- </div>
125
- <div className="analyticsSection dashboardSection_content">
126
- <Components.StatBox
127
- title={`${featureTitle} Requests`}
128
- icon={faWrench}
129
- value={analyticsData.requests}
130
- previousValue={analyticsData.prevRequests}
131
- prevText={prevText}
132
- viewGraphLink={`/chart?entity=${values.analyticsKey}&startTime=${startTime}&endTime=${endTime}&key=Request&countType=total&dayCount=${dayCount}`}
133
- isLoading={analyticsData.isLoading}
134
- />
135
- <Components.StatBox
136
- title={`Completed ${featureTitle} Requests`}
137
- icon={faCircleCheck}
138
- value={analyticsData.completedRequests}
139
- previousValue={analyticsData.prevCompletedRequests}
140
- prevText={prevText}
141
- viewGraphLink={`/chart?entity=${values.analyticsKey}&startTime=${startTime}&endTime=${endTime}&key=RequestCompleted&countType=unique&dayCount=${dayCount}`}
142
- isLoading={analyticsData.isLoading}
143
- />
144
- <Components.StatBox
145
- title={`${featureTitle} Comments`}
146
- icon={faComment}
147
- value={analyticsData.comments}
148
- previousValue={analyticsData.prevComments}
149
- prevText={prevText}
150
- viewGraphLink={`/chart?entity=${values.analyticsKey}&startTime=${startTime}&endTime=${endTime}&key=Comment&countType=total&dayCount=${dayCount}`}
151
- isLoading={analyticsData.isLoading}
152
- />
153
- </div>
154
- </div>
155
- );
26
+ const AnalyticsHub = ({
27
+ startTime,
28
+ endTime,
29
+ auth,
30
+ prevText,
31
+ dayCount,
32
+ strings,
33
+ userType,
34
+ userCategory,
35
+ selectedSites,
36
+ }) => {
37
+ const [analyticsData, setAnalyticsData] = useState(getInitialState());
38
+ const [isExportOpen, setIsExportOpen] = useState(false);
39
+ const [failedSites, setFailedSites] = useState([]);
40
+ const [comparisonData, setComparisonData] = useState({
41
+ requests: [],
42
+ completedRequests: [],
43
+ comments: [],
44
+ isLoading: true,
45
+ });
46
+
47
+ const comparisonMode = selectedSites && selectedSites.length > 1;
48
+
49
+ const hasAccess = Session.validateAccess(
50
+ auth.site,
51
+ values.permissionMaintenanceTracking,
52
+ auth,
53
+ );
54
+ if (!hasAccess) {
55
+ return null;
56
+ }
57
+
58
+ const featureTitle = ((key) => {
59
+ if (!strings || !strings.sideNav || !strings.sideNav[key]) {
60
+ return values.textMenuTitle;
61
+ }
62
+ return strings.sideNav[key];
63
+ })();
64
+
65
+ const exportColumns = [
66
+ { label: "Select All", key: "" },
67
+ ...(comparisonMode ? [{ label: "Site", key: "site" }] : []),
68
+ { label: "Start Date", key: "startDate" },
69
+ { label: "End Date", key: "endDate" },
70
+ { label: `${values.textSingularName}s`, key: "requests" },
71
+ {
72
+ label: `Completed ${values.textSingularName}s`,
73
+ key: "completedRequests",
74
+ },
75
+ { label: "Comments", key: "comments" },
76
+ ];
77
+
78
+ useEffect(() => {
79
+ getData();
80
+ }, [startTime, endTime, userType, userCategory, selectedSites]);
81
+
82
+ const getData = async () => {
83
+ if (comparisonMode) {
84
+ setComparisonData({
85
+ requests: [],
86
+ completedRequests: [],
87
+ comments: [],
88
+ isLoading: true,
89
+ });
90
+ const multiSiteResultsObj = await Analytics.fetchMultiSiteData(
91
+ selectedSites,
92
+ async (site) => {
93
+ const { data } = await analyticsActions.getAggregateEntityStats(
94
+ site,
95
+ values.analyticsKey,
96
+ startTime,
97
+ endTime,
98
+ true,
99
+ { userType, userCategory },
100
+ );
101
+ return data;
102
+ },
103
+ );
104
+ const roles = auth.user.Roles;
105
+ const failed = Object.entries(multiSiteResultsObj)
106
+ .filter(([, data]) => data === null)
107
+ .map(([site]) => Helper.getSiteNameFromRoles(site, roles));
108
+ setFailedSites(failed);
109
+ const multiSiteResults = Object.entries(multiSiteResultsObj)
110
+ .filter(([, data]) => data !== null)
111
+ .map(([site, data]) => ({ site, data }));
112
+ const buildComparison = (activityKey, countType) =>
113
+ multiSiteResults.map((result) => ({
114
+ name: Helper.getSiteNameFromRoles(result.site, roles),
115
+ value: Analytics.countActivities(result.data, activityKey, countType),
116
+ }));
117
+ setComparisonData({
118
+ requests: buildComparison("Request", "total"),
119
+ completedRequests: buildComparison("RequestCompleted", "unique"),
120
+ comments: buildComparison("Comment", "total"),
121
+ isLoading: false,
122
+ });
123
+ } else {
124
+ setAnalyticsData(getInitialState());
125
+ const site =
126
+ selectedSites && selectedSites.length === 1
127
+ ? selectedSites[0]
128
+ : auth.site;
129
+ const timeDifference = endTime - startTime;
130
+ const [currentStatsResponse, prevStatsResponse] = await Promise.all([
131
+ analyticsActions.getAggregateEntityStats(
132
+ site,
133
+ values.analyticsKey,
134
+ startTime,
135
+ endTime,
136
+ true,
137
+ { userType, userCategory },
138
+ ),
139
+ analyticsActions.getAggregateEntityStats(
140
+ site,
141
+ values.analyticsKey,
142
+ startTime - timeDifference,
143
+ startTime,
144
+ true,
145
+ { userType, userCategory },
146
+ ),
147
+ ]);
148
+
149
+ const data = {
150
+ requests: Analytics.countActivities(
151
+ currentStatsResponse.data,
152
+ "Request",
153
+ "total",
154
+ ),
155
+ prevRequests: Analytics.countActivities(
156
+ prevStatsResponse.data,
157
+ "Request",
158
+ "total",
159
+ ),
160
+ completedRequests: Analytics.countActivities(
161
+ currentStatsResponse.data,
162
+ "RequestCompleted",
163
+ "unique",
164
+ ),
165
+ prevCompletedRequests: Analytics.countActivities(
166
+ prevStatsResponse.data,
167
+ "RequestCompleted",
168
+ "unique",
169
+ ),
170
+ comments: Analytics.countActivities(
171
+ currentStatsResponse.data,
172
+ "Comment",
173
+ "total",
174
+ ),
175
+ prevComments: Analytics.countActivities(
176
+ prevStatsResponse.data,
177
+ "Comment",
178
+ "total",
179
+ ),
180
+ isLoading: false,
181
+ };
182
+ setAnalyticsData(data);
183
+ }
184
+ };
185
+
186
+ const isReadyToOpenCSV = () => {
187
+ return comparisonMode
188
+ ? !comparisonData.isLoading
189
+ : !analyticsData.isLoading;
190
+ };
191
+
192
+ const getExportSource = () => {
193
+ if (comparisonMode) {
194
+ const sites = comparisonData.requests || [];
195
+ return sites.map((site, i) => ({
196
+ site: site.name,
197
+ startDate: moment(startTime + 1).format("D-MM-YYYY"),
198
+ endDate: moment(endTime).format("D-MM-YYYY"),
199
+ requests: site.value,
200
+ completedRequests: comparisonData.completedRequests[i]?.value || 0,
201
+ comments: comparisonData.comments[i]?.value || 0,
202
+ }));
203
+ }
204
+ return [
205
+ {
206
+ startDate: moment(startTime + 1).format("D-MM-YYYY"),
207
+ endDate: moment(endTime).format("D-MM-YYYY"),
208
+ requests: analyticsData.requests,
209
+ completedRequests: analyticsData.completedRequests,
210
+ comments: analyticsData.comments,
211
+ },
212
+ ];
213
+ };
214
+
215
+ const csvPopup = () => {
216
+ if (!isExportOpen) {
217
+ return null;
218
+ }
219
+ const source = getExportSource();
220
+ const filterSuffix = [userType, userCategory].filter(Boolean).map((f) => f.toLowerCase().replace(/\s+/g, '-')).join('_');
221
+ return (
222
+ <Components.ExportCsvPopup
223
+ onClose={() => {
224
+ setIsExportOpen(false);
225
+ }}
226
+ columns={exportColumns}
227
+ source={source}
228
+ filename={`${values.analyticsKey}analytics${comparisonMode ? '_comparison' : ''}${filterSuffix ? `_${filterSuffix}` : ''}_${source[0].startDate}_${source[0].endDate}.csv`}
229
+ />
230
+ );
231
+ };
232
+
233
+ return (
234
+ <div className="dashboardSection">
235
+ {csvPopup()}
236
+ <div>
237
+ <Components.Text type="h4" className="inlineBlock marginRight-40">
238
+ {featureTitle}
239
+ </Components.Text>
240
+ <Components.Button
241
+ inline
242
+ buttonType="primaryAction"
243
+ onClick={() => {
244
+ if (!isReadyToOpenCSV()) return;
245
+ setIsExportOpen(true);
246
+ }}
247
+ isActive={isReadyToOpenCSV()}
248
+ leftIcon="file-code-o"
249
+ >
250
+ Export CSV
251
+ </Components.Button>
252
+ </div>
253
+ {failedSites.length > 0 && (
254
+ <Components.Text type="help" style={{ color: '#c02743' }}>
255
+ Data unavailable for: {failedSites.join(', ')}
256
+ </Components.Text>
257
+ )}
258
+ <div className="analyticsSection dashboardSection_content">
259
+ {(() => {
260
+ const chartSuffix = [
261
+ selectedSites && selectedSites.length > 0
262
+ ? `&sites=${selectedSites.join(",")}`
263
+ : "",
264
+ userType ? `&userType=${encodeURIComponent(userType)}` : "",
265
+ userCategory
266
+ ? `&userCategory=${encodeURIComponent(userCategory)}`
267
+ : "",
268
+ ].join("");
269
+ if (comparisonMode) {
270
+ return (
271
+ <div
272
+ style={{ display: "flex", flexDirection: "column", gap: 16 }}
273
+ >
274
+ <Components.ComparisonStatBox
275
+ title={`${featureTitle} Requests`}
276
+ data={comparisonData.requests}
277
+ prevText={prevText}
278
+ viewGraphLink={`/chart?entity=${values.analyticsKey}&startTime=${startTime}&endTime=${endTime}&key=Request&countType=total&dayCount=${dayCount}${chartSuffix}`}
279
+ isLoading={comparisonData.isLoading}
280
+ />
281
+ <Components.ComparisonStatBox
282
+ title={`Completed ${featureTitle} Requests`}
283
+ data={comparisonData.completedRequests}
284
+ prevText={prevText}
285
+ viewGraphLink={`/chart?entity=${values.analyticsKey}&startTime=${startTime}&endTime=${endTime}&key=RequestCompleted&countType=unique&dayCount=${dayCount}${chartSuffix}`}
286
+ isLoading={comparisonData.isLoading}
287
+ />
288
+ <Components.ComparisonStatBox
289
+ title={`${featureTitle} Comments`}
290
+ data={comparisonData.comments}
291
+ prevText={prevText}
292
+ viewGraphLink={`/chart?entity=${values.analyticsKey}&startTime=${startTime}&endTime=${endTime}&key=Comment&countType=total&dayCount=${dayCount}${chartSuffix}`}
293
+ isLoading={comparisonData.isLoading}
294
+ />
295
+ </div>
296
+ );
297
+ }
298
+ return (
299
+ <>
300
+ <Components.StatBox
301
+ title={`${featureTitle} Requests`}
302
+ icon={faWrench}
303
+ value={analyticsData.requests}
304
+ previousValue={analyticsData.prevRequests}
305
+ prevText={prevText}
306
+ viewGraphLink={`/chart?entity=${values.analyticsKey}&startTime=${startTime}&endTime=${endTime}&key=Request&countType=total&dayCount=${dayCount}${chartSuffix}`}
307
+ isLoading={analyticsData.isLoading}
308
+ />
309
+ <Components.StatBox
310
+ title={`Completed ${featureTitle} Requests`}
311
+ icon={faCircleCheck}
312
+ value={analyticsData.completedRequests}
313
+ previousValue={analyticsData.prevCompletedRequests}
314
+ prevText={prevText}
315
+ viewGraphLink={`/chart?entity=${values.analyticsKey}&startTime=${startTime}&endTime=${endTime}&key=RequestCompleted&countType=unique&dayCount=${dayCount}${chartSuffix}`}
316
+ isLoading={analyticsData.isLoading}
317
+ />
318
+ <Components.StatBox
319
+ title={`${featureTitle} Comments`}
320
+ icon={faComment}
321
+ value={analyticsData.comments}
322
+ previousValue={analyticsData.prevComments}
323
+ prevText={prevText}
324
+ viewGraphLink={`/chart?entity=${values.analyticsKey}&startTime=${startTime}&endTime=${endTime}&key=Comment&countType=total&dayCount=${dayCount}${chartSuffix}`}
325
+ isLoading={analyticsData.isLoading}
326
+ />
327
+ </>
328
+ );
329
+ })()}
330
+ </div>
331
+ </div>
332
+ );
156
333
  };
157
334
 
158
335
  const mapStateToProps = (state) => {
159
- const { auth } = state;
160
- return {
161
- auth,
162
- strings: (state.strings && state.strings.config) || {},
163
- };
336
+ const { auth } = state;
337
+ return {
338
+ auth,
339
+ strings: (state.strings && state.strings.config) || {},
340
+ };
164
341
  };
165
342
 
166
343
  const toExport = connect(mapStateToProps, {})(AnalyticsHub);
@@ -123,6 +123,5 @@ const FeatureConfig = {
123
123
  FeatureConfig.env = environment;
124
124
  PlussCore.Config.init(environment);
125
125
  },
126
- notificationPreference: values.notificationPreference,
127
126
  };
128
127
  export default FeatureConfig;
@@ -58,12 +58,6 @@ const values = {
58
58
  textEntityName: 'Job',
59
59
  stringConfigJobStatus: 'maintenanceJobStatusA',
60
60
  stringConfigHideSeen: 'maintenanceDisableSeenA',
61
- // Comment subscription notification preference
62
- notificationPreference: {
63
- key: 'maintenancerequestAComments',
64
- label: 'Maintenance A comments',
65
- permission: 'maintenanceTrackingA',
66
- },
67
61
  };
68
62
 
69
63
  export { values };
@@ -70,12 +70,6 @@ const values = {
70
70
  helpGuideURL: '',
71
71
  stringConfigJobStatus: 'maintenanceJobStatus',
72
72
  stringConfigHideSeen: 'maintenanceDisableSeen',
73
- // Comment subscription notification preference
74
- notificationPreference: {
75
- key: 'maintenancerequestComments',
76
- label: 'Maintenance comments',
77
- permission: 'maintenanceTracking',
78
- },
79
73
  };
80
74
 
81
75
  export { values };
@@ -69,12 +69,6 @@ const values = {
69
69
  helpGuideURL: 'https://www.plusscommunities.com/forms-feature',
70
70
  stringConfigJobStatus: 'maintenanceJobStatusForms',
71
71
  stringConfigHideSeen: 'maintenanceDisableSeenForms',
72
- // Comment subscription notification preference
73
- notificationPreference: {
74
- key: 'maintenancerequestFormsComments',
75
- label: 'Forms comments',
76
- permission: 'maintenanceTrackingForms',
77
- },
78
72
  };
79
73
 
80
74
  export { values };
@@ -69,12 +69,6 @@ const values = {
69
69
  helpGuideURL: 'https://www.plusscommunities.com/forms-feature',
70
70
  stringConfigJobStatus: 'maintenanceJobStatusForms',
71
71
  stringConfigHideSeen: 'maintenanceDisableSeenForms',
72
- // Comment subscription notification preference
73
- notificationPreference: {
74
- key: 'maintenancerequestFormsComments',
75
- label: 'Forms comments',
76
- permission: 'maintenanceTrackingForms',
77
- },
78
72
  };
79
73
 
80
74
  export { values };