tango-app-api-client 3.3.3-beta.0 → 3.3.3-beta.10

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,43 +1,42 @@
1
- {
2
- "name": "tango-app-api-client",
3
- "version": "3.3.3-beta.0",
4
- "description": "client",
5
- "main": "index.js",
6
- "type": "module",
7
- "scripts": {
8
- "start": "nodemon --exec \"eslint --fix . && node index.js\""
9
- },
10
- "engines": {
11
- "node": ">=18.10.0"
12
- },
13
- "author": "praveenraj",
14
- "license": "ISC",
15
- "dependencies": {
16
- "aws-sdk": "^2.1560.0",
17
- "cors": "^2.8.5",
18
- "dotenv": "^16.4.4",
19
- "express": "^4.18.2",
20
- "express-fileupload": "^1.4.3",
21
- "handlebars": "^4.7.8",
22
- "i": "^0.3.7",
23
- "joi": "^17.12.1",
24
- "joi-to-swagger": "^6.2.0",
25
- "lodash": "^4.17.21",
26
- "mongodb": "^6.3.0",
27
- "nodemon": "^3.0.3",
28
- "npm": "^10.9.1",
29
- "swagger-ui-express": "^5.0.0",
30
- "tango-api-schema": "^2.1.89",
31
- "tango-app-api-middleware": "^3.1.43-alpha.10",
32
- "winston": "^3.11.0",
33
- "winston-daily-rotate-file": "^5.0.0"
34
- },
35
- "devDependencies": {
36
- "eslint": "^8.56.0",
37
- "eslint-config-google": "^0.14.0",
38
- "eslint-config-semistandard": "^17.0.0",
39
- "eslint-config-standard": "^17.1.0",
40
- "eslint-plugin-import": "^2.29.1",
41
- "eslint-plugin-promise": "^6.1.1"
42
- }
43
- }
1
+ {
2
+ "name": "tango-app-api-client",
3
+ "version": "3.3.3-beta.10",
4
+ "description": "client",
5
+ "main": "index.js",
6
+ "type": "module",
7
+ "scripts": {
8
+ "start": "nodemon --exec \"eslint --fix . && node index.js\""
9
+ },
10
+ "engines": {
11
+ "node": ">=18.10.0"
12
+ },
13
+ "author": "praveenraj",
14
+ "license": "ISC",
15
+ "dependencies": {
16
+ "aws-sdk": "^2.1560.0",
17
+ "cors": "^2.8.5",
18
+ "dotenv": "^16.4.4",
19
+ "express": "^4.18.2",
20
+ "express-fileupload": "^1.4.3",
21
+ "handlebars": "^4.7.8",
22
+ "joi": "^17.12.1",
23
+ "joi-to-swagger": "^6.2.0",
24
+ "lodash": "^4.17.21",
25
+ "mongodb": "^6.7.0",
26
+ "nodemon": "^3.0.3",
27
+ "npm": "^10.9.1",
28
+ "swagger-ui-express": "^5.0.0",
29
+ "tango-api-schema": "^2.2.59",
30
+ "tango-app-api-middleware": "^3.1.60",
31
+ "winston": "^3.11.0",
32
+ "winston-daily-rotate-file": "^5.0.0"
33
+ },
34
+ "devDependencies": {
35
+ "eslint": "^8.56.0",
36
+ "eslint-config-google": "^0.14.0",
37
+ "eslint-config-semistandard": "^17.0.0",
38
+ "eslint-config-standard": "^17.1.0",
39
+ "eslint-plugin-import": "^2.29.1",
40
+ "eslint-plugin-promise": "^6.1.1"
41
+ }
42
+ }
@@ -1,5 +1,5 @@
1
1
  import { billingDetailsUpdate, brandInfoUpdate, domainDetailsConfigurationUpdate, featureConfigurationUpdate, getClientData, signatoryDetailsUpdate, ticketConfigurationUpdate, documentsUpdate, getUserData, CsmUsersGet, OpsUsersGet, userConfigurationUpdate, findClient, aggregateClient, createAuditQueue, findOne, insert, update, findOneClient, updateOneClient } from '../service/client.service.js';
2
- import { checkFileExist, fileUpload, signedUrl, chunkArray, download, logger, getOpenSearchData, insertOpenSearchData, sendEmailWithSES, createCustomer, createVirtualAccount } from 'tango-app-api-middleware';
2
+ import { checkFileExist, fileUpload, signedUrl, chunkArray, download, logger, getOpenSearchData, insertOpenSearchData, sendEmailWithSES, createCustomer, createVirtualAccount, getUuid } from 'tango-app-api-middleware';
3
3
  import { countDocumentsUser, findOneAndUpdateUser, findOneUser, getUserNameEmailById, updateManyUser } from '../service/user.service.js';
4
4
  import { aggregateStore, countDocumentsStore, findStore, updateManyStore } from '../service/store.service.js';
5
5
  import { aggregateCamera, countDocumentsCamera } from '../service/camera.service.js';
@@ -37,7 +37,6 @@ export async function create( req, res ) {
37
37
  ];
38
38
  const ID = await aggregateClient( countQuery );
39
39
  const count = ID[0]?.tangoId || 0;
40
- logger.info( { count: count } );
41
40
  const query = { clientName: inputData.clientName };
42
41
  const leadRecord = await findOne( query );
43
42
  const tangoId = count + 1;
