tango-app-api-client 3.3.3-beta.8 → 3.4.0-beta.0

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.8",
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.2.42",
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.4.0-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
+ "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
+ }
@@ -142,7 +142,23 @@ export async function create( req, res ) {
142
142
  'isDefaults': true,
143
143
  };
144
144
 
145
- await createclusterModel( defaultcluster );
145
+ const cluster = await createclusterModel( defaultcluster );
146
+
147
+ const logClusterObj = {
148
+ clientId: insertedClientRecord.clientId,
149
+ userName: req.user?.userName,
150
+ userId: req?.user?._id,
151
+ email: req.user?.email,
152
+ clusterId: cluster._id,
153
+ date: new Date(),
154
+ logType: 'cluster',
155
+ logSubType: 'clusterCreated',
156
+ changes: [ 'All stores' ],
157
+ eventType: 'create',
158
+ showTo: [ 'client', 'tango' ],
159
+ };
160
+
161
+ await insertOpenSearchData( openSearch.activityLog, logClusterObj );
146
162
 
147
163
 
148
164
  const defaultGroup = {
@@ -284,6 +300,7 @@ export async function create( req, res ) {
284
300
  const logObj = {
285
301
  clientId: insertedClientRecord.clientId,
286
302
  userName: req.user?.userName,
303
+ userId: req?.user?._id,
287
304
  email: req.user?.email,
288
305
  date: new Date(),
289
306
  logType: 'brandDetails',
@@ -691,7 +708,7 @@ export async function updateBrandInfo( req, res ) {
691
708
  ContentType: req.files.logo.mimetype,
692
709
  body: req.files.logo.data,
693
710
  };
694
- updateKeys.push( 'Logo' );
711
+ updateKeys.push( 'Brand Logo' );
695
712
  await fileUpload( uploadDataParams );
696
713
  }
697
714
 
@@ -701,32 +718,93 @@ export async function updateBrandInfo( req, res ) {
701
718
  } );
702
719
  }
703
720
 
704
- const user = await getUserNameEmailById( req.userId );
721
+ // Get updated client information before the update operation (fetch selected fields only)
722
+ const getPreCientInfo = await findOneClient(
723
+ { clientId: req.params.id }, // Filter by clientId
724
+ {
725
+ '_id': 0,
726
+ 'profileDetails.registeredCompanyName': 1,
727
+ 'profileDetails.industry': 1,
728
+ 'profileDetails.clientType': 1,
729
+ 'profileDetails.registeredAddress': 1,
730
+ 'profileDetails.headQuarters': 1,
731
+ 'profileDetails.website': 1,
732
+ 'status': 1,
733
+ 'averageTransactionValue': 1,
734
+ },
735
+ );
736
+
705
737
 
738
+ const updateAck = await brandInfoUpdate( {
739
+ clientId: req.params?.id, registeredCompanyName: req.body?.registeredCompanyName, industry: req.body?.industry,
740
+ clientType: req.body?.clientType, registeredAddress: req.body?.registeredAddress, headQuarters: req.body?.headQuarters,
741
+ website: req.body?.website, status: req.body?.status, logo: req.files?.logo ? `brandLogo.${req.files.logo.name.split( '.' )[1]}` : undefined, averageTransactionValue: req.body?.averageTransactionValue,
742
+ } );
706
743
 
744
+ // Get updated client information after the update operation (fetch selected fields only)
745
+ const getPosCientInfo = await findOneClient(
746
+ { clientId: req.params.id }, // Filter by clientId
747
+ {
748
+ '_id': 0,
749
+ 'profileDetails.registeredCompanyName': 1,
750
+ 'profileDetails.industry': 1,
751
+ 'profileDetails.clientType': 1,
752
+ 'profileDetails.registeredAddress': 1,
753
+ 'profileDetails.headQuarters': 1,
754
+ 'profileDetails.website': 1,
755
+ 'status': 1,
756
+ 'averageTransactionValue': 1,
757
+ },
758
+ );
759
+ // Map and rename keys from previous client info for UI display and logging
760
+ const oldData = {
761
+ RegisteredCompanyName: getPreCientInfo?.profileDetails?.registeredCompanyName,
762
+ IndustryType: getPreCientInfo?.profileDetails?.industry,
763
+ FirmType: getPreCientInfo?.profileDetails?.clientType,
764
+ RegisteredAddress: getPreCientInfo?.profileDetails?.registeredAddress,
765
+ HeadQuarters: getPreCientInfo?.profileDetails?.headQuarters,
766
+ CompanyWebsite: getPreCientInfo?.profileDetails?.website,
767
+ ProcessingStatus: getPreCientInfo?.status,
768
+ AverageTransactionValue: getPreCientInfo?.averageTransactionValue,
769
+ };
770
+
771
+ // Map and rename keys from current client info for UI display and logging
772
+ const newData ={
773
+ RegisteredCompanyName: getPosCientInfo?.profileDetails?.registeredCompanyName,
774
+ IndustryType: getPosCientInfo?.profileDetails?.industry,
775
+ FirmType: getPosCientInfo?.profileDetails?.clientType,
776
+ RegisteredAddress: getPosCientInfo?.profileDetails?.registeredAddress,
777
+ HeadQuarters: getPosCientInfo?.profileDetails?.headQuarters,
778
+ CompanyWebsite: getPosCientInfo?.profileDetails?.website,
779
+ ProcessingStatus: getPosCientInfo?.status,
780
+ AverageTransactionValue: getPosCientInfo?.averageTransactionValue,
781
+
782
+ };
783
+
784
+ // Prepare activity log object with all relevant details for OpenSearch logging
707
785
  const logObj = {
708
- clientId: req.params?.id,
709
- userName: user?.userName,
710
- email: user?.email,
711
- date: new Date(),
712
- logType: 'brandDetails',
713
- logSubType: 'brandInfo',
714
- changes: updateKeys,
715
- eventType: 'update',
716
- showTo: [ 'client', 'tango' ],
786
+ clientId: req.params?.id, // ID of the client whose data was updated
787
+ userName: req?.user?.userName, // Name of the user performing the update
788
+ email: req?.user?.email, // Email of the user performing the update
789
+ date: new Date(), // Timestamp of the update event
790
+ logType: 'brandDetails', // Type of log (e.g., related to brand details)
791
+ logSubType: 'brandInfo', // Subtype for more specific categorization
792
+ changes: updateKeys, // List of fields that were updated.these will shown in the UI as part of activity log
793
+ eventType: 'update', // Type of event (update operation)
794
+ showTo: [ 'client', 'tango' ], // Visibility of the log (who can see this log)
795
+ previous: getPreCientInfo, // Previous (old) client information before update based on DB keys (for internal reference and comparison).
796
+ current: getPosCientInfo, // Current (new) client information after update based on DB keys (for internal reference and comparison).
797
+ oldData: oldData, // Previous (old) client information before update (used for showing detailed changes in the UI).
798
+ newData: newData, // Current (new) client information after update (used for showing detailed changes in the UI).
717
799
  };
800
+ logger.info( { logObj: logObj, updateKeys: updateKeys, length: updateKeys?.length } );
718
801
 
802
+ // Insert activity log into OpenSearch if there are fields that were updated
719
803
  if ( updateKeys.length ) {
720
- await insertOpenSearchData( openSearch.activityLog, logObj );
804
+ await insertOpenSearchData( openSearch.activityLog, logObj ); // Insert activity log
721
805
  }
722
806
 
723
807
 
