tango-app-api-audit 1.0.40 → 1.0.42

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tango-app-api-audit",
3
- "version": "1.0.40",
3
+ "version": "1.0.42",
4
4
  "description": "audit & audit metrics apis",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -36,9 +36,9 @@ export async function userAuditHistory( req, res ) {
36
36
  );
37
37
  }
38
38
 
39
- if ( inputData.auditType?.length > 0 ) {
39
+ if ( inputData.filterByAuditType?.length > 0 ) {
40
40
  filter.push( {
41
- moduleType: { $in: inputData.auditType },
41
+ moduleType: { $in: inputData.filterByAuditType },
42
42
  } );
43
43
  }
44
44
 
@@ -63,14 +63,6 @@ export async function userAuditHistory( req, res ) {
63
63
  ];
64
64
 
65
65
 
66
- if ( inputData.sortColumnName ) {
67
- const sortBy = inputData.sortBy || -1;
68
- query.push( {
69
- $sort: { [inputData.sortColumName]: sortBy },
70
- },
71
- );
72
- }
73
-
74
66
  query.push( {
75
67
  $lookup: {
76
68
  from: 'stores',
@@ -93,17 +85,50 @@ export async function userAuditHistory( req, res ) {
93
85
 
94
86
  } );
95
87
 
88
+
96
89
  query.push( {
97
90
  $unwind: {
98
91
  path: '$store', preserveNullAndEmptyArrays: true,
99
92
  },
100
93
  } );
101
94
 
95
+ query.push( {
96
+ $lookup: {
97
+ from: 'clients',
98
+ let: { clientId: '$clientId' },
99
+ pipeline: [
100
+ {
101
+ $match: {
102
+ $expr: {
103
+ $eq: [ '$clientId', '$$clientId' ],
104
+ },
105
+ },
106
+ },
107
+ {
108
+ $project: {
109
+ clientName: 1,
110
+ },
111
+ },
112
+ ], as: 'client',
113
+ },
114
+
115
+ } );
116
+
117
+
118
+ query.push( {
119
+ $unwind: {
120
+ path: '$client', preserveNullAndEmptyArrays: true,
121
+ },
122
+ } );
123
+
102
124
  query.push( {
103
125
  $project: {
104
126
  storeId: 1,
105
127
  userId: 1,
106
128
  storeName: '$store.storeName',
129
+ clientId: 1,
130
+ clientName: 'client.clientName',
131
+ fileDate: 1,
107
132
  auditType: 1,
108
133
  zoneName: 1,
109
134
  moduleType: 1,
@@ -112,14 +137,51 @@ export async function userAuditHistory( req, res ) {
112
137
  accuracy: { $round: [
113
138
  {
114
139
  $divide: [ { $multiply: [ '$beforeCount', { $ifNull: [ '$afterCount', 0 ] } ] }, 100 ],
115
- }, 2,
140
+ }, 1,
116
141
  ],
117
142
  },
118
- startTime: 1,
119
- endTime: 1,
120
- timeSpent: { $round: [
121
- { $divide: [ '$timeSpent', 60 ] }, 2,
122
- ] },
143
+ startTime: {
144
+ $dateToString: {
145
+ format: '%Y-%m-%d %H:%M:%S',
146
+ date: '$startTime',
147
+ timezone: 'Asia/Kolkata',
148
+ },
149
+ },
150
+ endTime: {
151
+ $dateToString: {
152
+ format: '%Y-%m-%d %H:%M:%S',
153
+ date: '$endTime',
154
+ timezone: 'Asia/Kolkata',
155
+ },
156
+ },
157
+ // timeSpent: { $round: [
158
+ // { $divide: [ '$timeSpent', 60 ] }, 2,
159
+ // ] },
160
+ timeSpent: {
161
+
162
+ $cond: [
163
+ { $lt: [ '$timeSpent', 60 ] }, // Case 1: If less than 60 seconds
164
+ { $concat: [ { $toString: '$timeSpent' }, ' sec' ] },
165
+ {
166
+ $cond: [
167
+ { $lt: [ '$timeSpent', 3600 ] }, // Case 2: If less than 60 minutes (3600 seconds)
168
+ {
169
+ $concat: [
170
+ { $toString: { $round: [ { $divide: [ '$timeSpent', 60 ] }, 1 ] } }, // Convert to minutes
171
+ ' min',
172
+ ],
173
+ },
174
+ {
175
+ $concat: [
176
+ { $toString: { $round: [ { $divide: [ '$timeSpent', 3600 ] }, 1 ] } }, // Convert to hours
177
+ ' hr',
178
+ ],
179
+ },
180
+ ],
181
+ },
182
+ ],
183
+
184
+ },
123
185
  auditStatus: 1,
124
186
  createdAt: 1,
125
187
  },
@@ -129,17 +191,26 @@ export async function userAuditHistory( req, res ) {
129
191
  query.push( {
130
192
  $match: {
131
193
  $or: [
194
+ { clientName: { $regex: inputData.searchValue, $options: 'i' } },
195
+ { clientType: { $regex: inputData.searchValue, $options: 'i' } },
132
196
  { zoneName: { $regex: inputData.searchValue, $options: 'i' } },
133
- { storeId: { $regex: inputData.storeId, $options: 'i' } },
134
- { storeName: { $regex: inputData.storeName, $options: 'i' } },
135
- { moduleType: { $regex: inputData.moduleType, $options: 'i' } },
136
- { auditStatus: { $regex: inputData.auditStatus, $options: 'i' } },
197
+ { storeId: { $regex: inputData.searchValue, $options: 'i' } },
198
+ { storeName: { $regex: inputData.searchValue, $options: 'i' } },
199
+ { moduleType: { $regex: inputData.searchValue, $options: 'i' } },
200
+ { auditStatus: { $regex: inputData.searchValue, $options: 'i' } },
137
201
  ],
138
202
 
139
203
  },
140
204
  } );
141
205
  }
142
206
 
207
+ if ( inputData.sortColumnName ) {
208
+ const sortBy = inputData.sortBy || -1;
209
+ query.push( {
210
+ $sort: { [inputData.sortColumName]: sortBy },
211
+ },
212
+ );
213
+ }
143
214
  const count = await aggregateUserAudit( query );
144
215
  if ( count.length == 0 ) {
145
216
  return res.sendError( 'No Data Found', 204 );
@@ -151,6 +222,8 @@ export async function userAuditHistory( req, res ) {
151
222
  const exportData = [];
152
223
  chunk.forEach( ( element ) => {
153
224
  exportData.push( {
225
+ 'Client Id': element.clientId,
226
+ 'Client Name': element.clientName,
154
227
  'Store Id': element.storeId,
155
228
  'Store Name': element.storeName,
156
229
  'Type': element.auditType,
@@ -163,7 +236,7 @@ export async function userAuditHistory( req, res ) {
163
236
  'End Time': element.endTime,
164
237
  'Time Spent': element.timeSpent,
165
238
  'Audit Status': element.auditStatus,
166
- 'CreatedAt': element.createdAt,
239
+ 'CreatedAt': dayjs( element.createdAt ).format( 'YYYY,MMM D' ),
167
240
 
168
241
  } );
169
242
  } );
@@ -184,7 +257,7 @@ export async function userAuditHistory( req, res ) {
184
257
  } );
185
258
  const result = await aggregateUserAudit( query );
186
259
 
187
- return res.sendSuccess( { result: result } );
260
+ return res.sendSuccess( { result: result, count: count.length } );
188
261
  } catch ( error ) {
189
262
  const err = error.message || 'Internal Server Error';
190
263
  logger.info( { error: error, message: req.body, function: 'userAuditHistory' } );
@@ -214,6 +287,10 @@ export async function storeMetrics( req, res ) {
214
287
  filter.push( { status: { $in: inputData.filterByStatus } } );
215
288
  }
216
289
 
290
+ if ( inputData.filterByType && inputData.filterByType?.length > 0 ) {
291
+ filter.push( { auditType: { $in: inputData.filterByType } } );
292
+ }
293
+
217
294
 
218
295
  const query = [
219
296
  {
@@ -221,16 +298,45 @@ export async function storeMetrics( req, res ) {
221
298
  $and: filter,
222
299
  },
223
300
  },
301
+
302
+ {
303
+ $lookup: {
304
+ from: 'stores',
305
+ let: { storeId: '$storeId' },
306
+ pipeline: [
307
+ {
308
+ $match: {
309
+ $expr: {
310
+ $eq: [ '$storeId', '$$storeId' ],
311
+ },
312
+ },
313
+ },
314
+ {
315
+ $project: {
316
+ _id: 0,
317
+ storeName: 1,
318
+ },
319
+ },
320
+ ], as: 'stores',
321
+ },
322
+ },
323
+ {
324
+ $unwind: {
325
+ path: '$stores',
326
+ preserveNullAndEmptyArrays: true,
327
+ },
328
+ },
224
329
  {
225
330
  $project: {
226
331
  _id: 0,
227
332
  fileDate: 1,
228
333
  storeId: 1,
229
- storeName: '$storeId',
334
+ storeName: '$store.storeName',
335
+ clientName: '',
336
+ clientId: 1,
230
337
  userId: {
231
338
  $arrayElemAt: [ '$userId', { $subtract: [ { $size: '$userId' }, 1 ] } ],
232
339
  },
233
- clientId: 1,
234
340
  auditType: 1,
235
341
  beforeCount: 1,
236
342
  afterCount: { $ifNull: [ '$afterCount', null ] },
@@ -247,56 +353,56 @@ export async function storeMetrics( req, res ) {
247
353
  },
248
354
  {
249
355
  $lookup: {
250
- from: 'stores',
251
- let: { storeId: '$storeId' },
356
+ from: 'users',
357
+ let: { userId: '$userId' },
252
358
  pipeline: [
253
359
  {
254
360
  $match: {
255
361
  $expr: {
256
- $eq: [ '$storeId', '$$storeId' ],
362
+ $eq: [ '$_id', '$$userId' ],
257
363
  },
258
364
  },
259
365
  },
260
366
  {
261
367
  $project: {
262
368
  _id: 0,
263
- storeName: 1,
369
+ userName: 1,
370
+ userEmail: '$email',
264
371
  },
265
372
  },
266
- ], as: 'stores',
373
+ ], as: 'users',
267
374
  },
268
375
  },
269
376
  {
270
377
  $unwind: {
271
- path: '$stores',
378
+ path: '$users',
272
379
  preserveNullAndEmptyArrays: true,
273
380
  },
274
381
  },
275
382
  {
276
383
  $lookup: {
277
- from: 'users',
278
- let: { userId: '$userId' },
384
+ from: 'clients',
385
+ let: { clientId: '$clientId' },
279
386
  pipeline: [
280
387
  {
281
388
  $match: {
282
389
  $expr: {
283
- $eq: [ '$_id', '$$userId' ],
390
+ $eq: [ '$clientId', '$$clientId' ],
284
391
  },
285
392
  },
286
393
  },
287
394
  {
288
395
  $project: {
289
396
  _id: 0,
290
- userName: 1,
291
- userEmail: '$email',
397
+ clientName: 1,
292
398
  },
293
399
  },
294
- ], as: 'users',
400
+ ], as: 'client',
295
401
  },
296
402
  },
297
403
  {
298
404
  $unwind: {
299
- path: '$users',
405
+ path: '$client',
300
406
  preserveNullAndEmptyArrays: true,
301
407
  },
302
408
  },
@@ -308,19 +414,43 @@ export async function storeMetrics( req, res ) {
308
414
  storeName: '$stores.storeName',
309
415
  userName: '$users.userName',
310
416
  userEmail: '$users.userEmail',
311
-
312
417
  clientId: 1,
418
+ clientName: '$client.clientName',
313
419
  auditType: 1,
314
420
  beforeCount: 1,
315
421
  afterCount: { $ifNull: [ '$afterCount', null ] },
316
422
  accuracy: { $round: [
317
423
  { $divide: [
318
424
  { $multiply: [ { $ifNull: [ '$afterCount', 0 ] }, '$beforeCount' ] }, 100,
319
- ] }, 2,
425
+ ] }, 1,
320
426
  ],
321
427
 
322
428
  },
323
- timeSpent: 1,
429
+ timeSpent: {
430
+
431
+ $cond: [
432
+ { $lt: [ '$timeSpent', 60 ] }, // Case 1: If less than 60 seconds
433
+ { $concat: [ { $toString: '$timeSpent' }, ' sec' ] },
434
+ {
435
+ $cond: [
436
+ { $lt: [ '$timeSpent', 3600 ] }, // Case 2: If less than 60 minutes (3600 seconds)
437
+ {
438
+ $concat: [
439
+ { $toString: { $round: [ { $divide: [ '$timeSpent', 60 ] }, 1 ] } }, // Convert to minutes
440
+ ' min',
441
+ ],
442
+ },
443
+ {
444
+ $concat: [
445
+ { $toString: { $round: [ { $divide: [ '$timeSpent', 3600 ] }, 1 ] } }, // Convert to hours
446
+ ' hr',
447
+ ],
448
+ },
449
+ ],
450
+ },
451
+ ],
452
+
453
+ },
324
454
  status: 1,
325
455
  },
326
456
  },
@@ -333,10 +463,23 @@ export async function storeMetrics( req, res ) {
333
463
  $or: [
334
464
  { clientId: { $regex: inputData.searchValue, $options: 'i' } },
335
465
  { clientName: { $regex: inputData.searchValue, $options: 'i' } },
466
+ { storeId: { $regex: inputData.searchValue, $options: 'i' } },
467
+ { storeName: { $regex: inputData.searchValue, $options: 'i' } },
468
+ { userName: { $regex: inputData.searchValue, $options: 'i' } },
469
+ { status: { $regex: inputData.searchValue, $options: 'i' } },
470
+ { auditType: { $regex: inputData.searchValue, $options: 'i' } },
336
471
  ],
337
472
  },
338
473
  } );
339
474
  }
475
+
476
+ if ( inputData.sortColumnName ) {
477
+ const sortBy = inputData.sortBy || -1;
478
+ query.push( {
479
+ $sort: { [inputData.sortColumName]: sortBy },
480
+ },
481
+ );
482
+ }
340
483
  const count = await aggregateStoreAudit( query );
341
484
 
342
485
  if ( count.length == 0 ) {
@@ -361,6 +504,7 @@ export async function storeMetrics( req, res ) {
361
504
  'Store Id': element.storeId,
362
505
  'Store Name': element.storeName,
363
506
  'Client Id': element.clientId,
507
+ 'Client Name': element.clientName,
364
508
  'User Name': element.userName,
365
509
  'User Email': element.userEmail,
366
510
  'Audit Type': element.auditType,
@@ -389,6 +533,7 @@ export async function clientMetrics( req, res ) {
389
533
  let filter = [
390
534
  { fileDateISO: { $gte: dateRange.start } },
391
535
  { fileDateISO: { $lte: dateRange.end } },
536
+ { moduleType: { $eq: 'traffic' } },
392
537
 
393
538
  ];
394
539
  if ( inputData?.filterByClient?.length> 0 ) {
@@ -433,6 +578,7 @@ export async function clientMetrics( req, res ) {
433
578
  {
434
579
  $project: {
435
580
  completedStores: { $cond: [ { $eq: [ '$status', 'completed' ] }, 1, 0 ] },
581
+ notAssignedStores: { $cond: [ { $eq: [ '$status', 'not_assign' ] }, 1, 0 ] },
436
582
  inprogressStores: { $cond: [ { $in: [ '$status', [ 'inprogress', 'drafted', 'assigned' ] ] }, 1, 0 ] },
437
583
  },
438
584
  },
@@ -441,6 +587,8 @@ export async function clientMetrics( req, res ) {
441
587
  _id: { clientId: '$clientId', fileDate: '$fileDate' },
442
588
  completedStores: { $sum: '$completedStores' },
443
589
  inprogressStores: { $sum: '$inprogressStores' },
590
+ notAssignedStores: { $sum: '$notAssignedStores' },
591
+ fileCount: { $sum: 1 },
444
592
  },
445
593
  },
446
594
  {
@@ -448,6 +596,8 @@ export async function clientMetrics( req, res ) {
448
596
  _id: 0,
449
597
  completedStores: 1,
450
598
  inprogressStores: 1,
599
+ notAssignedStores: 1,
600
+ fileCount: 1,
451
601
  completionPercentage: { $ifNull: [
452
602
  {
453
603
  $round: [ {
@@ -482,15 +632,18 @@ export async function clientMetrics( req, res ) {
482
632
  clientName: 1,
483
633
  totalFilesCount: 1,
484
634
  installedStore: 1,
635
+ notAssignedStores: '$storeAudit.notAssignedStores',
485
636
  inprogressStoresCount: '$storeAudit.inprogressStores',
637
+ fileCount: '$storeAudit.fileCount',
486
638
  notAssignedCount: 1,
487
- clientStatus: { $cond: [ { $gt: [ '$storeAudit.inprogressStores', 0 ] }, 'not-taken', { $cond: [ { $gt: [ '$notAssignedCount', 0 ] }, 'pending', 'completed' ] } ] },
639
+ clientStatus: { $cond: [ { $eq: [ '$storeAudit.fileCount', 0 ] }, 'not-taken', { $cond: [ { $or: [ { $gt: [ '$notAssignedCount', 0 ] }, { $gt: [ '$storeAudit.notAssignedStores', 0 ] } ] }, 'pending', 'completed' ] } ] },
488
640
  completedStores: '$storeAudit.completedStores',
489
641
  completionPercentage: '$storeAudit.completionPercentage',
490
642
 
491
643
  },
492
644
  },
493
645
  ];
646
+
494
647
  if ( inputData.searchValue && inputData.searchValue!== '' ) {
495
648
  query.push( {
496
649
  $match: {
@@ -532,6 +685,7 @@ export async function clientMetrics( req, res ) {
532
685
  'Audit Stores': '$totalFilesCount',
533
686
  'Inprogress Stores': '$inprogressStoresCount',
534
687
  'Not Assigned': '$notAssignedCount',
688
+ 'Not Assigned Stores': '$notAssignedStores',
535
689
  'Completed': '$completedStores',
536
690
  'Status': '$clientStatus',
537
691
  },
@@ -573,6 +727,11 @@ export async function userMetrics( req, res ) {
573
727
 
574
728
  );
575
729
  }
730
+ if ( inputData?.filterByAuditType?.length> 0 ) {
731
+ filter.push(
732
+ { moduleType: { $in: inputData.filterByAuditType } },
733
+ );
734
+ }
576
735
  if ( inputData?.filterByStatus?.length> 0 ) {
577
736
  filter.push(
578
737
  { auditStatus: { $in: inputData.filterByStatus } },
@@ -599,7 +758,7 @@ export async function userMetrics( req, res ) {
599
758
  startTime: 1,
600
759
  endTime: 1,
601
760
  totalCompletedFiles: { $cond: [ { $eq: [ '$auditStatus', 'completed' ] }, 1, 0 ] },
602
- beforeCount: { $cond: [ { $eq: [ '$auditStatus', 'completed' ] }, '$beforeCount', 0 ] },
761
+ beforeCount: 1,
603
762
  afterCount: { $cond: [ { $eq: [ '$auditStatus', 'completed' ] }, '$afterCount', 0 ] },
604
763
  },
605
764
  },
@@ -694,6 +853,7 @@ export async function userMetrics( req, res ) {
694
853
  beforeCount: 1,
695
854
  afterCount: 1,
696
855
  checkIntime: 1,
856
+ zoneName: 1,
697
857
  checkOutTime: 1,
698
858
  workingHours: {
699
859
  $concat: [ '$hours', ':', '$minutes', ':', '$seconds' ],
@@ -735,8 +895,20 @@ export async function userMetrics( req, res ) {
735
895
  totalCompletedFiles: 1,
736
896
  beforeCount: 1,
737
897
  afterCount: 1,
738
- checkIntime: 1,
739
- checkOutTime: 1,
898
+ checkIntime: {
899
+ $dateToString: {
900
+ format: '%Y-%m-%d %H:%M:%S',
901
+ date: '$checkIntime',
902
+ timezone: 'Asia/Kolkata',
903
+ },
904
+ },
905
+ checkOutTime: {
906
+ $dateToString: {
907
+ format: '%Y-%m-%d %H:%M:%S',
908
+ date: '$checkOutTime',
909
+ timezone: 'Asia/Kolkata',
910
+ },
911
+ },
740
912
  workingHours: 1,
741
913
  },
742
914
  },
@@ -745,7 +917,7 @@ export async function userMetrics( req, res ) {
745
917
  query.push( {
746
918
  $match: {
747
919
  $or: [
748
- { clientId: { $regex: inputData.searchValue, $options: 'i' } },
920
+ { userName: { $regex: inputData.searchValue, $options: 'i' } },
749
921
  { clientName: { $regex: inputData.searchValue, $options: 'i' } },
750
922
  ],
751
923
 
@@ -775,7 +947,7 @@ export async function userMetrics( req, res ) {
775
947
  $project: {
776
948
  '_id': 0,
777
949
  'File Date': '$fileDate',
778
- 'zuser Name': '$userName',
950
+ 'User Name': '$userName',
779
951
  'User Id': '$userId',
780
952
  'Total Completed Files Count': '$totalCompletedFiles',
781
953
  'Total efore Count': '$beforeCount',
@@ -1152,7 +1324,8 @@ export async function overAllAuditSummary( req, res ) {
1152
1324
  let filters =[
1153
1325
  { fileDateISO: { $gte: dateRange.start } },
1154
1326
  { fileDateISO: { $lte: dateRange.end } },
1155
-
1327
+ { moduleType: { $eq: inputData.moduleType },
1328
+ },
1156
1329
  ];
1157
1330
  if ( inputData.clientId.length > 0 ) {
1158
1331
  filters.push( { clientId: { $in: inputData.clientId } } );
@@ -1,4 +1,4 @@
1
- import { chunkArray, download, fileUpload, getJsonFileData, getUTC, insertOpenSearchData, listFileByPath, sendMessageToQueue, signedUrl, sqsReceive } from 'tango-app-api-middleware';
1
+ import { download, fileUpload, getJsonFileData, getUTC, insertOpenSearchData, listFileByPath, sendMessageToQueue, signedUrl, sqsReceive } from 'tango-app-api-middleware';
2
2
  import { aggregateZoneUserAudit, createZoneUserAudit, findOneZoneUserAudit } from '../service/zoneUserAudit.service.js';
3
3
  import { logger } from 'tango-app-api-middleware';
4
4
  import { aggregateAssignZoneAudit } from '../service/assignZoneAudit.service.js';
@@ -899,40 +899,30 @@ export async function workSpace( req, res ) {
899
899
  }
900
900
  }
901
901
 
902
- export async function userZoneAuditHistory( req, res ) {
902
+ export async function storeMetrics( req, res ) {
903
903
  try {
904
904
  const inputData = req.body;
905
- const userId = inputData.userId || req.user._id;
906
905
  const limit = inputData.limit || 10;
907
906
  const offset = inputData.offset ? ( inputData.offset - 1 ) * limit : 0;
908
907
  const dateRange = await getUTC( new Date( inputData.fromDate ), new Date( inputData.toDate ) );
909
908
  let filter = [
910
-
911
- { userId: { $eq: userId } },
912
- { moduleType: 'zone' },
913
-
909
+ { fileDateISO: { $gte: dateRange.start } },
910
+ { fileDateISO: { $lte: dateRange.end } },
914
911
  ];
915
- if ( inputData.fileType == 'fileDate' ) {
916
- filter.push(
917
- { fileDateISO: { $gte: dateRange.start } },
918
- { fileDateISO: { $lte: dateRange.end } },
919
- );
920
- } else {
921
- filter.push(
922
- { createdAt: { $gte: dateRange.start } },
923
- { createdAt: { $lte: dateRange.end } },
924
- );
912
+ if ( inputData.filterByClientId && inputData.filterByClientId?.length > 0 ) {
913
+ filter.push( { clientId: { $in: inputData.filterByClientId } } );
925
914
  }
926
- if ( inputData.filterByStoreId?.length > 0 ) {
927
- filter.push( {
928
- storeId: { $in: inputData.filterByStoreId },
929
- } );
915
+
916
+ if ( inputData.filterByStoreId && inputData.filterByStoreId?.length > 0 ) {
917
+ filter.push( { storeId: { $in: inputData.filterByStoreId } } );
930
918
  }
931
919
 
932
- if ( inputData.filterByStatus?.length > 0 ) {
933
- filter.push( {
934
- auditStatus: { $in: inputData.filterByStatus },
935
- } );
920
+ if ( inputData.filterByStatus && inputData.filterByStatus?.length > 0 ) {
921
+ filter.push( { status: { $in: inputData.filterByStatus } } );
922
+ }
923
+
924
+ if ( inputData.filterByType && inputData.filterByType?.length > 0 ) {
925
+ filter.push( { auditType: { $in: inputData.filterByType } } );
936
926
  }
937
927
 
938
928
  const query = [
@@ -941,19 +931,179 @@ export async function userZoneAuditHistory( req, res ) {
941
931
  $and: filter,
942
932
  },
943
933
  },
934
+ {
935
+ $lookup: {
936
+ from: 'stores',
937
+ let: { storeId: '$storeId' },
938
+ pipeline: [
939
+ {
940
+ $match: {
941
+ $expr: {
942
+ $eq: [ '$storeId', '$$storeId' ],
943
+ },
944
+ },
945
+ },
946
+ {
947
+ $project: {
948
+ storeName: 1,
949
+ },
950
+ },
951
+ ], as: 'store',
952
+ },
953
+ },
954
+ {
955
+ $unwind: {
956
+ path: '$store', preserveNullAndEmptyArrays: true,
957
+ },
958
+ },
959
+ {
960
+ $project: {
961
+ _id: 0,
962
+ fileDate: 1,
963
+ storeId: 1,
964
+ storeName: '$store.storeName',
965
+ clientName: '',
966
+ clientId: 1,
967
+ userId: {
968
+ $arrayElemAt: [ '$userId', { $subtract: [ { $size: '$userId' }, 1 ] } ],
969
+ },
970
+ clientId: 1,
971
+ auditType: 1,
972
+ beforeCount: 1,
973
+ afterCount: { $ifNull: [ '$afterCount', null ] },
974
+ accuracy: { $round: [
975
+ { $divide: [
976
+ { $multiply: [ { $ifNull: [ '$afterCount', 0 ] }, '$beforeCount' ] }, 100,
977
+ ] }, 2,
978
+ ],
979
+
980
+ },
981
+ timeSpent: 1,
982
+ status: 1,
983
+ },
984
+ },
985
+ {
986
+ $lookup: {
987
+ from: 'users',
988
+ let: { userId: '$userId' },
989
+ pipeline: [
990
+ {
991
+ $match: {
992
+ $expr: {
993
+ $eq: [ '$_id', '$$userId' ],
994
+ },
995
+ },
996
+ },
997
+ {
998
+ $project: {
999
+ _id: 0,
1000
+ userName: 1,
1001
+ userEmail: '$email',
1002
+ },
1003
+ },
1004
+ ], as: 'users',
1005
+ },
1006
+ },
1007
+ {
1008
+ $unwind: {
1009
+ path: '$users',
1010
+ preserveNullAndEmptyArrays: true,
1011
+ },
1012
+ },
1013
+ {
1014
+ $lookup: {
1015
+ from: 'clients',
1016
+ let: { clientId: '$clientId' },
1017
+ pipeline: [
1018
+ {
1019
+ $match: {
1020
+ $expr: {
1021
+ $eq: [ '$clientId', '$$clientId' ],
1022
+ },
1023
+ },
1024
+ },
1025
+ {
1026
+ $project: {
1027
+ _id: 0,
1028
+ clientName: 1,
1029
+ },
1030
+ },
1031
+ ], as: 'client',
1032
+ },
1033
+ },
1034
+ {
1035
+ $unwind: {
1036
+ path: '$client',
1037
+ preserveNullAndEmptyArrays: true,
1038
+ },
1039
+ },
1040
+ {
1041
+ $project: {
1042
+ _id: 0,
1043
+ fileDate: 1,
1044
+ storeId: 1,
1045
+ storeName: '$stores.storeName',
1046
+ userName: '$users.userName',
1047
+ userEmail: '$users.userEmail',
1048
+ clientId: 1,
1049
+ clientName: '$client.clientName',
1050
+ auditType: 1,
1051
+ beforeCount: 1,
1052
+ afterCount: { $ifNull: [ '$afterCount', null ] },
1053
+ accuracy: { $round: [
1054
+ { $divide: [
1055
+ { $multiply: [ { $ifNull: [ '$afterCount', 0 ] }, '$beforeCount' ] }, 100,
1056
+ ] }, 1,
1057
+ ],
1058
+
1059
+ },
1060
+ timeSpent: {
1061
+
1062
+ $cond: [
1063
+ { $lt: [ '$timeSpent', 60 ] }, // Case 1: If less than 60 seconds
1064
+ { $concat: [ { $toString: '$timeSpent' }, ' sec' ] },
1065
+ {
1066
+ $cond: [
1067
+ { $lt: [ '$timeSpent', 3600 ] }, // Case 2: If less than 60 minutes (3600 seconds)
1068
+ {
1069
+ $concat: [
1070
+ { $toString: { $round: [ { $divide: [ '$timeSpent', 60 ] }, 1 ] } }, // Convert to minutes
1071
+ ' min',
1072
+ ],
1073
+ },
1074
+ {
1075
+ $concat: [
1076
+ { $toString: { $round: [ { $divide: [ '$timeSpent', 3600 ] }, 1 ] } }, // Convert to hours
1077
+ ' hr',
1078
+ ],
1079
+ },
1080
+ ],
1081
+ },
1082
+ ],
1083
+
1084
+ },
1085
+ status: 1,
1086
+ },
1087
+ },
1088
+
944
1089
  ];
945
1090
 
946
- if ( inputData.searchValue && inputData.searchValue!== '' ) {
1091
+ if ( inputData.searchValue && inputData.searchValue !== '' ) {
947
1092
  query.push( {
948
1093
  $match: {
949
1094
  $or: [
950
1095
  { clientId: { $regex: inputData.searchValue, $options: 'i' } },
951
1096
  { clientName: { $regex: inputData.searchValue, $options: 'i' } },
1097
+ { storeId: { $regex: inputData.searchValue, $options: 'i' } },
1098
+ { storeName: { $regex: inputData.searchValue, $options: 'i' } },
1099
+ { userName: { $regex: inputData.searchValue, $options: 'i' } },
1100
+ { status: { $regex: inputData.searchValue, $options: 'i' } },
1101
+ { auditType: { $regex: inputData.searchValue, $options: 'i' } },
952
1102
  ],
953
-
954
1103
  },
955
1104
  } );
956
1105
  }
1106
+
957
1107
  if ( inputData.sortColumnName ) {
958
1108
  const sortBy = inputData.sortBy || -1;
959
1109
  query.push( {
@@ -962,108 +1112,229 @@ export async function userZoneAuditHistory( req, res ) {
962
1112
  );
963
1113
  }
964
1114
 
965
- query.push( {
966
- $lookup: {
967
- from: 'stores',
968
- let: { storeId: '$storeId' },
969
- pipeline: [
970
- {
971
- $match: {
972
- $expr: {
973
- $eq: [ '$storeId', '$$storeId' ],
1115
+ const count = await aggregateStoreZoneAudit( query );
1116
+
1117
+ if ( count.length == 0 ) {
1118
+ return res.sendError( 'No data Found', 204 );
1119
+ }
1120
+ if ( !inputData.isExport ) {
1121
+ query.push( {
1122
+ $skip: offset,
1123
+ },
1124
+ {
1125
+ $limit: limit,
1126
+ } );
1127
+ } else {
1128
+ query.push( { $limit: 10000 } );
1129
+ }
1130
+ const result = await aggregateStoreZoneAudit( query );
1131
+ if ( inputData.isExport ) {
1132
+ const exportdata = [];
1133
+ result.forEach( ( element ) => {
1134
+ exportdata.push( {
1135
+ 'File Date': element.fileDate,
1136
+ 'Store Id': element.storeId,
1137
+ 'Store Name': element.storeName,
1138
+ 'Client Id': element.clientId,
1139
+ 'Client Name': element.clientName,
1140
+ 'User Name': element.userName,
1141
+ 'User Email': element.userEmail,
1142
+ 'Audit Type': element.auditType,
1143
+ 'Accuracy': element.accuracy,
1144
+ 'Time Spent': element.timeSpent,
1145
+ 'Status': element.status,
1146
+ } );
1147
+ } );
1148
+ await download( exportdata, res );
1149
+ return;
1150
+ }
1151
+ return res.sendSuccess( { result: result, count: count.length } );
1152
+ } catch ( error ) {
1153
+ const err = error.message || 'Internal Server Error';
1154
+ logger.error( { error: error, data: req.body, function: 'storeMetrics' } );
1155
+ return res.sendError( err, 500 );
1156
+ }
1157
+ }
1158
+
1159
+ export async function clientMetrics( req, res ) {
1160
+ try {
1161
+ const inputData = req.body;
1162
+ const limit = inputData.limit || 10;
1163
+ const offset = inputData.offset ? ( inputData.offset - 1 ) * limit : 0;
1164
+ const dateRange = await getUTC( new Date( inputData.fromDate ), new Date( inputData.toDate ) );
1165
+ let filter = [
1166
+ { fileDateISO: { $gte: dateRange.start } },
1167
+ { fileDateISO: { $lte: dateRange.end } },
1168
+ { moduleType: { $eq: 'zone' } },
1169
+
1170
+ ];
1171
+ if ( inputData?.filterByClient?.length> 0 ) {
1172
+ filter.push(
1173
+ { clientId: { $in: inputData.filterByClient } },
1174
+ );
1175
+ }
1176
+ const query = [
1177
+ {
1178
+ $match: {
1179
+ $and: filter,
1180
+ },
1181
+ },
1182
+ {
1183
+ $project: {
1184
+ fileDate: 1,
1185
+ clientId: 1,
1186
+ clientName: 1,
1187
+ queueName: 1,
1188
+ installedStore: 1,
1189
+ totalFilesCount: 1,
1190
+ notAssignedCount: { $ifNull: [ 0, 0 ] },
1191
+ clientStatus: { $ifNull: [ '', '' ] },
1192
+ },
1193
+ },
1194
+ {
1195
+ $lookup: {
1196
+ 'from': 'storeZoneAudit',
1197
+ 'let': { clientId: '$clientId', fileDate: '$fileDate', totalAuditFiles: '$totalAuditFiles' },
1198
+ 'pipeline': [
1199
+
1200
+ {
1201
+ $match: {
1202
+ $expr: {
1203
+ $and: [
1204
+ { $eq: [ '$clientId', '$$clientId' ] },
1205
+ { $eq: [ '$fileDate', '$$fileDate' ] },
1206
+ ],
1207
+ },
974
1208
  },
975
1209
  },
976
- },
977
- {
978
- $project: {
979
- storeName: 1,
1210
+ {
1211
+ $project: {
1212
+ completedStores: { $cond: [ { $eq: [ '$status', 'completed' ] }, 1, 0 ] },
1213
+ notAssignedStores: { $cond: [ { $eq: [ '$status', 'not_assign' ] }, 1, 0 ] },
1214
+ inprogressStores: { $cond: [ { $in: [ '$status', [ 'inprogress', 'drafted', 'assigned' ] ] }, 1, 0 ] },
1215
+ },
980
1216
  },
981
- },
982
- ], as: 'store',
1217
+ {
1218
+ $group: {
1219
+ _id: { clientId: '$clientId', fileDate: '$fileDate' },
1220
+ completedStores: { $sum: '$completedStores' },
1221
+ inprogressStores: { $sum: '$inprogressStores' },
1222
+ notAssignedStores: { $sum: '$notAssignedStores' },
1223
+ fileCount: { $sum: 1 },
1224
+ },
1225
+ },
1226
+ {
1227
+ $project: {
1228
+ _id: 0,
1229
+ completedStores: 1,
1230
+ inprogressStores: 1,
1231
+ notAssignedStores: 1,
1232
+ fileCount: 1,
1233
+ completionPercentage: { $ifNull: [
1234
+ {
1235
+ $round: [ {
1236
+ $multiply: [
1237
+ 100, {
1238
+ $divide: [
1239
+ '$completedStores', '$$totalAuditFiles',
1240
+ ],
1241
+ },
1242
+ ],
1243
+ }, 1 ],
1244
+
1245
+ },
1246
+ 0,
1247
+ ],
1248
+ },
1249
+ },
1250
+ },
1251
+ ], 'as': 'storeZoneAudit',
1252
+ },
983
1253
  },
984
-
985
- } );
986
-
987
- query.push( {
988
- $unwind: {
989
- path: '$store', preserveNullAndEmptyArrays: true,
1254
+ {
1255
+ $unwind: {
1256
+ path: '$storeZoneAudit',
1257
+ preserveNullAndEmptyArrays: true,
1258
+ },
990
1259
  },
991
- } );
1260
+ {
1261
+ $project: {
1262
+ fileDate: 1,
1263
+ clientId: 1,
1264
+ clientName: 1,
1265
+ totalFilesCount: 1,
1266
+ installedStore: 1,
1267
+ fileCount: '$storeZoneAudit.fileCount',
1268
+ notAssignedStores: '$storeZoneAudit.notAssignedStores',
1269
+ inprogressStoresCount: '$storeZoneAudit.inprogressStores',
1270
+ notAssignedCount: 1,
1271
+ clientStatus: { $cond: [ { $eq: [ '$storeZoneAudit.fileCount', 0 ] }, 'not-taken', { $cond: [ { $or: [ { $gt: [ '$notAssignedCount', 0 ] }, { $gt: [ '$storeZoneAudit.notAssignedStores', 0 ] } ] }, 'pending', 'completed' ] } ] },
1272
+ completedStores: '$storeZoneAudit.completedStores',
1273
+ completionPercentage: '$storeZoneAudit.completionPercentage',
992
1274
 
1275
+ },
1276
+ },
1277
+ ];
993
1278
 
994
- query.push( {
1279
+ if ( inputData.searchValue && inputData.searchValue!== '' ) {
1280
+ query.push( {
1281
+ $match: {
1282
+ $or: [
1283
+ { clientId: { $regex: inputData.searchValue, $options: 'i' } },
1284
+ { clientName: { $regex: inputData.searchValue, $options: 'i' } },
1285
+ ],
995
1286
 
996
- $project: {
997
- storeId: 1,
998
- userId: 1,
999
- storeName: '$store.storeName',
1000
- zoneName: 1,
1001
- auditType: 1,
1002
- beforeCount: 1,
1003
- afterCount: 1,
1004
- accuracy: { $round: [
1005
- {
1006
- $divide: [ { $multiply: [ '$beforeCount', { $ifNull: [ '$afterCount', 0 ] } ] }, 100 ],
1007
- }, 2,
1008
- ],
1009
1287
  },
1010
- startTime: 1,
1011
- endTime: 1,
1012
- timeSpent: { $round: [
1013
- { $divide: [ '$timeSpent', 60 ] }, 2,
1014
- ] },
1015
- auditStatus: 1,
1016
- createdAt: 1,
1288
+ } );
1289
+ }
1290
+ if ( inputData.sortColumnName ) {
1291
+ const sortBy = inputData.sortBy || -1;
1292
+ query.push( {
1293
+ $sort: { [inputData.sortColumName]: sortBy },
1017
1294
  },
1295
+ );
1296
+ }
1018
1297
 
1019
- } );
1020
-
1021
- const count = await aggregateUserAudit( query );
1298
+ const count = await aggregateAuditClientData( query );
1022
1299
  if ( count.length == 0 ) {
1023
1300
  return res.sendError( 'No Data Found', 204 );
1024
1301
  }
1025
1302
 
1303
+ query.push(
1304
+ { $skip: offset },
1305
+ { $limit: limit },
1306
+ );
1026
1307
  if ( inputData.isExport ) {
1027
- const chunkedMappingData = await chunkArray( count, 10 );
1028
- const promises = chunkedMappingData.map( async ( chunk ) => {
1029
- const exportData = [];
1030
- chunk.forEach( ( element ) => {
1031
- exportData.push( {
1032
- 'storeId': element.storeId,
1033
- 'storeName': element.storeName,
1034
- 'auditType': element.auditType,
1035
- 'beforeCount': element.beforeCount,
1036
- 'afterCount': element.afterCount,
1037
- 'accuracy': element.accuracy,
1038
- 'startTime': element.startTime,
1039
- 'endTime': element.endTime,
1040
- 'timeSpent': element.timeSpent,
1041
- 'auditStatus': element.auditStatus,
1042
- 'createdAt': element.createdAt,
1308
+ query.push(
1309
+ {
1310
+ $project: {
1311
+ '_id': 0,
1312
+ 'File Date': '$fileDate',
1313
+ 'Client Name': '$clientName',
1314
+ 'Client Id': '$clientId',
1315
+ 'Completed Percentage': '$completionPercentage',
1316
+ 'Installed Stores': '$installedStore',
1317
+ 'Audit Stores': '$totalFilesCount',
1318
+ 'Inprogress Stores': '$inprogressStoresCount',
1319
+ 'Not Assigned': '$notAssignedCount',
1320
+ 'Not Assigned Stores': '$notAssignedStores',
1321
+ 'Completed': '$completedStores',
1322
+ 'Status': '$clientStatus',
1323
+ },
1324
+ },
1325
+ );
1326
+ }
1043
1327
 
1044
- } );
1045
- } );
1046
- return exportData;
1047
- } );
1048
- const mappedArrays = await Promise.all( promises );
1049
- mappedArrays.flat();
1050
1328
 
1051
- await download( mappedArrays, res );
1329
+ const result = await aggregateAuditClientData( query );
1330
+ if ( inputData.isExport ) {
1331
+ await download( result, res );
1052
1332
  return;
1053
1333
  }
1054
-
1055
- query.push( {
1056
- $skip: offset,
1057
- },
1058
- {
1059
- $limit: limit,
1060
- } );
1061
- const result = await aggregateUserAudit( query );
1062
-
1063
- return res.sendSuccess( { result: result } );
1334
+ return res.sendSuccess( { result: result, count: count.length } );
1064
1335
  } catch ( error ) {
1065
- const err = error.message || 'Internal Server Error';
1066
- logger.info( { error: error, message: req.body, function: 'userZoneAuditHistory' } );
1336
+ const err = error.error || 'Internal Server Error';
1337
+ logger.info( { error: error, message: req.body, function: 'clientMetrics' } );
1067
1338
  return res.sendError( err, 500 );
1068
1339
  }
1069
1340
  }
@@ -1,5 +1,5 @@
1
1
  import j2s from 'joi-to-swagger';
2
- import { getDraftedDataSchema, getFileSchema, saveDraftSchema, saveSchema, workSpaceSchema } from '../dtos/zoneAudit.dtos.js';
2
+ import { clientMetricsSchema, getDraftedDataSchema, getFileSchema, saveDraftSchema, saveSchema, storeMetricsSchema, workSpaceSchema } from '../dtos/zoneAudit.dtos.js';
3
3
 
4
4
  export const zoneAuditDocs = {
5
5
  '/v3/zone-audit/get-file': {
@@ -155,4 +155,51 @@ export const zoneAuditDocs = {
155
155
  },
156
156
  },
157
157
 
158
+ // metrics
159
+ '/v3/zone-audit/metrics/store-metrics': {
160
+ post: {
161
+ tags: [ 'Zone Audit Metrics' ],
162
+ description: `Get store level zone metrics`,
163
+ operationId: 'save',
164
+ parameters: {},
165
+ requestBody: {
166
+ content: {
167
+ 'application/json': {
168
+ schema: j2s( storeMetricsSchema ).swagger,
169
+ },
170
+ },
171
+ },
172
+ responses: {
173
+ 200: { description: 'Successful' },
174
+ 401: { description: 'Unauthorized User' },
175
+ 422: { description: 'Field Error' },
176
+ 500: { description: 'Server Error' },
177
+ 204: { description: 'Not Found' },
178
+ },
179
+ },
180
+ },
181
+
182
+ '/v3/zone-audit/metrics/client-metrics': {
183
+ post: {
184
+ tags: [ 'Zone Audit Metrics' ],
185
+ description: `Get list of client wise details with date range`,
186
+ operationId: 'metrics/client-metrics',
187
+ parameters: {},
188
+ requestBody: {
189
+ content: {
190
+ 'application/json': {
191
+ schema: j2s( clientMetricsSchema ).swagger,
192
+ },
193
+ },
194
+ },
195
+ responses: {
196
+ 200: { description: 'Successful' },
197
+ 401: { description: 'Unauthorized User' },
198
+ 422: { description: 'Field Error' },
199
+ 500: { description: 'Server Error' },
200
+ 204: { description: 'Not Found' },
201
+ },
202
+ },
203
+ },
204
+
158
205
  };
@@ -21,6 +21,7 @@ export const userAuditHistorySchema = joi.object(
21
21
  fileType: joi.string().required(),
22
22
  filterByStoreId: joi.array().required(),
23
23
  filterByType: joi.array().optional(),
24
+ filterByAuditType: joi.array().optional(),
24
25
  filterByStatus: joi.array().optional(),
25
26
  searchValue: joi.string().optional().allow( '' ),
26
27
  limit: joi.number().optional(),
@@ -44,7 +45,7 @@ export const storeMetricsSchema = joi.object(
44
45
  filterByClientId: joi.array().required(),
45
46
  filterByStoreId: joi.array().optional(),
46
47
  filterByStatus: joi.array().optional(),
47
- filterByAuditType: joi.array().optional(),
48
+ filterByType: joi.array().optional(),
48
49
  sortColumnName: joi.string().optional(),
49
50
  sortBy: joi.number().optional(),
50
51
  limit: joi.number().optional(),
@@ -83,6 +84,7 @@ export const userMetricsSchema = joi.object(
83
84
  fileType: joi.string().required(),
84
85
  searchValue: joi.string().optional().allow( '' ),
85
86
  filterByUser: joi.array().optional(),
87
+ filterByAuditType: joi.array().optional(),
86
88
  filterByStatus: joi.array().optional(),
87
89
  sortColumnName: joi.string().optional(),
88
90
  sortBy: joi.number().optional(),
@@ -145,6 +147,7 @@ export const overAllAuditSummarySchema = joi.object(
145
147
  fromDate: joi.string().required(),
146
148
  toDate: joi.string().required(),
147
149
  clientId: joi.array().optional(),
150
+ moduleType: joi.string().required(),
148
151
  },
149
152
  );
150
153
 
@@ -87,3 +87,43 @@ export const workSpaceValid = {
87
87
  query: workSpaceSchema,
88
88
  };
89
89
 
90
+ export const storeMetricsSchema = joi.object(
91
+ {
92
+ fromDate: joi.string().required(),
93
+ toDate: joi.string().required(),
94
+ searchValue: joi.string().optional().allow( '' ),
95
+ filterByClientId: joi.array().required(),
96
+ filterByStoreId: joi.array().optional(),
97
+ filterByStatus: joi.array().optional(),
98
+ filterByType: joi.array().optional(),
99
+ sortColumnName: joi.string().optional(),
100
+ sortBy: joi.number().optional(),
101
+ limit: joi.number().optional(),
102
+ offset: joi.number().optional(),
103
+ isExport: joi.boolean().optional(),
104
+ },
105
+ );
106
+
107
+ export const storeMetricsValid = {
108
+ body: storeMetricsSchema,
109
+ };
110
+
111
+ export const clientMetricsSchema = joi.object(
112
+ {
113
+ fromDate: joi.string().required(),
114
+ toDate: joi.string().required(),
115
+ searchValue: joi.string().optional().allow( '' ),
116
+ filterByClient: joi.array().required(),
117
+ filterByStatus: joi.array().optional(),
118
+ sortColumnName: joi.string().optional(),
119
+ sortBy: joi.number().optional(),
120
+ limit: joi.number().optional(),
121
+ offset: joi.number().optional(),
122
+ isExport: joi.boolean().optional(),
123
+ },
124
+ );
125
+
126
+ export const clientMetricsValid = {
127
+ body: clientMetricsSchema,
128
+ };
129
+
@@ -1,8 +1,8 @@
1
1
  import { Router } from 'express';
2
- import { getDraftedDataValid, getFileValid, saveDraftValid, saveValid, workSpaceValid } from '../dtos/zoneAudit.dtos.js';
2
+ import { clientMetricsValid, getDraftedDataValid, getFileValid, saveDraftValid, saveValid, storeMetricsValid, workSpaceValid } from '../dtos/zoneAudit.dtos.js';
3
3
  import { isExistsQueue, validateUserAudit } from '../validation/audit.validation.js';
4
4
  import { isAllowedSessionHandler, validate } from 'tango-app-api-middleware';
5
- import { getDraftedData, getZoneAuditFile, save, saveDraft, workSpace } from '../controllers/zoneAudit.controller.js';
5
+ import { clientMetrics, getDraftedData, getZoneAuditFile, save, saveDraft, storeMetrics, workSpace } from '../controllers/zoneAudit.controller.js';
6
6
 
7
7
  export const zoneAuditRouter=Router();
8
8
 
@@ -12,3 +12,8 @@ zoneAuditRouter.post( '/save-draft', isAllowedSessionHandler, validate( saveDraf
12
12
  zoneAuditRouter.get( '/get-drafted-data', isAllowedSessionHandler, validate( getDraftedDataValid ), getDraftedData );
13
13
  zoneAuditRouter.post( '/save', isAllowedSessionHandler, validate( saveValid ), validateUserAudit, save );
14
14
  zoneAuditRouter.get( '/work-space', isAllowedSessionHandler, validate( workSpaceValid ), workSpace );
15
+
16
+ // metrics
17
+
18
+ zoneAuditRouter.post( '/metrics/store-metrics', isAllowedSessionHandler, validate( storeMetricsValid ), storeMetrics );
19
+ zoneAuditRouter.post( '/metrics/client-metrics', isAllowedSessionHandler, validate( clientMetricsValid ), clientMetrics );