@@ -71,7 +70,6 @@ export async function create( req, res ) {
71
70
  'planDetails.product': product,
72
71
  'profileDetails.website': leadRecord?.websiteUrl,
73
72
  'reportConfigs.reportName': generatedName,
74
- 'auditConfigs.queueName': generatedName,
75
73
  'auditConfigs.zoneQueueName': `${generatedName}-zone`,
76
74
  'auditConfigs.trafficQueueName': `${generatedName}-traffic`,
77
75
  'auditConfigs.traxQueueName.unattendedCustomer': `${generatedName}-unattendedCustomer`,
@@ -79,6 +77,10 @@ export async function create( req, res ) {
79
77
  'auditConfigs.traxQueueName.uniformDetection': `${generatedName}-uniformDetection`,
80
78
  'auditConfigs.traxQueueName.mobileDetection': `${generatedName}-mobileDetection`,
81
79
  'auditConfigs.traxQueueName.cameraAngleChange': `${generatedName}-cameraAngleChange`,
80
+ 'auditConfigs.traxQueueName.hygiene': `${generatedName}-hygiene`,
81
+ 'clientApi.apiKey': await getUuid(),
82
+ 'clientApi.status': true,
83
+
82
84
  };
83
85
 
84
86
  record.featureConfigs = {};
@@ -330,7 +332,6 @@ export async function create( req, res ) {
330
332
  // await deleteOneAuthentication( { user: user?._id, type: 'retail' } );
331
333
  await findOneAndUpdateUser( userQuery, userRecord );
332
334
  await Promise.all( [
333
- createAuditQueue( generatedName ),
334
335
  createAuditQueue( `${generatedName}-zone` ),
335
336
  createAuditQueue( `${generatedName}-traffic` ),
336
337
  createAuditQueue( `${generatedName}-unattendedCustomer` ),
@@ -338,7 +339,7 @@ export async function create( req, res ) {
338
339
  createAuditQueue( `${generatedName}-uniformDetection` ),
339
340
  createAuditQueue( `${generatedName}-mobileDetection` ),
340
341
  createAuditQueue( `${generatedName}-cameraAngleChange` ),
341
- ] );
342
+ createAuditQueue( `${generatedName}-hygiene` ) ] );
342
343
  return res.sendSuccess( { result: { clientId: String( tangoId ) } } );
343
344
  }