724
- const updateAck = await brandInfoUpdate( {
725
- clientId: req.params?.id, registeredCompanyName: req.body?.registeredCompanyName, industry: req.body?.industry,
726
- clientType: req.body?.clientType, registeredAddress: req.body?.registeredAddress, headQuarters: req.body?.headQuarters,
727
- website: req.body?.website, status: req.body?.status, logo: req.files?.logo ? `brandLogo.${req.files.logo.name.split( '.' )[1]}` : undefined, averageTransactionValue: req.body?.averageTransactionValue,
728
- } );
729
-
730
808
  if ( req.body?.status === 'active' ) {
731
809
  await updateManyStore( { clientId: req.params?.id }, { status: 'active' } );
732
810
  await updateManyUser( { clientId: req.params?.id }, { isActive: true } );
@@ -743,7 +821,6 @@ export async function updateBrandInfo( req, res ) {
743
821
 
744
822
  if ( req.body?.status === 'deactive' ) {
745
823
  await updateManyStore( { clientId: req.params?.id }, { status: 'deactive' } );
746
- // await updateManyCamera( { clientId: req.params?.id }, { isUp: false, isActivated: false } );
747
824
  await updateManyUser( { clientId: req.params?.id }, { isActive: false } );
748
825
  }
749
826
 
@@ -769,7 +846,7 @@ export async function updateBrandInfo( req, res ) {
769
846
  await postApi( `${url.oldapidomain}/oldBrandUpdate/${data?._id}`, { clientStatus: data.clientStatus } );
770
847
 
771
848
  if ( updateAck ) {
772
- res.sendSuccess( { result: 'Updated Successfully' } );
849
+ return res.sendSuccess( { result: 'Updated Successfully' } );
773
850
  }
774
851
  } catch ( error ) {
775
852
  logger.error( { error: error, message: req.params, function: 'updateBrandInfo' } );
@@ -878,6 +955,25 @@ export async function updateSignatoryDetails( req, res ) {
878
955
  export async function updateTicketConfiguration( req, res ) {
879
956
  try {
880
957
  const openSearch = JSON.parse( process.env.OPENSEARCH );
958
+ const fields ={
959
+ 'ticketConfigs.downTimeType': 1,
960
+ 'ticketConfigs.installationReAssign': 1,
961
+ 'ticketConfigs.infraDownTime': 1,
962
+ 'ticketConfigs.MinFilesCount': 1,
963
+ 'ticketConfigs.rcaTicketAssign': 1,
964
+ 'ticketConfigs.refreshAlert': 1,
965
+ 'ticketConfigs.statusCheckAlert': 1,
966
+ 'ticketConfigs.accuracyPercentage': 1,
967
+ 'ticketConfigs.reTrain': 1,
968
+ };
969
+
970
+ // Get updated client ticket configuration before the update operation (fetch selected fields only)
971
+ let findClient = await findOneClient(
972
+ { clientId: req.params?.id }, // Filter by clientId
973
+ fields,
974
+ );
975
+
976
+ // update the requested values in Mongo DB
881
977
  const updateAck = await ticketConfigurationUpdate( {
882
978
  clientId: req.params?.id, MinFilesCount: req.body?.MinFilesCount, accuracyPercentage: req.body?.accuracyPercentage, downTimeType: req.body?.downTimeType,
883
979
  infraDownTime: req.body?.infraDownTime, installationReAssign: req.body?.installationReAssign, isRcaTicketAssign: req.body?.isRcaTicketAssign,
@@ -885,37 +981,75 @@ export async function updateTicketConfiguration( req, res ) {
885
981
  refreshAlert: req.body?.refreshAlert, sendToAdmin: req.body?.sendToAdmin, sendToUser: req.body?.sendToUser, statusCheckAlert: req.body?.statusCheckAlert,
886
982
  } );
887
983
 
888
- let updateKeys = [];
984
+ let updateKeys = []; // List of updated field names in readable format
985
+
986
+ // Check if request body has any fields to update
889
987
  if ( Object.keys( req.body ).length > 0 ) {
890
988
  Object.keys( req.body ).forEach( ( element ) => {
891
- updateKeys.push( camelCaseToWords( element ) );
989
+ updateKeys.push( camelCaseToWords( element ) ); // Convert camelCase field name to readable format and add to updateKeys
892
990
  } );
893
991
  }
894
992
 
895
- const user = await getUserNameEmailById( req.userId );
896
993
 
994
+ // Get updated client ticket configuration after the update operation (fetch selected fields only)
995
+ let updatedClient = await findOneClient( { clientId: req.params?.id }, fields );
996
+
997
+ // Map and rename keys from previous client info for UI display and logging
998
+ const oldData = {
999
+ InfraDowntimeType: findClient?.ticketConfigs?.downTimeType,
1000
+ AutoReAssignInstallation: findClient?.ticketConfigs?.installationReAssign,
1001
+ InfraDowntime: findClient?.ticketConfigs?.infraDownTime,
1002
+ InfraFiles: findClient?.ticketConfigs?.MinFilesCount,
1003
+ RcaTicket: findClient?.ticketConfigs?.rcaTicketAssign,
1004
+ RefreshTicketAlert: findClient?.ticketConfigs?.refreshAlert,
1005
+ StatusCheckAlert: findClient?.ticketConfigs?.statusCheckAlert,
1006
+ DataMismatchAccuracyPercentage: findClient?.ticketConfigs?.accuracyPercentage,
1007
+ DataMismatchReTraining: findClient?.ticketConfigs?.reTrain,
1008
+ };
1009
+
1010
+ // Map and rename keys from current client info for UI display and logging
1011
+ const newData ={
1012
+ InfraDowntimeType: updatedClient?.ticketConfigs?.downTimeType,
1013
+ AutoReAssignInstallation: updatedClient?.ticketConfigs?.installationReAssign,
1014
+ InfraDowntime: updatedClient?.ticketConfigs?.infraDownTime,
1015
+ InfraFiles: updatedClient?.ticketConfigs?.MinFilesCount,
1016
+ RcaTicket: updatedClient?.ticketConfigs?.rcaTicketAssign,
1017
+ RefreshTicketAlert: updatedClient?.ticketConfigs?.refreshAlert,
1018
+ StatusCheckAlert: updatedClient?.ticketConfigs?.statusCheckAlert,
1019
+ DataMismatchAccuracyPercentage: updatedClient?.ticketConfigs?.accuracyPercentage,
1020
+ DataMismatchReTraining: updatedClient?.ticketConfigs?.reTrain,
1021
+
1022
+ };
1023
+
1024
+ // Prepare activity log object with all relevant details for OpenSearch logging
897
1025
  const logObj = {
898
- clientId: req.params?.id,
899
- userName: user.userName,
900
- email: user.email,
901
- date: new Date(),
902
- logType: 'configuration',
903
- logSubType: 'ticketConfig',
904
- changes: updateKeys,
905
- eventType: 'update',
906
- showTo: [ 'tango' ],
1026
+ clientId: req.params?.id, // ID of the client whose data was updated
1027
+ userName: req?.user?.userName, // Name of the user performing the update
1028
+ email: req?.user?.email, // Email of the user performing the update
1029
+ date: new Date(), // Timestamp of the update event
1030
+ logType: 'configuration', // Type of log (e.g., related to brand details)
1031
+ logSubType: 'ticketConfig', // Subtype for more specific categorization
1032
+ changes: updateKeys, // List of fields that were updated.these will shown in the UI as part of activity log
1033
+ eventType: 'update', // Type of event (update operation)
1034
+ showTo: [ 'tango' ], // Visibility of the log (who can see this log)
1035
+ current: updatedClient.ticketConfigs, // Previous (old) client information before update based on DB keys (for internal reference and comparison).
1036
+ previous: findClient.ticketConfigs, // Current (new) client information after update based on DB keys (for internal reference and comparison).
1037
+ oldData: oldData, // Previous (old) client information before update (used for showing detailed changes in the UI).
1038
+ newData: newData, // Current (new) client information after update (used for showing detailed changes in the UI).
907
1039
  };
908
1040
 
1041
+ // Insert activity log into OpenSearch if there are fields that were updated
909
1042
  if ( updateKeys.length ) {
910
- await insertOpenSearchData( openSearch.activityLog, logObj );
1043
+ await insertOpenSearchData( openSearch.activityLog, logObj ); // Insert activity log
911
1044
  }
912
1045
 
913
1046
  if ( updateAck ) {
914
1047
  res.sendSuccess( { result: 'Updated Successfully' } );
915
1048
  }
916
1049
  } catch ( error ) {
1050
+ const err = error.message || 'Internal Server Error';
917
1051
  logger.error( { error: error, message: req.params, function: 'updateTicketConfiguration' } );
918
- return res.sendError( 'Internal Server Error', 500 );
1052
+ return res.sendError( err, 500 );
919
1053
  }
920
1054
  }
921
1055
 
@@ -923,6 +1057,9 @@ export async function updateFeatureConfiguration( req, res ) {
923
1057
  try {
924
1058
  const openSearch = JSON.parse( process.env.OPENSEARCH );
925
1059
  const url = JSON.parse( process.env.URL );
1060
+
1061
+ // Get updated client Feature configuration before the update operation (fetch selected fields only)
1062
+ const previousData = await findOneClient( { clientId: req.params?.id }, { featureConfigs: 1 } );
926
1063
  const inputData = req.body;
927
1064
  if ( inputData?.bouncedLimitValue ) {
928
1065
  inputData.missedOpportunityFromValue = inputData?.bouncedLimitValue;
@@ -933,11 +1070,15 @@ export async function updateFeatureConfiguration( req, res ) {
933
1070
  }
934
1071
  const updateAck = await featureConfigurationUpdate( { clientId: req.params?.id }, inputData );
935
1072
 
936
- let updateKeys = [];
1073
+ // Get updated client Feature configuration after the update operation (fetch selected fields only)
1074
+ const postData = await findOneClient( { clientId: req.params?.id }, { featureConfigs: 1 } );
1075
+ let updateKeys = []; // List of updated field names in readable format
937
1076
 
1077
+ // Check if request body has any fields to update
938
1078
  if ( Object.keys( inputData ).length > 0 ) {
939
1079
  Object.keys( inputData ).forEach( ( element ) => {
940
- updateKeys.push( camelCaseToWords( element ) );
1080
+ element === 'billableCalculation'? 'potentialCalculation': element; // Replace 'billableCalculation' with 'potentialCalculation' if present
1081
+ updateKeys.push( camelCaseToWords( element ) ); // Convert camelCase field name to readable format and add to updateKeys
941
1082
  } );
942
1083
  }
943
1084
 
@@ -986,21 +1127,105 @@ export async function updateFeatureConfiguration( req, res ) {
986
1127
  }
987
1128
 
988
1129
  await postApi( `${url.oldapidomain}/oldBrandUpdate/${data?._id}`, { brandConfigs: data.brandConfigs } );
1130
+ const keysArray = [
1131
+ 'isExcludedArea', 'isPasserByData', 'isNormalized', 'isbillingDisabled',
1132
+ 'isCameraDisabled', 'isFootfallDirectory', 'isNOB', 'isNewTraffic',
1133
+ 'isTrax', 'isNewZone', 'isNewReports', 'isNewDashboard', 'streamBy',
1134
+ ];
1135
+ // Map and rename keys from previous client info for UI display and logging
1136
+ const oldData = {
1137
+ StoreOpenTime: previousData?.featureConfigs?.open,
1138
+ StoreCloseTime: previousData?.featureConfigs?.close,
1139
+ ConversionCalculations: previousData?.featureConfigs?.conversionCalculation,
1140
+ MissedOpportunityCalculation: previousData?.featureConfigs?.missedOpportunityCalculation,
1141
+ PotentialCalculations: previousData?.featureConfigs?.billableCalculation,
1142
+ ConversionCondition: previousData?.featureConfigs?.conversion.condition,
1143
+ ConversionValue: previousData?.featureConfigs?.conversion.value,
1144
+ MissedOpportunityFromCondition: previousData?.featureConfigs?.missedOpportunityFrom?.condition,
1145
+ MissedOpportunityToCondition: previousData?.featureConfigs?.missedOpportunityTo?.condition,
1146
+ MissedOpportunityFromValue: previousData?.featureConfigs?.missedOpportunityFrom?.value,
1147
+ MissedOpportunityToValue: previousData?.featureConfigs?.missedOpportunityTo?.value,
1148
+ BouncedLimitCondition: previousData?.featureConfigs?.bouncedLimit?.condition,
1149
+ BouncedLimitValue: previousData?.featureConfigs?.bouncedLimit?.value,
1150
+ InfraAlertValue: previousData?.featureConfigs?.infraAlert?.value,
1151
+ InfraAlertCondition: previousData?.featureConfigs?.infraAlert?.condition,
1152
+ ConversionCalculations: previousData?.featureConfigs?.conversionCalculation,
1153
+ ExcludedArea: previousData?.featureConfigs?.isExcludedArea ==true ? 'Enable': 'Disable',
1154
+ PasserBydata: previousData?.featureConfigs?.isPasserByData ==true ? 'Enable': 'Disable',
1155
+ NormalizedDataDuringDowntime: previousData?.featureConfigs?.isNormalized ==true ? 'Enable': 'Disable',
1156
+ Billing: previousData?.featureConfigs?.isbillingDisabled ==true ? 'Enable': 'Disable',
1157
+ CameraBlurring: previousData?.featureConfigs?.isCameraDisabled ==true ? 'Enable': 'Disable',
1158
+ FootfallDirectory: previousData?.featureConfigs?.isFootfallDirectory ==true ? 'Enable': 'Disable',
1159
+ NOBStatus: previousData?.featureConfigs?.isNOB ==true ? 'Enable': 'Disable',
1160
+ EnableAnalyze: previousData?.featureConfigs?.isNewDashboard ==true ? 'Enable': 'Disable',
1161
+ Traffic: previousData?.featureConfigs?.isNewTraffic==true ? 'Enable': 'Disable',
1162
+ Zone: previousData?.featureConfigs?.isNewZone ==true ? 'Enable': 'Disable',
1163
+ Zonev2: previousData?.featureConfigs?.isNewZoneV2 ==true ? 'Enable': 'Disable',
1164
+ Reports: previousData?.featureConfigs?.isNewReports ==true ? 'Enable': 'Disable',
1165
+ Trax: previousData?.featureConfigs?.isTrax ==true ? 'Enable': 'Disable',
1166
+ StreamType: previousData?.featureConfigs?.streamBy =='Edge' ? 'Edge App': 'RTSP',
1167
+ FootfallDirectoryOnlyAudit: previousData?.featureConfigs?.isFootfallDirectoryAudit ==true ? 'Enable': 'Disable',
1168
+ FootfallDirectoryOnlyFew: previousData?.featureConfigs?.isFootfallDirectoryLimit ==true ? 'Enable': 'Disable',
1169
+
1170
+ };
989
1171
 
1172
+ // Map and rename keys from current client info for UI display and logging
1173
+ const newData ={
1174
+ StoreOpenTime: postData?.featureConfigs?.open,
1175
+ StoreCloseTime: postData?.featureConfigs?.close,
1176
+ ConversionCalculations: postData?.featureConfigs?.conversionCalculation,
1177
+ MissedOpportunityCalculation: postData?.featureConfigs?.missedOpportunityCalculation,
1178
+ PotentialCalculations: postData?.featureConfigs?.billableCalculation,
1179
+ ConversionCondition: postData?.featureConfigs?.conversion.condition,
1180
+ ConversionValue: postData?.featureConfigs?.conversion.value,
1181
+ MissedOpportunityFromCondition: postData?.featureConfigs?.missedOpportunityFrom?.condition,
1182
+ MissedOpportunityToCondition: postData?.featureConfigs?.missedOpportunityTo?.condition,
1183
+ MissedOpportunityFromValue: postData?.featureConfigs?.missedOpportunityFrom?.value,
1184
+ MissedOpportunityToValue: postData?.featureConfigs?.missedOpportunityTo?.value,
1185
+ BouncedLimitCondition: postData?.featureConfigs?.bouncedLimit?.condition,
1186
+ BouncedLimitValue: postData?.featureConfigs?.bouncedLimit?.value,
1187
+ InfraAlertValue: postData?.featureConfigs?.infraAlert?.value,
1188
+ InfraAlertCondition: postData?.featureConfigs?.infraAlert?.condition,
1189
+ ConversionCalculations: postData?.featureConfigs?.conversionCalculation,
1190
+ ExcludedArea: postData?.featureConfigs?.isExcludedArea ==true ? 'Enable': 'Disable',
1191
+ PasserBydata: postData?.featureConfigs?.isPasserByData ==true ? 'Enable': 'Disable',
1192
+ NormalizedDataDuringDowntime: postData?.featureConfigs?.isNormalized ==true ? 'Enable': 'Disable',
1193
+ Billing: postData?.featureConfigs?.isbillingDisabled ==true ? 'Enable': 'Disable',
1194
+ CameraBlurring: postData?.featureConfigs?.isCameraDisabled ==true ? 'Enable': 'Disable',
1195
+ FootfallDirectory: postData?.featureConfigs?.isFootfallDirectory ==true ? 'Enable': 'Disable',
1196
+ NOBStatus: postData?.featureConfigs?.isNOB ==true ? 'Enable': 'Disable',
1197
+ EnableAnalyze: postData?.featureConfigs?.isNewDashboard ==true ? 'Enable': 'Disable',
1198
+ Traffic: postData?.featureConfigs?.isNewTraffic==true ? 'Enable': 'Disable',
1199
+ Zone: postData?.featureConfigs?.isNewZone ==true ? 'Enable': 'Disable',
1200
+ Zonev2: postData?.featureConfigs?.isNewZoneV2 ==true ? 'Enable': 'Disable',
1201
+ Reports: postData?.featureConfigs?.isNewReports ==true ? 'Enable': 'Disable',
1202
+ Trax: postData?.featureConfigs?.isTrax ==true ? 'Enable': 'Disable',
1203
+ StreamType: postData?.featureConfigs?.streamBy =='Edge' ? 'Edge App': 'RTSP',
1204
+ FootfallDirectoryOnlyAudit: postData?.featureConfigs?.isFootfallDirectoryAudit ==true ? 'Enable': 'Disable',
1205
+ FootfallDirectoryOnlyFew: postData?.featureConfigs?.isFootfallDirectoryLimit ==true ? 'Enable': 'Disable',
990
1206
 
1207
+ };
1208
+
1209
+ // Prepare activity log object with all relevant details for OpenSearch logging
991
1210
  const logObj = {
992
- clientId: req.params?.id,
993
- userName: user?.userName,
994
- email: user?.email,
995
- date: new Date(),
996
- logType: 'configuration',
997
- logSubType: 'featureConfig',
998
- changes: updateKeys,
999
- eventType: 'update',
1000
- showTo: [ 'client', 'tango' ],
1211
+ clientId: req.params?.id, // ID of the client whose data was updated
1212
+ userName: user?.userName, // Name of the user performing the update
1213
+ email: user?.email, // Email of the user performing the update
1214
+ date: new Date(), // Timestamp of the update event
1215
+ logType: 'configuration', // Type of log (e.g., related to brand details)
1216
+ logSubType: Object.keys( inputData ).some( ( key ) => keysArray.includes( key ) ) ? 'dashboardConfig' : 'featureConfig', // Subtype for more specific categorization
1217
+ changes: updateKeys, // List of fields that were updated.these will shown in the UI as part of activity log
1218
+ eventType: 'update', // Type of event (update operation)
1219
+ showTo: [ 'client', 'tango' ], // Visibility of the log (who can see this log)
1220
+ previous: previousData.featureConfigs, // Previous (old) client information before update based on DB keys (for internal reference and comparison).
1221
+ current: postData.featureConfigs, // Current (new) client information after update based on DB keys (for internal reference and comparison).
1222
+ oldData: oldData, // Previous (old) client information before update (used for showing detailed changes in the UI).
1223
+ newData: newData, // Current (new) client information after update (used for showing detailed changes in the UI).
1001
1224
  };
1225
+
1226
+ // Insert activity log into OpenSearch if there are fields that were updated
1002
1227
  if ( updateKeys.length ) {
1003
- await insertOpenSearchData( openSearch.activityLog, logObj );
1228
+ await insertOpenSearchData( openSearch.activityLog, logObj ); // Insert activity log
1004
1229
  }
1005
1230
 
1006
1231
  if ( updateAck ) {
@@ -1016,6 +1241,18 @@ export async function updateFeatureConfiguration( req, res ) {
1016
1241
  export async function domainDetailsConfiguration( req, res ) {
1017
1242
  try {
1018
1243
  const openSearch = JSON.parse( process.env.OPENSEARCH );
1244
+
1245
+ // Get updated client domain configuration before the update operation (fetch selected fields only)
1246
+ const getPreData = await findOneClient(
1247
+ { clientId: req.params?.id }, // filter by clientId
1248
+ {
1249
+ domainName: '$domainConfig.ssoLogin.domainName',
1250
+ isEnable: '$domainConfig.ssoLogin.isEnable',
1251
+ ipWhitelist: '$domainConfig.ipWhitelisting.enableWhitelisting',
1252
+ WhitelistedIps: '$domainConfig.ipWhitelisting.allowedIps',
1253
+ TwoFactorAuthentication: '$domainConfig.enableOtp',
1254
+ },
1255
+ );
1019
1256
  const updateAck = await domainDetailsConfigurationUpdate( {
1020
1257
  clientId: req.params?.id, domainName: req.body?.domainName, isEnable: req.body?.isEnable,
1021
1258
  enableWhitelisting: req.body?.enableWhitelisting, allowedIps: req.body?.allowedIps, enableOtp: req.body?.enableOtp,
@@ -1023,30 +1260,66 @@ export async function domainDetailsConfiguration( req, res ) {
1023
1260
 
1024
1261
  let updateKeys = [];
1025
1262
 
1026
-
1263
+ // Check if request body has any fields to update
1027
1264
  if ( Object.keys( req.body ).length > 0 ) {
1028
1265
  Object.keys( req.body ).forEach( ( element ) => {
1029
1266
  updateKeys.push( camelCaseToWords( element ) );
1030
1267
  } );
1031
1268
  }
1032
1269
 
1033
- const user = await getUserNameEmailById( req.userId );
1270
+ // Get updated client domain configuration after the update operation (fetch selected fields only)
1271
+ const getPostData = await findOneClient(
1272
+ { clientId: req.params?.id }, // Filter by clientId
1273
+ {
1274
+ domainName: '$domainConfig.ssoLogin.domainName',
1275
+ isEnable: '$domainConfig.ssoLogin.isEnable',
1276
+ ipWhitelist: '$domainConfig.ipWhitelisting.enableWhitelisting',
1277
+ WhitelistedIps: '$domainConfig.ipWhitelisting.allowedIps',
1278
+ TwoFactorAuthentication: '$domainConfig.enableOtp',
1279
+ },
1280
+ );
1281
+
1034
1282
 
1283
+ // Map and rename keys from previous client info for UI display and logging
1284
+ const oldData = {
1285
+ DomainName: getPreData?.domainConfig?.ssoLogin?.domainName,
1286
+ IsEnable: getPreData?.domainConfig?.ssoLogin?.isEnable ==true ? 'Enable': 'Disable',
1287
+ IPWhitelist: getPreData?.domainConfig?.ipWhitelisting?.enableWhitelisting ==true ? 'Enable': 'Disable',
1288
+ WhitelistedIps: ( getPreData?.domainConfig?.ipWhitelisting?.allowedIps ).join( ', ' ),
1289
+ TwoFactorAuthentication: getPreData?.domainConfig?.enableOtp ==true ? 'Enable': 'Disable',
1290
+
1291
+ };
1035
1292
 
1293
+ // Map and rename keys from current client info for UI display and logging
1294
+ const newData ={
1295
+ DomainName: getPostData?.domainConfig?.ssoLogin?.domainName,
1296
+ IsEnable: getPostData?.domainConfig?.ssoLogin?.isEnable ==true ? 'Enable': 'Disable',
1297
+ IPWhitelist: getPostData?.domainConfig?.ipWhitelisting?.enableWhitelisting ==true ? 'Enable': 'Disable',
1298
+ WhitelistedIps: ( getPostData?.domainConfig?.ipWhitelisting?.allowedIps ).join( ', ' ),
1299
+ TwoFactorAuthentication: getPostData?.domainConfig?.enableOtp ==true ? 'Enable': 'Disable',
1300
+ };
1301
+
1302
+
1303
+ // Prepare activity log object with all relevant details for OpenSearch logging
1036
1304
  const logObj = {
1037
- clientId: req.params?.id,
1038
- userName: user?.userName,
1039
- email: user?.email,
1040
- date: new Date(),
1041
- logType: 'configuration',
1042
- logSubType: 'domainDetails',
1043
- changes: updateKeys,
1044
- eventType: 'update',
1045
- showTo: [ 'client', 'tango' ],
1305
+ clientId: req.params?.id, // ID of the client whose data was updated
1306
+ userName: req?.user?.userName, // Name of the user performing the update
1307
+ email: req?.user?.email, // Email of the user performing the update
1308
+ date: new Date(), // Timestamp of the update event
1309
+ logType: 'configuration', // Type of log (e.g., related to brand details)
1310
+ logSubType: 'domainDetails', // Subtype for more specific categorization
1311
+ changes: updateKeys, // List of fields that were updated.these will shown in the UI as part of activity log
1312
+ eventType: 'update', // Type of event (update operation)
1313
+ showTo: [ 'client', 'tango' ], // Visibility of the log (who can see this log)
1314
+ previous: getPreData, // Previous (old) client information before update based on DB keys (for internal reference and comparison).
1315
+ current: getPostData, // Current (new) client information after update based on DB keys (for internal reference and comparison).
1316
+ oldData: oldData, // Previous (old) client information before update (used for showing detailed changes in the UI).
1317
+ newData: newData, // Current (new) client information after update (used for showing detailed changes in the UI).
1046
1318
  };
1047
1319
 
1320
+ // Insert activity log into OpenSearch if there are fields that were updated
1048
1321
  if ( updateKeys.length ) {
1049
- await insertOpenSearchData( openSearch.activityLog, logObj );
1322
+ await insertOpenSearchData( openSearch.activityLog, logObj ); // Insert activity log
1050
1323
  }
1051
1324
 
1052
1325
  if ( updateAck ) {
@@ -1148,7 +1421,6 @@ export async function updateDocuments( req, res ) {
1148
1421
 
1149
1422
  const user = await getUserNameEmailById( req.userId );
1150
1423
 
1151
-
1152
1424
  const logObj = {
1153
1425
  clientId: req.params?.id,
1154
1426
  userName: user?.userName,
@@ -1210,14 +1482,29 @@ export async function auditConfiguration( req, res ) {
1210
1482
  auditConfigs: 1, _id: 0,
1211
1483
  };
1212
1484
  const previousClient = await findOneClient( query, fields );
1485
+ previousClient.auditConfigs.ratio = previousClient?.auditConfigs?.ratio? ( previousClient.auditConfigs.ratio )*100 : null;
1213
1486
  const record = {
1214
1487
  'auditConfigs.count': inputData.count,
1215
1488
  'auditConfigs.audit': inputData.audit,
1216
1489
  'auditConfigs.ratio': Number( normalizeNumber( inputData.ratio, 0, 100 ) ),
1217
1490
  };
1218
- logger.info( { record: record, query: query } );
1219
- const a = await updateOneClient( query, record );
1220
- logger.info( { a: a } );
1491
+ await updateOneClient( query, record );
1492
+
1493
+
1494
+ // Map and rename keys from previous client info for UI display and logging
1495
+ const oldData = {
1496
+ AuditStatus: inputData?.audit ==true ? 'Enable': 'Disable',
1497
+ MappingPercentage: inputData?.ratio,
1498
+ AuditCount: inputData?.count,
1499
+
1500
+ };
1501
+
1502
+ // Map and rename keys from current client info for UI display and logging
1503
+ const newData ={
1504
+ AuditStatus: previousClient.auditConfigs?.audit ==true ? 'Enable': 'Disable',
1505
+ MappingPercentage: previousClient.auditConfigs?.ratio,
1506
+ AuditCount: previousClient.auditConfigs?.count,
1507
+ };
1221
1508
  const logObj = {
1222
1509
  clientId: req.params?.id,
1223
1510
  userName: req.user?.userName,
@@ -1228,13 +1515,15 @@ export async function auditConfiguration( req, res ) {
1228
1515
  eventType: 'update',
1229
1516
  showTo: [ 'tango' ],
1230
1517
  changes: [ `Audit config for client id ${req.params?.id}` ],
1231
- previous: previousClient,
1518
+ previous: previousClient.auditConfigs,
1232
1519
  current: {
1233
1520
  clientId: req.params?.id,
1234
1521
  count: inputData.count,
1235
1522
  audit: inputData.audit,
1236
- ratio: normalizeNumber( inputData.ratio, 0, 100 ),
1523
+ ratio: inputData.ratio,
1237
1524
  },
1525
+ oldData: oldData,
1526
+ newData: newData,
1238
1527
  };
1239
1528
 
1240
1529
  await insertOpenSearchData( openSearch.activityLog, logObj );
@@ -1277,17 +1566,6 @@ export async function clientList( req, res ) {
1277
1566
  $match: {
1278
1567
  userEmail: { $eq: req?.user?.email },
1279
1568
  userType: 'tango',
1280
- // $expr: {
1281
- // $cond: {
1282
- // if: {
1283
- // $and: [
1284
- // { $eq: [ '$tangoUserType', 'csm' ] },
1285
- // ],
1286
- // },
1287
- // then: { $eq: [ '$isClientApproved', true ] },
1288
- // else: true,
1289
- // },
1290
- // },
1291
1569
 
1292
1570
  },
1293
1571
  }, {
@@ -1603,17 +1881,6 @@ export async function clientListV1( req, res ) {
1603
1881
  $match: {
1604
1882
  userEmail: { $eq: req?.user?.email },
1605
1883
  userType: 'tango',
1606
- // $expr: {
1607
- // $cond: {
1608
- // if: {
1609
- // $and: [
1610
- // { $eq: [ '$tangoUserType', 'csm' ] },
1611
- // ],
1612
- // },
1613
- // then: { $eq: [ '$isClientApproved', true ] },
1614
- // else: true,
1615
- // },
1616
- // },
1617
1884
 
1618
1885
  },
1619
1886
  },
@@ -2009,9 +2276,37 @@ export async function detailedClientCount( req, res ) {
2009
2276
  }
2010
2277
  }
2011
2278
 
2012
- export async function getActivityLogs( req, res ) {
2279
+ export async function getActivityLogs1( req, res ) {
2013
2280
  try {
2281
+ const inputData = req.body;
2014
2282
  const openSearch = JSON.parse( process.env.OPENSEARCH );
2283
+ const clientQuery =[
2284
+ {
2285
+ $match: {
2286
+ $and: [
2287
+ {
2288
+ clientId: { $in: inputData },
2289
+ },
2290
+ ],
2291
+ },
2292
+ },
2293
+ {
2294
+ $group: {
2295
+ _id: null,
2296
+ clientName: { $push: '$clientName' },
2297
+ },
2298
+ },
2299
+ {
2300
+ $project: {
2301
+ _id: 0,
2302
+ clientName: 1,
2303
+ },
2304
+ },
2305
+ ];
2306
+ const getClientName = await aggregateClient( clientQuery );
2307
+ if ( getClientName?.length == 0 || getClientName[0]?.clientName?.length ===0 ) {
2308
+ return res.sendError( 'No Data Found', 204 );
2309
+ }
2015
2310
  const query = {
2016
2311
  '_source': [
2017
2312
  'userId', 'userName', 'email', 'date', 'logType', 'logSubType',
@@ -2019,13 +2314,27 @@ export async function getActivityLogs( req, res ) {
2019
2314
  ],
2020
2315
  'query': {
2021
2316
  'bool': {
2022
- 'must': [
2317
+ // 'must': [
2318
+ // {
2319
+ // 'terms': {
2320
+ // 'clientId.keyword': [ req.body.clientId ],
2321
+ // },
2322
+ // },
2323
+ // ],
2324
+
2325
+ 'should': [
2326
+ {
2327
+ 'terms': {
2328
+ 'clientId.keyword': [ inputData.clientId ],
2329
+ },
2330
+ },
2023
2331
  {
2024
2332
  'terms': {
2025
- 'clientId.keyword': [ req.body.clientId ],
2333
+ 'clientName.keyword': [ getClientName[0].clientName ], // Add clientName condition
2026
2334
  },
2027
2335
  },
2028
2336
  ],
2337
+ 'minimum_should_match': 1, // Ensures at least one condition must match
2029
2338
  },
2030
2339
  },
2031
2340
  'from': ( req.body.offset - 1 ) * req.body.limit,
@@ -2072,30 +2381,22 @@ export async function getActivityLogs( req, res ) {
2072
2381
 
2073
2382
  const hits = logs?.body?.hits?.hits;
2074
2383
  const totalDocuments = logs?.body?.hits?.total?.value;
2384
+
2075
2385
  let temp = [];
2076
2386
  if ( totalDocuments ) {
2077
2387
  hits.map( ( hit, i ) => {
2078
- temp.push( hit?._source );
2388
+ let respo ={};
2389
+ hit._source.logSubType = hit._source.logSubType === 'documentUpload'? 'documentsUpload': hit._source.logSubType === 'domainDetails'? 'securityFeatures': hit?._source?.logSubType;
2079
2390
  if ( ( ( hit?._source?.eventType ).match( /update/ ) || ( hit?._source?.eventType ).match( /edit/ ) )&& hit?._source?.previous ) {
2080
- const previous = removeIndexedKeysAndFlatten( hit?._source?.previous );
2081
- const current=removeIndexedKeysAndFlatten( hit?._source?.current );
2082
- delete previous?._id;
2083
- delete current?._id;
2084
- delete previous?.updatedAt;
2085
- delete current?.updatedAt;
2086
- delete current?.createdAt;
2087
-
2088
- logger.info( { previous2: hit?._source?.previous, previous: previous, current: hit?._source?.current, current: current } );
2089
- const diff = Object.entries( current )
2090
- .reduce( ( acc, [ key, value ] ) => {
2091
- if ( value !== previous[key] & typeof previous[key] === 'string' ) {
2092
- acc[key] = { previous: previous[key], current: value };
2093
- }
2094
-
2095
- return acc;
2096
- }, {} );
2097
- logger.info( { diff: diff } );
2098
- temp[i].updatedValue = diff;
2391
+ const previous = hit?._source?.previous;
2392
+ const current=hit?._source?.current;
2393
+ const logType =hit?._source?.logType;
2394
+
2395
+ const logSubType =hit?._source?.logSubType;
2396
+ respo = findDifferences1( previous, current, logType, logSubType );
2397
+ hit._source.updatedValue = respo;
2398
+ temp.push( hit?._source );
2399
+ hit._source.changes =logSubType === 'ticketConfig'? Object.keys( respo ).map( ( item ) => item ) : hit._source.changes;
2099
2400
  } else {
2100
2401
  temp.push( hit?._source );
2101
2402
  }
@@ -2110,25 +2411,284 @@ export async function getActivityLogs( req, res ) {
2110
2411
  return res.sendError( 'Internal Server Error', 500 );
2111
2412
  }
2112
2413
  }
2113
- function removeIndexedKeysAndFlatten( obj ) {
2114
- const result = {};
2115
2414
 
2116
- for ( const [ key, value ] of Object.entries( obj ) ) {
2117
- // Skip numeric keys and preserve arrays intact
2118
- if ( /^\d+$/.test( key ) ) continue;
2119
2415
 
2120
- if ( Array.isArray( value ) ) {
2121
- result[key] = value;
2122
- } else if ( typeof value === 'object' && value !== null ) {
2123
- Object.assign( result, removeIndexedKeysAndFlatten( value ) );
2416
+ function findDifferences1( previous, current, logType, logSubType, path = '' ) {
2417
+ const dbKeys = JSON.parse( process.env.DB_KEYS );
2418
+ const ignoredKeys = new Set( [
2419
+ '_id', 'updatedAt', 'createdAt', 'password', 'clientId',
2420
+ 'storeId', 'refreshToken', 'employeeId', 'fcmToken', 'permission', 'updateFeatureConfig',
2421
+ ] );
2422
+ const documents = dbKeys.DOCUMENTS;
2423
+ // Get correct key mapping based on logType
2424
+ const keyMapping = logType == 'stores' ?documents.stores: logType == 'users' ?documents.users :logSubType=='reportConfig'?documents.reports: logType=='brandInfo'?documents.client:documents.client;
2425
+
2426
+
2427
+ let differences = {};
2428
+
2429
+ for ( const key in current ) {
2430
+ if ( !Object.prototype.hasOwnProperty.call( current, key ) || ignoredKeys.has( key ) ) continue;
2431
+
2432
+ const prevValue = previous[key];
2433
+ const currValue = current[key];
2434
+
2435
+ if ( typeof prevValue === 'object' && typeof currValue === 'object' && prevValue !== null && currValue !== null ) {
2436
+ if ( logSubType!== 'userUpdated' ) {
2437
+ if ( key == 'spocDetails' ) {
2438
+ if ( _.isEqual( prevValue, currValue ) ) {
2439
+ continue;
2440
+ } else {
2441
+ let result = compareArrayObjects( prevValue, currValue );
2442
+ differences = { ...differences, ...result };
2443
+ }
2444
+ } else if ( Array.isArray( prevValue ) && prevValue.every( ( item ) => typeof item === 'string' ) ||Array.isArray( currValue ) && currValue.every( ( item ) => typeof item === 'string' ) ) {
2445
+ JSON.stringify( prevValue ) !== JSON.stringify( currValue )?
2446
+ differences[`${path}${key}`] = { previous: prevValue.length> 0 ? prevValue.join( ',' ): null, current: currValue.length > 0? currValue.join( ',' ): null } : null;
2447
+
2448
+ // differences[`${path}${key}`] = { previous: removed.length> 0 ? prevValue.join( ',' ): prevValue.join( ', ' ) :, current: added.length > 0? addedUsers.join( ', ' ) : currValue.join( ',' ) } : null;
2449
+ } else {
2450
+ const nestedDiffs = findDifferences1( prevValue, currValue, logType, logSubType, `${path}${key}.` );
2451
+ Object.assign( differences, nestedDiffs );
2452
+ }
2453
+ } else {
2454
+ const nestedDiffs = findDifferences1( prevValue, currValue, logType, logSubType, `${path}${key}.` );
2455
+ Object.assign( differences, nestedDiffs );
2456
+ }
2457
+ } else if ( prevValue !== currValue ) {
2458
+ if (
2459
+ ( prevValue === '' && currValue === '' ) ||
2460
+ ( Array.isArray( prevValue ) && Array.isArray( currValue ) && prevValue.length === 0 && currValue.length === 0 )
2461
+ ) {
2462
+ continue;
2463
+ }
2464
+ differences[`${path}${key}`] = { previous: prevValue, current: currValue };
2465
+ }
2466
+ }
2467
+
2468
+ // **Transform & Filter Differences**
2469
+ let updatedDifferences = {};
2470
+ Object.keys( differences ).forEach( ( key ) => {
2471
+ let newKey = key.replace( /spocDetails\.\d+\./, 'spocDetails.' ).replace( /WhitelistedIps\.\d+/, 'Whitelisted Ips' );
2472
+ let diff = differences[key];
2473
+ if ( newKey.toLowerCase().includes( 'isactive' ) ) {
2474
+ diff = {
2475
+ previous: diff.previous ? 'Active' : 'Deactive',
2476
+ current: diff.current ? 'Active' : 'Deactive',
2477
+ };
2478
+ }
2479
+
2480
+ const userFriendlyKey = getUserFriendlyKey( newKey, keyMapping );
2481
+ if ( userFriendlyKey ) {
2482
+ newKey = userFriendlyKey;
2483
+ }
2484
+ const binaryKeys = [ 'Two Factor Authentication', 'Mat Enabled', 'Audit Status', 'Infra Email Alert', 'Ip Whitelist', 'Excluded Area', 'Passer-by data', 'Normalized data during downtime', 'Billing', 'Camera Blurring', 'Footfall Directory', 'Footfall Directory Audit', 'Footfall Directory Limit', 'NOB Status', 'Traffic', 'Trax', 'Zone V1', 'Zone V2', 'Reports', 'Analyze' ];
2485
+ if ( binaryKeys.includes( newKey ) ) {
2486
+ diff.previous = ( diff.previous == true || diff.previous == 'Enabled' ) ? 'Enabled' : 'Disabled';
2487
+ diff.current = ( diff.current == true || diff.current == 'Enabled' ) ? 'Enabled' : 'Disabled';
2488
+ }
2489
+ if ( newKey === 'Server Type' ) {
2490
+ diff.previous = ( diff.previous == true || diff.previous == 'Server' ) ? 'Server' : 'Serverless';
2491
+ diff.current = ( diff.current == true || diff.current == 'Server' ) ? 'Server' : 'Serverless';
2492
+ }
2493
+
2494
+ if ( newKey === 'Stream Type' ) {
2495
+ diff.previous = ( diff.previous == 'Edge' || diff.previous == 'Edge App' ) ? 'Edge App' : 'RTSP';
2496
+ diff.current = ( diff.current == 'Edge' || diff.current == 'Edge App' ) ? 'Edge App' : 'RTSP';
2497
+ }
2498
+
2499
+ // **Transform Roles Permission Keys**
2500
+ const rolesPermissionMatch = newKey.match( /rolespermission\.(\d+)\.modules\.(\d+)\.(isAdd|isEdit)/ );
2501
+ if ( rolesPermissionMatch ) {
2502
+ const [ , roleIndex, moduleIndex, action ] = rolesPermissionMatch;
2503
+ const role = previous.rolespermission?.[roleIndex];
2504
+ const module = role?.modules?.[moduleIndex];
2505
+
2506
+ if ( module ) {
2507
+ newKey = `User's ${module.name} Module ${action === 'isAdd' ? 'Add' : 'Edit'}`;
2508
+ diff.previous = diff.previous ? 'Enabled' : 'Disabled';
2509
+ diff.current = diff.current ? 'Enabled' : 'Disabled';
2510
+ }
2511
+ }
2512
+
2513
+ if ( !newKey || ( diff.previous === '' && !diff.current ) ) return;
2514
+
2515
+ if (
2516
+ ( diff.previous === '' || diff.previous === null || ( Array.isArray( diff.previous ) && diff.previous.length === 0 ) ) &&
2517
+ diff.current === undefined
2518
+ ) {
2519
+ return;
2520
+ }
2521
+ if (
2522
+ ( diff.current === '' || diff.current === null || ( Array.isArray( diff.current ) && diff.current.length === 0 ) ) &&
2523
+ diff.previous === undefined
2524
+ ) {
2525
+ return;
2526
+ }
2527
+
2528
+ updatedDifferences[newKey] = diff;
2529
+ } );
2530
+
2531
+ return updatedDifferences;
2532
+ }
2533
+
2534
+
2535
+ function getUserFriendlyKey( keyPath, mapping ) {
2536
+ const keys = keyPath.split( '.' ); // Split the key path (e.g., storeProfile.pincode → ['storeProfile', 'pincode'])
2537
+ let value = mapping;
2538
+
2539
+ for ( const key of keys ) {
2540
+ if ( value && typeof value === 'object' && key in value ) {
2541
+ value = value[key]; // Traverse down the object
2124
2542
  } else {
2125
- result[key] = value;
2543
+ return null; // Return original if not found
2126
2544
  }
2127
2545
  }
2128
2546
 
2129
- return result;
2547
+ return typeof value === 'string' ? value : keyPath; // Return mapped value or original key
2130
2548
  }
2131
2549
 
2550
+ export async function getActivityLogs( req, res ) {
2551
+ try {
2552
+ const inputData = req.body;
2553
+ const openSearch = JSON.parse( process.env.OPENSEARCH );
2554
+ const clientQuery =[
2555
+ {
2556
+ $match: {
2557
+ $and: [
2558
+ {
2559
+ clientId: { $eq: inputData.clientId },
2560
+ },
2561
+ ],
2562
+ },
2563
+ },
2564
+ {
2565
+ $group: {
2566
+ _id: null,
2567
+ clientName: { $push: '$clientName' },
2568
+ },
2569
+ },
2570
+ {
2571
+ $project: {
2572
+ _id: 0,
2573
+ clientName: 1,
2574
+ },
2575
+ },
2576
+ ];
2577
+ const getClientName = await aggregateClient( clientQuery );
2578
+ if ( getClientName?.length == 0 || getClientName[0]?.clientName?.length ===0 ) {
2579
+ return res.sendError( 'No Data Found', 204 );
2580
+ }
2581
+ const query = {
2582
+ '_source': [
2583
+ 'userId', 'userName', 'email', 'date', 'logType', 'logSubType',
2584
+ 'changes', 'eventType', 'previous', 'current', 'oldData', 'newData',
2585
+ ],
2586
+ 'query': {
2587
+ 'bool': {
2588
+ 'must': [
2589
+ {
2590
+ 'terms': {
2591
+ 'clientId.keyword': [ inputData.clientId ],
2592
+ },
2593
+ },
2594
+
2595
+ ],
2596
+ 'should': [
2597
+ {
2598
+ 'terms': {
2599
+ 'clientName.keyword': getClientName[0].clientName, // Add clientName condition
2600
+ },
2601
+ },
2602
+ ],
2603
+ },
2604
+ },
2605
+ 'from': ( req.body.offset - 1 ) * req.body.limit,
2606
+ 'size': req.body.limit,
2607
+ 'sort': [
2608
+ {
2609
+ 'date': {
2610
+ 'order': 'desc',
2611
+ },
2612
+ },
2613
+ ],
2614
+ };
2615
+
2616
+ if ( req.body?.logTypeFilters?.length ) {
2617
+ query.query.bool.must.push(
2618
+ {
2619
+ 'terms': {
2620
+ 'logType.keyword': req.body.logTypeFilters,
2621
+ },
2622
+ },
2623
+ );
2624
+ }
2625
+
2626
+ if ( req.body?.startDate || req.body?.endDate ) {
2627
+ query.query.bool.must.push( {
2628
+ 'range': {
2629
+ 'date': {
2630
+ 'gte': req.body?.startDate ? req.body.startDate : '',
2631
+ 'lte': req.body?.endDate ? req.body.endDate : '',
2632
+ },
2633
+ },
2634
+ } );
2635
+ }
2636
+
2637
+ query.query.bool.must.push(
2638
+ {
2639
+ 'terms': {
2640
+ 'showTo.keyword': [ req.user.userType ],
2641
+ },
2642
+ },
2643
+ );
2644
+
2645
+ const logs = await getOpenSearchData( openSearch.activityLog, query );
2646
+
2647
+ const hits = logs?.body?.hits?.hits;
2648
+ const totalDocuments = logs?.body?.hits?.total?.value;
2649
+
2650
+ let temp = [];
2651
+ if ( totalDocuments ) {
2652
+ hits.map( ( hit, i ) => {
2653
+ let respo ={};
2654
+ hit._source.logSubType = hit._source.logSubType === 'documentUpload'? 'documentsUpload': hit._source.logSubType === 'domainDetails'? 'securityFeatures': hit?._source?.logSubType;
2655
+ if ( ( ( hit?._source?.eventType ).match( /update/ ) || ( hit?._source?.eventType ).match( /edit/ ) )&& hit?._source?.previous ) {
2656
+ const previous = hit?._source?.oldData;
2657
+ const current=hit?._source?.newData;
2658
+ respo = findDifferences( previous, current );
2659
+ hit._source.updatedValue = respo;
2660
+ temp.push( hit?._source );
2661
+ // hit._source.changes =logSubType === 'ticketConfig'? Object.keys( respo ).map( ( item ) => item ) : hit._source.changes;
2662
+ } else {
2663
+ temp.push( hit?._source );
2664
+ }
2665
+ },
2666
+ );
2667
+ res.sendSuccess( { data: temp, count: totalDocuments } );
2668
+ } else {
2669
+ res.sendError( 'No data found', 204 );
2670
+ }
2671
+ } catch ( error ) {
2672
+ logger.error( { error: error, message: req.body, function: 'getActivityLogs' } );
2673
+ return res.sendError( 'Internal Server Error', 500 );
2674
+ }
2675
+ }
2676
+
2677
+ function findDifferences( obj1, obj2 ) {
2678
+ let differences = {};
2679
+
2680
+ for ( let key in obj1 ) {
2681
+ if ( obj1[key] !== obj2[key] ) {
2682
+ differences[splitCamelCase( key )] = { previous: obj1[key], current: obj2[key] };
2683
+ }
2684
+ }
2685
+
2686
+ return differences;
2687
+ }
2688
+
2689
+ function splitCamelCase( text ) {
2690
+ return text.replace( /([a-z])([A-Z])/g, '$1 $2' );
2691
+ }
2132
2692
 
2133
2693
  async function postApi( url, data ) {
2134
2694
  const requestOptions = {
@@ -2197,3 +2757,34 @@ export async function clientCsmAssignAction( req, res ) {
2197
2757
  return res.sendError( 'Internal Server Error', 500 );
2198
2758
  }
2199
2759
  }
2760
+
2761
+ function compareArrayObjects( prevArray, currArray ) {
2762
+ let changes = {};
2763
+ // logger.info( { prevArray: prevArray, currArray: currArray } );
2764
+ // Find the longest array length to avoid index mismatch
2765
+ let maxLength = Math.max( prevArray.length, currArray.length );
2766
+
2767
+ for ( let i = 0; i < maxLength; i++ ) {
2768
+ let prevObj = prevArray[i] || {}; // Default to empty object if missing
2769
+ let currObj = currArray[i] || {};
2770
+ let diff = {};
2771
+
2772
+ Object.keys( { ...prevObj, ...currObj } ).forEach( ( key ) => {
2773
+ if ( prevObj[key] !== currObj[key] && key !== '_id' ) {
2774
+ const key1 = key == 'contact'? 'Contact Number': key == 'email'? 'Email ID':key == 'name'?'Name': key == 'designation'?'Designation':key;
2775
+ const name = i ==0? 'Primary Spoc': `Alternate Spoc ${i+1}`;
2776
+ diff[`${name} ${key1}`] = {
2777
+ previous: prevObj[key] || 'null',
2778
+ current: currObj[key] || 'null',
2779
+ };
2780
+ }
2781
+ } );
2782
+
2783
+ if ( Object.keys( diff ).length > 0 ) {
2784
+ // changes.push( { index: i, changes: diff } );
2785
+ changes = { ...changes, ...diff };
2786
+ }
2787
+ }
2788
+
2789
+ return changes;
2790
+ }
@@ -400,20 +400,12 @@ export const clientDocs = {
400
400
  },
401
401
  },
402
402
 
403
- '/v3/client/activity-log/{id}': {
403
+ '/v3/client/activity-log': {
404
404
  post: {
405
405
  tags: [ 'Client' ],
406
406
  description: 'Activity log',
407
407
  operationId: 'activity-log',
408
- parameters: [ {
409
- in: 'path',
410
- name: 'id',
411
- required: true,
412
- description: 'The ID of the client.',
413
- schema: {
414
- type: 'string',
415
- },
416
- } ],
408
+ parameters: [],
417
409
  requestBody: {
418
410
  content: {
419
411
  'application/json': {