344
345
  } catch ( error ) {
@@ -395,7 +396,7 @@ export async function changeStatus( req, res, next ) {
395
396
 
396
397
  export async function getClients( req, res ) {
397
398
  try {
398
- const field = { clientName: 1, clientId: 1 };
399
+ const field = { clientName: 1, clientId: 1, traxDateRange: '$featureConfigs.traxDateRange', trafficDateRange: '$featureConfigs.trafficDateRange' };
399
400
  let result = [];
400
401
  if ( req?.user?.role === 'superadmin' ) {
401
402
  result = await findClient( {}, field );
@@ -405,18 +406,6 @@ export async function getClients( req, res ) {
405
406
  $match: {
406
407
  userEmail: req?.user?.email,
407
408
  assignedType: { $eq: 'client' },
408
- // $expr: {
409
- // $cond: {
410
- // if: {
411
- // $and: [
412
- // { $eq: [ '$userType', 'tango' ] },
413
- // { $eq: [ '$tangoUserType', 'csm' ] },
414
- // ],
415
- // },
416
- // then: { $eq: [ '$isClientApproved', true ] },
417
- // else: true,
418
- // },
419
- // },
420
409
  },
421
410
  },
422
411
  {
@@ -450,9 +439,9 @@ export async function getClients( req, res ) {
450
439
  },
451
440
  {
452
441
  $project: {
453
- _id: 0,
454
442
  clientId: 1,
455
443
  clientName: 1,
444
+ traxDateRange: '$featureConfigs.traxDateRange',
456
445
  },
457
446
  },
458
447
  ];
@@ -889,6 +878,7 @@ export async function updateSignatoryDetails( req, res ) {
889
878
  export async function updateTicketConfiguration( req, res ) {
890
879
  try {
891
880
  const openSearch = JSON.parse( process.env.OPENSEARCH );
881
+ let findClient = await findOneClient({clientId:req.params?.id})
892
882
  const updateAck = await ticketConfigurationUpdate( {
893
883
  clientId: req.params?.id, MinFilesCount: req.body?.MinFilesCount, accuracyPercentage: req.body?.accuracyPercentage, downTimeType: req.body?.downTimeType,
894
884
  infraDownTime: req.body?.infraDownTime, installationReAssign: req.body?.installationReAssign, isRcaTicketAssign: req.body?.isRcaTicketAssign,
@@ -904,6 +894,7 @@ export async function updateTicketConfiguration( req, res ) {
904
894
  }
905
895
 
906
896
  const user = await getUserNameEmailById( req.userId );
897
+ let updatedClient = await findOneClient({clientId:req.params?.id})
907
898
 
908
899
  const logObj = {
909
900
  clientId: req.params?.id,
@@ -915,6 +906,8 @@ export async function updateTicketConfiguration( req, res ) {
915
906
  changes: updateKeys,
916
907
  eventType: 'update',
917
908
  showTo: [ 'tango' ],
909
+ current:updatedClient.ticketConfigs,
910
+ previous:findClient.ticketConfigs
918
911
  };
919
912
 
920
913
  if ( updateKeys.length ) {
@@ -934,65 +927,66 @@ export async function updateFeatureConfiguration( req, res ) {
934
927
  try {
935
928
  const openSearch = JSON.parse( process.env.OPENSEARCH );
936
929
  const url = JSON.parse( process.env.URL );
937
- const updateAck = await featureConfigurationUpdate( {
938
- clientId: req.params?.id, billableCalculation: req.body?.billableCalculation, bouncedLimitCondition: req.body?.bouncedLimitCondition, bouncedLimitValue: req.body?.bouncedLimitValue,
939
- close: req.body?.close, conversionCalculation: req.body?.conversionCalculation, conversionCondition: req.body?.conversionCondition,
940
- conversionValue: req.body?.conversionValue, infraAlertCondition: req.body?.infraAlertCondition, infraAlertValue: req.body?.infraAlertValue, isFootfallDirectory: req.body?.isFootfallDirectory,
941
- isNormalized: req.body?.isNormalized, isPasserByData: req.body?.isPasserByData, missedOpportunityCalculation: req.body?.missedOpportunityCalculation, open: req.body?.open,
942
- isExcludedArea: req.body?.isExcludedArea, isCameraDisabled: req.body?.isCameraDisabled, isbillingDisabled: req.body?.isbillingDisabled, isNewDashboard: req.body?.isNewDashboard,
943
- isFootfallAuditStores: req.body?.isFootfallAuditStores, isNewTraffic: req.body?.isNewTraffic, isNewZone: req.body?.isNewZone, isNewReports: req.body?.isNewReports, isNOB: req.body?.isNOB, isNewZoneV2: req.body?.isNewZoneV2, isTrax: req.body?.isTrax,
944
- } );
930
+ const inputData = req.body;
931
+ if ( inputData?.bouncedLimitValue ) {
932
+ inputData.missedOpportunityFromValue = inputData?.bouncedLimitValue;
933
+ }
934
+
935
+ if ( inputData?.conversionValue ) {
936
+ inputData.missedOpportunityToValue = inputData?.conversionValue;
937
+ }
938
+ const updateAck = await featureConfigurationUpdate( { clientId: req.params?.id }, inputData );
945
939
 
946
940
  let updateKeys = [];
947
941
 
948
- if ( Object.keys( req.body ).length > 0 ) {
949
- Object.keys( req.body ).forEach( ( element ) => {
942
+ if ( Object.keys( inputData ).length > 0 ) {
943
+ Object.keys( inputData ).forEach( ( element ) => {
950
944
  updateKeys.push( camelCaseToWords( element ) );
951
945
  } );
952
946
  }
953
947
 
954
- const user = await getUserNameEmailById( req.userId );
948
+ const user = req.user;
955
949
 
956
950
  const { data } = await getApi( `${url.oldapidomain}/oldBrandGet/${req.params?.id}` );
957
951
 
958
- if ( req.body?.open ) {
959
- data.brandConfigs.storeOpenTime = req.body?.open;
952
+ if ( inputData?.open ) {
953
+ data.brandConfigs.storeOpenTime = inputData?.open;
960
954
  }
961
955
 
962
- if ( req.body?.close ) {
963
- data.brandConfigs.storeCloseTime = req.body?.open;
956
+ if ( inputData?.close ) {
957
+ data.brandConfigs.storeCloseTime = inputData?.open;
964
958
  }
965
959
 
966
- if ( req.body?.missedOpportunityCalculation ) {
967
- data.brandConfigs.missedOpportunityCalculate = req.body?.missedOpportunityCalculation;
960
+ if ( inputData?.missedOpportunityCalculation ) {
961
+ data.brandConfigs.missedOpportunityCalculate = inputData?.missedOpportunityCalculation;
968
962
  }
969
963
 
970
- if ( req.body?.conversionCalculation ) {
971
- data.brandConfigs.conversionCalculate = req.body?.conversionCalculation;
964
+ if ( inputData?.conversionCalculation ) {
965
+ data.brandConfigs.conversionCalculate = inputData?.conversionCalculation;
972
966
  }
973
967
 
974
- if ( req.body?.billableCalculation ) {
975
- data.brandConfigs.billableCalculate = req.body?.billableCalculation;
968
+ if ( inputData?.billableCalculation ) {
969
+ data.brandConfigs.billableCalculate = inputData?.billableCalculation;
976
970
  }
977
971
 
978
- if ( req.body?.bouncedLimitCondition ) {
979
- data.brandConfigs.bouncedConfigTime = data.brandConfigs.bouncedConfigTime.replace( /^.+(?=\d)/, req.body?.bouncedLimitCondition );
980
- data.brandConfigs.missedOpportunityEndTime = data.brandConfigs.missedOpportunityEndTime.replace( /^.+(?=\d)/, req.body?.bouncedLimitCondition );
972
+ if ( inputData?.bouncedLimitCondition ) {
973
+ data.brandConfigs.bouncedConfigTime = data.brandConfigs.bouncedConfigTime.replace( /^.+(?=\d)/, inputData?.bouncedLimitCondition );
974
+ data.brandConfigs.missedOpportunityEndTime = data.brandConfigs.missedOpportunityEndTime.replace( /^.+(?=\d)/, inputData?.bouncedLimitCondition );
981
975
  }
982
976
 
983
- if ( req.body?.bouncedLimitValue ) {
984
- data.brandConfigs.bouncedConfigTime = data.brandConfigs.bouncedConfigTime.replace( /\d+/, req.body?.bouncedLimitValue );
985
- data.brandConfigs.missedOpportunityStartTime = data.brandConfigs.missedOpportunityStartTime.replace( /\d+/, req.body?.bouncedLimitValue );
977
+ if ( inputData?.bouncedLimitValue ) {
978
+ data.brandConfigs.bouncedConfigTime = data.brandConfigs.bouncedConfigTime.replace( /\d+/, inputData?.bouncedLimitValue );
979
+ data.brandConfigs.missedOpportunityStartTime = data.brandConfigs.missedOpportunityStartTime.replace( /\d+/, inputData?.bouncedLimitValue );
986
980
  }
987
981
 
988
- if ( req.body?.conversionCondition ) {
989
- data.brandConfigs.conversionConfigTime = data.brandConfigs.conversionConfigTime.replace( /^.+(?=\d)/, req.body?.conversionCondition );
990
- data.brandConfigs.missedOpportunityStartTime = data.brandConfigs.missedOpportunityStartTime.replace( /^.+(?=\d)/, req.body?.conversionCondition );
982
+ if ( inputData?.conversionCondition ) {
983
+ data.brandConfigs.conversionConfigTime = data.brandConfigs.conversionConfigTime.replace( /^.+(?=\d)/, inputData?.conversionCondition );
984
+ data.brandConfigs.missedOpportunityStartTime = data.brandConfigs.missedOpportunityStartTime.replace( /^.+(?=\d)/, inputData?.conversionCondition );
991
985
  }
992
986
 
993
- if ( req.body?.conversionValue ) {
994
- data.brandConfigs.conversionConfigTime = data.brandConfigs.conversionConfigTime.replace( /\d+/, req.body?.conversionValue );
995
- data.brandConfigs.missedOpportunityEndTime = data.brandConfigs.missedOpportunityEndTime.replace( /\d+/, req.body?.conversionValue );
987
+ if ( inputData?.conversionValue ) {
988
+ data.brandConfigs.conversionConfigTime = data.brandConfigs.conversionConfigTime.replace( /\d+/, inputData?.conversionValue );
989
+ data.brandConfigs.missedOpportunityEndTime = data.brandConfigs.missedOpportunityEndTime.replace( /\d+/, inputData?.conversionValue );
996
990
  }
997
991
 
998
992
  await postApi( `${url.oldapidomain}/oldBrandUpdate/${data?._id}`, { brandConfigs: data.brandConfigs } );
@@ -1017,8 +1011,9 @@ export async function updateFeatureConfiguration( req, res ) {
1017
1011
  res.sendSuccess( { result: 'Updated Successfully' } );
1018
1012
  }
1019
1013
  } catch ( error ) {
1020
- logger.error( { error: error, message: req.params, function: 'updateTicketConfiguration' } );
1021
- return res.sendError( 'Internal Server Error', 500 );
1014
+ const err = error.message || 'Internal Server Error';
1015
+ logger.error( { error: error, message: req.params, function: 'updateFeatureConfiguration' } );
1016
+ return res.sendError( err, 500 );
1022
1017
  }
1023
1018
  }
1024
1019
 
@@ -2024,7 +2019,7 @@ export async function getActivityLogs( req, res ) {
2024
2019
  const query = {
2025
2020
  '_source': [
2026
2021
  'userId', 'userName', 'email', 'date', 'logType', 'logSubType',
2027
- 'changes', 'eventType',
2022
+ 'changes', 'eventType', 'previous', 'current',
2028
2023
  ],
2029
2024
  'query': {
2030
2025
  'bool': {
@@ -2081,9 +2076,24 @@ export async function getActivityLogs( req, res ) {
2081
2076
 
2082
2077
  const hits = logs?.body?.hits?.hits;
2083
2078
  const totalDocuments = logs?.body?.hits?.total?.value;
2079
+ let temp = [];
2084
2080
  if ( totalDocuments ) {
2085
- const dataArray = hits.map( ( hit ) => hit._source );
2086
- res.sendSuccess( { data: dataArray, count: totalDocuments } );
2081
+ hits.map( ( hit, i ) => {
2082
+ let respo ={};
2083
+
2084
+ if ( ( ( hit?._source?.eventType ).match( /update/ ) || ( hit?._source?.eventType ).match( /edit/ ) )&& hit?._source?.previous ) {
2085
+ const previous = hit?._source?.previous;
2086
+ const current=hit?._source?.current;
2087
+ const logType =hit?._source?.logType;
2088
+ respo = findDifferences( previous, current, logType );
2089
+ hit._source.updatedValue = respo;
2090
+ temp.push( hit?._source );
2091
+ } else {
2092
+ temp.push( hit?._source );
2093
+ }
2094
+ },
2095
+ );
2096
+ res.sendSuccess( { data: temp, count: totalDocuments } );
2087
2097
  } else {
2088
2098
  res.sendError( 'No data found', 204 );
2089
2099
  }
@@ -2094,6 +2104,112 @@ export async function getActivityLogs( req, res ) {
2094
2104
  }
2095
2105
 
2096
2106
 
2107
+ function findDifferences( previous, current, logType, path = '' ) {
2108
+ const dbKeys = JSON.parse( process.env.DB_KEYS );
2109
+ const ignoredKeys = new Set( [
2110
+ '_id', 'updatedAt', 'createdAt', 'password', 'clientId',
2111
+ 'storeId', 'refreshToken', 'employeeId', 'fcmToken', 'permission',
2112
+ ] );
2113
+
2114
+ // Get correct key mapping based on logType
2115
+ const keyMapping = dbKeys.DOCUMENTS[logType] || {};
2116
+
2117
+ let differences = {};
2118
+
2119
+ for ( const key in current ) {
2120
+ if ( !Object.prototype.hasOwnProperty.call( current, key ) || ignoredKeys.has( key ) ) continue;
2121
+
2122
+ const prevValue = previous[key];
2123
+ const currValue = current[key];
2124
+
2125
+ if ( typeof prevValue === 'object' && typeof currValue === 'object' && prevValue !== null && currValue !== null ) {
2126
+ const nestedDiffs = findDifferences( prevValue, currValue, logType, `${path}${key}.` );
2127
+ Object.assign( differences, nestedDiffs );
2128
+ } else if ( prevValue !== currValue ) {
2129
+ if (
2130
+ ( prevValue === '' && currValue === '' ) ||
2131
+ ( Array.isArray( prevValue ) && Array.isArray( currValue ) && prevValue.length === 0 && currValue.length === 0 )
2132
+ ) {
2133
+ continue;
2134
+ }
2135
+ differences[`${path}${key}`] = { previous: prevValue, current: currValue };
2136
+ }
2137
+ }
2138
+
2139
+ // **Transform & Filter Differences**
2140
+ let updatedDifferences = {};
2141
+ Object.keys( differences ).forEach( ( key ) => {
2142
+ let newKey = key.replace( /spocDetails\.\d+\./, 'spocDetails.' );
2143
+ let diff = differences[key];
2144
+
2145
+ if ( newKey.toLowerCase().includes( 'isactive' ) ) {
2146
+ diff = {
2147
+ previous: diff.previous ? 'Active' : 'Deactive',
2148
+ current: diff.current ? 'Active' : 'Deactive',
2149
+ };
2150
+ }
2151
+
2152
+ const userFriendlyKey = getUserFriendlyKey( newKey, keyMapping );
2153
+ if ( userFriendlyKey ) {
2154
+ newKey = userFriendlyKey;
2155
+ }
2156
+
2157
+ if ( newKey === 'Infra Email Alert' ) {
2158
+ diff.previous = diff.previous ? 'Enabled' : 'Disabled';
2159
+ diff.current = diff.current ? 'Enabled' : 'Disabled';
2160
+ }
2161
+
2162
+ // **Transform Roles Permission Keys**
2163
+ const rolesPermissionMatch = newKey.match( /rolespermission\.(\d+)\.modules\.(\d+)\.(isAdd|isEdit)/ );
2164
+ if ( rolesPermissionMatch ) {
2165
+ const [ , roleIndex, moduleIndex, action ] = rolesPermissionMatch;
2166
+ const role = previous.rolespermission?.[roleIndex];
2167
+ const module = role?.modules?.[moduleIndex];
2168
+
2169
+ if ( module ) {
2170
+ newKey = `User's ${module.name} Module ${action === 'isAdd' ? 'Add' : 'Edit'}`;
2171
+ diff.previous = diff.previous ? 'Enabled' : 'Disabled';
2172
+ diff.current = diff.current ? 'Enabled' : 'Disabled';
2173
+ }
2174
+ }
2175
+
2176
+ if ( !newKey || ( diff.previous === '' && !diff.current ) ) return;
2177
+
2178
+ if (
2179
+ ( diff.previous === '' || diff.previous === null || ( Array.isArray( diff.previous ) && diff.previous.length === 0 ) ) &&
2180
+ diff.current === undefined
2181
+ ) {
2182
+ return;
2183
+ }
2184
+ if (
2185
+ ( diff.current === '' || diff.current === null || ( Array.isArray( diff.current ) && diff.current.length === 0 ) ) &&
2186
+ diff.previous === undefined
2187
+ ) {
2188
+ return;
2189
+ }
2190
+
2191
+ updatedDifferences[newKey] = diff;
2192
+ } );
2193
+
2194
+ return updatedDifferences;
2195
+ }
2196
+
2197
+
2198
+ function getUserFriendlyKey( keyPath, mapping ) {
2199
+ const keys = keyPath.split( '.' ); // Split the key path (e.g., storeProfile.pincode → ['storeProfile', 'pincode'])
2200
+ let value = mapping;
2201
+
2202
+ for ( const key of keys ) {
2203
+ if ( value && typeof value === 'object' && key in value ) {
2204
+ value = value[key]; // Traverse down the object
2205
+ } else {
2206
+ return null; // Return original if not found
2207
+ }
2208
+ }
2209
+
2210
+ return typeof value === 'string' ? value : keyPath; // Return mapped value or original key
2211
+ }
2212
+
2097
2213
  async function postApi( url, data ) {
2098
2214
  const requestOptions = {
2099
2215
  method: 'POST',
@@ -120,6 +120,10 @@ export const featureConfigurationSchemaBody = joi.object(
120
120
  isNOB: joi.boolean().optional(),
121
121
  isNewZoneV2: joi.boolean().optional(),
122
122
  isTrax: joi.boolean().optional(),
123
+ isControlCenter: joi.boolean().optional(),
124
+ isFootfallDirectoryAudit: joi.boolean().optional(),
125
+ isFootfallDirectoryLimit: joi.boolean().optional(),
126
+ streamBy: joi.string().optional(),
123
127
  },
124
128
  );
125
129
 
@@ -1,171 +1,171 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8">
5
- <meta http-equiv="x-ua-compatible" content="ie=edge">
6
- <title>Your account was approved!</title>
7
- <meta name="viewport" content="width=device-width, initial-scale=1">
8
- <style type="text/css">
9
- @media screen {
10
- @font-face {
11
- font-family: 'Inter';
12
- font-style: normal;
13
- font-weight: 400;
14
- font-display: swap;
15
- src: local("Inter"), local("Inter-Regular"), url(https://fonts.gstatic.com/s/inter/v12/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZJhiI2B.woff2) format('woff2');
16
- unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
17
- }
18
- }
19
-
20
- body {
21
- font-family: "Inter", sans-serif !important;
22
- }
23
-
24
- body,
25
- table,
26
- td,
27
- a {
28
- -ms-text-size-adjust: 100%;
29
- -webkit-text-size-adjust: 100%;
30
- }
31
-
32
- table,
33
- td {
34
- mso-table-rspace: 0pt;
35
- mso-table-lspace: 0pt;
36
- }
37
-
38
- img {
39
- -ms-interpolation-mode: bicubic;
40
- }
41
-
42
- a[x-apple-data-detectors] {
43
- font-family: "inherit" !important;
44
- font-size: inherit !important;
45
- font-weight: inherit !important;
46
- line-height: inherit !important;
47
- color: inherit !important;
48
- text-decoration: none !important;
49
- }
50
-
51
- div[style*="margin: 16px 0;"] {
52
- margin: 0 !important;
53
- }
54
-
55
- body {
56
- width: 100% !important;
57
- height: 100% !important;
58
- padding: 0 !important;
59
- margin: 0 !important;
60
- }
61
-
62
- table {
63
- border-collapse: collapse !important;
64
- }
65
-
66
- a {
67
- color: #1a82e2;
68
- }
69
-
70
- img {
71
- height: auto;
72
- line-height: 100%;
73
- text-decoration: none;
74
- border: 0;
75
- outline: none;
76
- }
77
- </style>
78
- </head>
79
- <body style="background-color: #dbe5ea;">
80
- <div class="preheader" style="display: none; max-width: 0; max-height: 0; overflow: hidden; font-size: 1px; line-height: 1px; color: #fff; opacity: 0;"> Email Summary (Hidden) </div>
81
- <table border="0" cellpadding="0" cellspacing="0" width="100%" style="padding-left:10px;padding-right:10px">
82
- <tr>
83
- <td bgcolor="#dbe5ea" style="padding:32px 10px 0 10px">
84
- <table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px;" align="center">
85
- <tr>
86
- <td class="o_bg-white o_px-md o_py-md o_sans o_text" style="margin-top: 0px;margin-bottom: 0px;font-size: 16px;line-height: 24px;background-color: #ffffff;padding-left: 18px;padding-right: 24px;padding-top: 24px;padding-bottom: 24px;">
87
- <p style="margin-top: 0px;margin-bottom: 0px;">
88
- <a class="o_text-white" href="https://tangoeye.ai/" style="text-decoration: none;outline: none;color: #ffffff;">
89
- <img src={{logo}} width="200" height="100" alt="SimpleApp" style="-ms-interpolation-mode: bicubic;vertical-align: middle;border: 0;line-height: 100%;height: auto;outline: none;text-decoration: none;">
90
- </a>
91
- </p>
92
- </td>
93
- </tr>
94
- <tr>
95
- <td align="left" bgcolor="#ffffff" style="padding-left: 30px;padding-right: 24px; font-size: 14px; line-height: 24px;">
96
- <p class="o_heading o_mb-xxs" style="width: 544px;height: 0px;border: 1px solid #CBD5E1;flex: none;order: 1;flex-grow: 0;"></p>
97
- </td>
98
- </tr>
99
- </table>
100
- </td>
101
- </tr>
102
- <tr>
103
- <td align="center" bgcolor="#dbe5ea" style="padding:0 10px 0 10px">
104
- <table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px;">
105
- <tr>
106
- <td class="o_bg-white o_px-md o_py-xl o_xs-py-md" style="background-color: #ffffff;padding-left: 30px;padding-right: 24px;padding-top: 10px;padding-bottom: 10px;">
107
- <div class="o_col-6s o_sans o_text-md o_text-light o_center" style="margin-top: 0px;margin-bottom: 0px;font-size: 20px;line-height: 28px;color: #82899a;">
108
- <span class="o_heading o_text-dark o_mb-xxs" style="font-weight: 700;margin-top: 0px;margin-bottom: 4px;color: #242b3d;line-height: 39px;"> Your account was approved!</span>
109
- </div>
110
- </td>
111
- </tr>
112
- </table>
113
- </td>
114
- </tr>
115
- <tr>
116
- <td align="center" bgcolor="#dbe5ea" style="padding:0 10px 0 10px">
117
- <table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px;">
118
- <tr>
119
- <td align="left" bgcolor="#ffffff" style="padding-left: 30px;padding-right: 24px; font-size: 16px;padding-top:10px; line-height: 24px;font-weight:400;color:#384860">
120
- <p style="margin: 0;">As a verified user, you will be among the first to receive product updates, new feature announcements, enhanced security and personalised support.
121
- <br><br>
122
- Head over to the dashboard to finish your setup. </p>
123
- </td>
124
- </tr>
125
- </table>
126
- </td>
127
- </tr>
128
- <tr>
129
- <td align="center" bgcolor="#dbe5ea" style="padding:0 10px 0 10px">
130
- <table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px;">
131
- <tr>
132
- <td align="left" bgcolor="#fff" style="border-radius: 6px;padding:14px 30px">
133
- <a href={{url}} style="display: inline-block;padding: 10px 36px;font-size: 20px;color: #fff;text-decoration: none;border-radius: 6px;background-color: #00a3ff;"> Add Stores</a>
134
- </td>
135
- </tr>
136
- </table>
137
- </td>
138
- </tr>
139
- <tr>
140
- <td align="center" bgcolor="#dbe5ea" style="padding:0 10px 0 10px">
141
- <table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px;">
142
- <tr>
143
- <td align="left" bgcolor="#ffffff" style="padding-left: 30px;padding-right: 24px; font-size: 16px;padding-top:10px; line-height: 24px;font-weight:400;color:#384860">
144
- <p style="margin: 0;">Enjoy the full potential of Tango Traffic Module. If you have any questions or need assistance, our support team is here to help.
145
-
146
- <br><br>
147
-
148
- Happy exploring! </p>
149
- </td>
150
- </tr>
151
- </table>
152
- </td>
153
- </tr>
154
- <tr>
155
- <td align="center" bgcolor="#dbe5ea" style="padding:0 10px 32px 10px">
156
- <table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px;">
157
- <tr>
158
- <td class="o_bg-white o_px-md o_py-xl o_xs-py-md" style="background-color: #ffffff;padding-left: 30px;padding-right: 24px;padding-bottom:15px">
159
- <div class="o_col-6s o_sans o_text-md o_text-light o_center" style="margin-top: 0px;margin-bottom: 0px;font-size: 12px;color: #202B3C;font-style: normal;font-weight: 400;font-size: 12px;line-height: 150%;">
160
- <br>
161
- <p>If you'd rather not receive this kind of email, Don’t want any more emails from TangoEye?<u style="color:#00A3FF">Unsubscribe</u>.</p>
162
- <p> © Tango Eye. All rights reserved.</p>
163
- </div>
164
- </td>
165
- </tr>
166
- </table>
167
- </td>
168
- </tr>
169
- </table>
170
- </body>
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta http-equiv="x-ua-compatible" content="ie=edge">
6
+ <title>Your account was approved!</title>
7
+ <meta name="viewport" content="width=device-width, initial-scale=1">
8
+ <style type="text/css">
9
+ @media screen {
10
+ @font-face {
11
+ font-family: 'Inter';
12
+ font-style: normal;
13
+ font-weight: 400;
14
+ font-display: swap;
15
+ src: local("Inter"), local("Inter-Regular"), url(https://fonts.gstatic.com/s/inter/v12/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZJhiI2B.woff2) format('woff2');
16
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
17
+ }
18
+ }
19
+
20
+ body {
21
+ font-family: "Inter", sans-serif !important;
22
+ }
23
+
24
+ body,
25
+ table,
26
+ td,
27
+ a {
28
+ -ms-text-size-adjust: 100%;
29
+ -webkit-text-size-adjust: 100%;
30
+ }
31
+
32
+ table,
33
+ td {
34
+ mso-table-rspace: 0pt;
35
+ mso-table-lspace: 0pt;
36
+ }
37
+
38
+ img {
39
+ -ms-interpolation-mode: bicubic;
40
+ }
41
+
42
+ a[x-apple-data-detectors] {
43
+ font-family: "inherit" !important;
44
+ font-size: inherit !important;
45
+ font-weight: inherit !important;
46
+ line-height: inherit !important;
47
+ color: inherit !important;
48
+ text-decoration: none !important;
49
+ }
50
+
51
+ div[style*="margin: 16px 0;"] {
52
+ margin: 0 !important;
53
+ }
54
+
55
+ body {
56
+ width: 100% !important;
57
+ height: 100% !important;
58
+ padding: 0 !important;
59
+ margin: 0 !important;
60
+ }
61
+
62
+ table {
63
+ border-collapse: collapse !important;
64
+ }
65
+
66
+ a {
67
+ color: #1a82e2;
68
+ }
69
+
70
+ img {
71
+ height: auto;
72
+ line-height: 100%;
73
+ text-decoration: none;
74
+ border: 0;
75
+ outline: none;
76
+ }
77
+ </style>
78
+ </head>
79
+ <body style="background-color: #dbe5ea;">
80
+ <div class="preheader" style="display: none; max-width: 0; max-height: 0; overflow: hidden; font-size: 1px; line-height: 1px; color: #fff; opacity: 0;"> Email Summary (Hidden) </div>
81
+ <table border="0" cellpadding="0" cellspacing="0" width="100%" style="padding-left:10px;padding-right:10px">
82
+ <tr>
83
+ <td bgcolor="#dbe5ea" style="padding:32px 10px 0 10px">
84
+ <table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px;" align="center">
85
+ <tr>
86
+ <td class="o_bg-white o_px-md o_py-md o_sans o_text" style="margin-top: 0px;margin-bottom: 0px;font-size: 16px;line-height: 24px;background-color: #ffffff;padding-left: 18px;padding-right: 24px;padding-top: 24px;padding-bottom: 24px;">
87
+ <p style="margin-top: 0px;margin-bottom: 0px;">
88
+ <a class="o_text-white" href="https://tangoeye.ai/" style="text-decoration: none;outline: none;color: #ffffff;">
89
+ <img src={{logo}} width="200" height="100" alt="SimpleApp" style="-ms-interpolation-mode: bicubic;vertical-align: middle;border: 0;line-height: 100%;height: auto;outline: none;text-decoration: none;">
90
+ </a>
91
+ </p>
92
+ </td>
93
+ </tr>
94
+ <tr>
95
+ <td align="left" bgcolor="#ffffff" style="padding-left: 30px;padding-right: 24px; font-size: 14px; line-height: 24px;">
96
+ <p class="o_heading o_mb-xxs" style="width: 544px;height: 0px;border: 1px solid #CBD5E1;flex: none;order: 1;flex-grow: 0;"></p>
97
+ </td>
98
+ </tr>
99
+ </table>
100
+ </td>
101
+ </tr>
102
+ <tr>
103
+ <td align="center" bgcolor="#dbe5ea" style="padding:0 10px 0 10px">
104
+ <table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px;">
105
+ <tr>
106
+ <td class="o_bg-white o_px-md o_py-xl o_xs-py-md" style="background-color: #ffffff;padding-left: 30px;padding-right: 24px;padding-top: 10px;padding-bottom: 10px;">
107
+ <div class="o_col-6s o_sans o_text-md o_text-light o_center" style="margin-top: 0px;margin-bottom: 0px;font-size: 20px;line-height: 28px;color: #82899a;">
108
+ <span class="o_heading o_text-dark o_mb-xxs" style="font-weight: 700;margin-top: 0px;margin-bottom: 4px;color: #242b3d;line-height: 39px;"> Your account was approved!</span>
109
+ </div>
110
+ </td>
111
+ </tr>
112
+ </table>
113
+ </td>
114
+ </tr>
115
+ <tr>
116
+ <td align="center" bgcolor="#dbe5ea" style="padding:0 10px 0 10px">
117
+ <table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px;">
118
+ <tr>
119
+ <td align="left" bgcolor="#ffffff" style="padding-left: 30px;padding-right: 24px; font-size: 16px;padding-top:10px; line-height: 24px;font-weight:400;color:#384860">
120
+ <p style="margin: 0;">As a verified user, you will be among the first to receive product updates, new feature announcements, enhanced security and personalised support.
121
+ <br><br>
122
+ Head over to the dashboard to finish your setup. </p>
123
+ </td>
124
+ </tr>
125
+ </table>
126
+ </td>
127
+ </tr>
128
+ <tr>
129
+ <td align="center" bgcolor="#dbe5ea" style="padding:0 10px 0 10px">
130
+ <table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px;">
131
+ <tr>
132
+ <td align="left" bgcolor="#fff" style="border-radius: 6px;padding:14px 30px">
133
+ <a href={{url}} style="display: inline-block;padding: 10px 36px;font-size: 20px;color: #fff;text-decoration: none;border-radius: 6px;background-color: #00a3ff;"> Add Stores</a>
134
+ </td>
135
+ </tr>
136
+ </table>
137
+ </td>
138
+ </tr>
139
+ <tr>
140
+ <td align="center" bgcolor="#dbe5ea" style="padding:0 10px 0 10px">
141
+ <table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px;">
142
+ <tr>
143
+ <td align="left" bgcolor="#ffffff" style="padding-left: 30px;padding-right: 24px; font-size: 16px;padding-top:10px; line-height: 24px;font-weight:400;color:#384860">
144
+ <p style="margin: 0;">Enjoy the full potential of Tango Traffic Module. If you have any questions or need assistance, our support team is here to help.
145
+
146
+ <br><br>
147
+
148
+ Happy exploring! </p>
149
+ </td>
150
+ </tr>
151
+ </table>
152
+ </td>
153
+ </tr>
154
+ <tr>
155
+ <td align="center" bgcolor="#dbe5ea" style="padding:0 10px 32px 10px">
156
+ <table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px;">
157
+ <tr>
158
+ <td class="o_bg-white o_px-md o_py-xl o_xs-py-md" style="background-color: #ffffff;padding-left: 30px;padding-right: 24px;padding-bottom:15px">
159
+ <div class="o_col-6s o_sans o_text-md o_text-light o_center" style="margin-top: 0px;margin-bottom: 0px;font-size: 12px;color: #202B3C;font-style: normal;font-weight: 400;font-size: 12px;line-height: 150%;">
160
+ <br>
161
+ <p>If you'd rather not receive this kind of email, Don’t want any more emails from TangoEye?<u style="color:#00A3FF">Unsubscribe</u>.</p>
162
+ <p> © Tango Eye. All rights reserved.</p>
163
+ </div>
164
+ </td>
165
+ </tr>
166
+ </table>
167
+ </td>
168
+ </tr>
169
+ </table>
170
+ </body>
171
171
  </html> `
@@ -128,43 +128,42 @@ export function ticketConfigurationUpdate( {
128
128
  } );
129
129
  }
130
130
 
131
- export function featureConfigurationUpdate( {
132
- clientId, billableCalculation, bouncedLimitCondition, bouncedLimitValue,
133
- close, conversionCalculation, conversionCondition,
134
- conversionValue, infraAlertCondition, infraAlertValue, isFootfallDirectory,
135
- isNormalized, isPasserByData, missedOpportunityCalculation, open, isExcludedArea,
136
- isCameraDisabled, isbillingDisabled, isNewDashboard, isFootfallAuditStores, isNewTraffic,
137
- isNewZone, isNewReports, isNOB, isNewZoneV2, isTrax,
138
- } ) {
139
- return clientModel.updateOne( { clientId: clientId },
131
+ export function featureConfigurationUpdate( query, inputData ) {
132
+ return clientModel.updateOne( query,
140
133
  {
141
134
  $set: {
142
- 'featureConfigs.open': open,
143
- 'featureConfigs.close': close,
144
- 'featureConfigs.infraAlert.condition': infraAlertCondition,
145
- 'featureConfigs.infraAlert.value': infraAlertValue,
146
- 'featureConfigs.bouncedLimit.condition': bouncedLimitCondition,
147
- 'featureConfigs.bouncedLimit.value': bouncedLimitValue,
148
- 'featureConfigs.conversion.condition': conversionCondition,
149
- 'featureConfigs.conversion.value': conversionValue,
150
- 'featureConfigs.billableCalculation': billableCalculation,
151
- 'featureConfigs.missedOpportunityCalculation': missedOpportunityCalculation,
152
- 'featureConfigs.conversionCalculation': conversionCalculation,
153
- 'featureConfigs.isNormalized': isNormalized,
154
- 'featureConfigs.isPasserByData': isPasserByData,
155
- 'featureConfigs.isFootfallDirectory': isFootfallDirectory,
156
- 'featureConfigs.isExcludedArea': isExcludedArea,
135
+ 'featureConfigs.open': inputData?.open,
136
+ 'featureConfigs.close': inputData?.close,
137
+ 'featureConfigs.infraAlert.condition': inputData?.infraAlertCondition,
138
+ 'featureConfigs.infraAlert.value': inputData?.infraAlertValue,
139
+ 'featureConfigs.bouncedLimit.condition': inputData?.bouncedLimitCondition,
140
+ 'featureConfigs.bouncedLimit.value': inputData?.bouncedLimitValue,
141
+ 'featureConfigs.conversion.condition': inputData?.conversionCondition,
142
+ 'featureConfigs.conversion.value': inputData?.conversionValue,
143
+ 'featureConfigs.missedOpportunityFrom.value': inputData?.missedOpportunityFromValue,
144
+ 'featureConfigs.missedOpportunityTo.value': inputData?.missedOpportunityToValue,
145
+ 'featureConfigs.billableCalculation': inputData?.billableCalculation,
146
+ 'featureConfigs.missedOpportunityCalculation': inputData?.missedOpportunityCalculation,
147
+ 'featureConfigs.conversionCalculation': inputData?.conversionCalculation,
148
+ 'featureConfigs.isNormalized': inputData?.isNormalized,
149
+ 'featureConfigs.isPasserByData': inputData?.isPasserByData,
150
+ 'featureConfigs.isFootfallDirectory': inputData?.isFootfallDirectory,
151
+ 'featureConfigs.isExcludedArea': inputData?.isExcludedArea,
157
152
  'featureConfigs.updateFeatureConfig': false,
158
- 'featureConfigs.isCameraDisabled': isCameraDisabled,
159
- 'featureConfigs.isbillingDisabled': isbillingDisabled,
160
- 'featureConfigs.isNewDashboard': isNewDashboard,
161
- 'featureConfigs.isFootfallAuditStores': isFootfallAuditStores,
162
- 'featureConfigs.isNewTraffic': isNewTraffic,
163
- 'featureConfigs.isNewZone': isNewZone,
164
- 'featureConfigs.isNewReports': isNewReports,
165
- 'featureConfigs.isNOB': isNOB,
166
- 'featureConfigs.isNewZoneV2': isNewZoneV2,
167
- 'featureConfigs.isTrax': isTrax,
153
+ 'featureConfigs.isCameraDisabled': inputData?.isCameraDisabled,
154
+ 'featureConfigs.isbillingDisabled': inputData?.isbillingDisabled,
155
+ 'featureConfigs.isNewDashboard': inputData?.isNewDashboard,
156
+ 'featureConfigs.isFootfallAuditStores': inputData?.isFootfallAuditStores,
157
+ 'featureConfigs.isNewTraffic': inputData?.isNewTraffic,
158
+ 'featureConfigs.isNewZone': inputData?.isNewZone,
159
+ 'featureConfigs.isNewReports': inputData?.isNewReports,
160
+ 'featureConfigs.isNOB': inputData?.isNOB,
161
+ 'featureConfigs.isNewZoneV2': inputData?.isNewZoneV2,
162
+ 'featureConfigs.isTrax': inputData?.isTrax,
163
+ 'featureConfigs.isControlCenter': inputData?.isControlCenter,
164
+ 'featureConfigs.isFootfallDirectoryAudit': inputData?.isFootfallDirectoryAudit,
165
+ 'featureConfigs.isFootfallDirectoryLimit': inputData?.isFootfallDirectoryLimit,
166
+ 'featureConfigs.streamBy': inputData?.streamBy,
168
167
  },
169
168
  } );
170
169
  }