@c8y/ngx-components 1017.0.511 → 1017.0.512

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.
@@ -6996,7 +6996,7 @@ class CookieBannerService {
6996
6996
  return JSON.parse(localStorage.getItem(this.STORAGE_KEY));
6997
6997
  }
6998
6998
  /**
6999
- * Verifies that cookie preferences configuration is defined.
6999
+ * Verifies that cookie preferences configuration is defined in the application options.
7000
7000
  * @returns {boolean} Returns if the cookie preferences configuration is defined.
7001
7001
  */
7002
7002
  isConfigCookiePreferencesDefined() {
@@ -7063,8 +7063,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImpor
7063
7063
  * tag and
7064
7064
  */
7065
7065
  class GainsightService {
7066
- constructor(document, appState, options, cookieBannerService, userPreferencesService, translateService) {
7067
- this.document = document;
7066
+ constructor(appState, options, cookieBannerService, userPreferencesService, translateService) {
7068
7067
  this.appState = appState;
7069
7068
  this.options = options;
7070
7069
  this.cookieBannerService = cookieBannerService;
@@ -7074,6 +7073,11 @@ class GainsightService {
7074
7073
  * A subject that emits the tag function as soon as a new tag is set.
7075
7074
  */
7076
7075
  this.tagFunction$ = new BehaviorSubject(null);
7076
+ this.trackingLoaded$ = new Subject();
7077
+ /**
7078
+ * Gainsight is activated only when the cookie banner is present. If functional cookies are enabled, both personally identifiable information (PII) and required data are sent.
7079
+ * Otherwise, only the required data is transmitted during the identity step execution.
7080
+ */
7077
7081
  this.USER_PREFERENCES_GAINSIGHT_KEY = 'gainsightEnabled';
7078
7082
  /**
7079
7083
  * The name of the key remained unchanged, but applies to all engagements.
@@ -7085,13 +7089,21 @@ class GainsightService {
7085
7089
  this.SCRIPT_EXECUTION_WAIT_TIME = 500;
7086
7090
  this.OPTIONS_KEY_CATEGORY = 'gainsight';
7087
7091
  this.OPTIONS_KEY_NAME = 'api.key';
7088
- this.ENGAGEMENTS = 'engagements';
7089
7092
  this.isScriptLoaded = false;
7090
7093
  }
7094
+ /**
7095
+ * Checks if the specified Gainsight preference is disabled in user preferences.
7096
+ * @param preferenceName - Name of the Gainsight preference.
7097
+ * @returns A promise that resolves to `true` if the preference is disabled, otherwise `false`.
7098
+ */
7091
7099
  async isGainsightPreferenceDisabledInUserPreferences(preferenceName) {
7092
7100
  const userGainsightPref = await this.userPreferencesService.get(preferenceName).toPromise();
7093
7101
  return userGainsightPref === false;
7094
7102
  }
7103
+ /**
7104
+ * Sets the state of the functional cookie.
7105
+ * @param value - A boolean value to indicate whether the functional cookie should be enabled (`true`) or disabled (`false`).
7106
+ */
7095
7107
  setFunctionalCookie(value) {
7096
7108
  const cookies = this.cookieBannerService.getUserCookiePreferences();
7097
7109
  if (cookies) {
@@ -7120,56 +7132,111 @@ class GainsightService {
7120
7132
  /**
7121
7133
  * Load the script tag and calls the identify function to start the tracking.
7122
7134
  * @param currentTenant The current tenant.
7123
- * @param identify If set to false, only the tag is loaded.
7135
+ * @param sendPiiData Flag for sending personally identifiable information (PII) during identification in Gainsight.
7124
7136
  */
7125
- async loadTag(currentTenant, identify = true) {
7137
+ async loadTag(currentTenant, sendPiiData) {
7126
7138
  const scriptTag = document.createElement('script');
7127
7139
  const key = await this.getGainsightKey();
7128
7140
  if (key && !this.isScriptLoaded) {
7129
7141
  this.loadScriptTag(scriptTag, key);
7130
- combineLatest(this.appState.currentUser, fromEvent(scriptTag, 'load'), this.appState.state$.pipe(filter(({ versions }) => versions.backend), map(({ versions }) => versions), take(1)))
7142
+ const currentUserStream = this.appState.currentUser;
7143
+ const scriptLoadStream = fromEvent(scriptTag, 'load');
7144
+ const versionStream = this.appState.state$.pipe(filter(({ versions }) => versions.backend), map(({ versions }) => versions), take(1));
7145
+ const sourceStreams = sendPiiData
7146
+ ? [currentUserStream, scriptLoadStream, versionStream]
7147
+ : [currentUserStream, scriptLoadStream];
7148
+ combineLatest(sourceStreams)
7131
7149
  .pipe(delay(this.SCRIPT_EXECUTION_WAIT_TIME), filter(([user, scriptEvent]) => !!(scriptEvent && user)))
7132
- .subscribe(([user, , versions]) => {
7150
+ .subscribe(args => {
7151
+ const [user, , versions] = args;
7152
+ this.setGlobalContext();
7133
7153
  const instanceId = this.getInstanceIdFromUrl();
7134
- if (identify) {
7135
- this.setGlobalContext();
7136
- this.identify(user, currentTenant, instanceId, versions.ui.ngx, versions.backend);
7154
+ if (sendPiiData) {
7155
+ const versionUI = versions.ui.ngx;
7156
+ const versionBE = versions.backend;
7157
+ const extendedIdentifyData = {
7158
+ user,
7159
+ currentTenant,
7160
+ instanceId,
7161
+ versionUI,
7162
+ versionBE
7163
+ };
7164
+ this.identify(sendPiiData, extendedIdentifyData);
7165
+ }
7166
+ else {
7167
+ const requiredIdentifyData = { user, currentTenant, instanceId };
7168
+ this.identify(sendPiiData, requiredIdentifyData);
7137
7169
  }
7138
7170
  this.isScriptLoaded = true;
7139
7171
  this.tagFunction$.next(this.tagFunction);
7172
+ this.trackingLoaded$.next(true);
7140
7173
  });
7141
7174
  }
7142
7175
  }
7143
7176
  /**
7144
7177
  * Identifies the user/account at Gainsight.
7145
- * @param user The user which is given to Gainsight.
7146
- * @param tenant The tenant which is given to Gainsight.
7147
- * @param versionUI The UI version used.
7148
- * @param versionBE The BE version used.
7178
+ * @param sendPiiData Flag for sending personally identifiable information.
7179
+ * @param identifyData Object containing identification data.
7149
7180
  */
7150
- identify(user, tenant, instanceId, versionUI, versionBE) {
7181
+ identify(sendPiiData, identifyData) {
7151
7182
  const windowRef = window;
7152
- const { id: userId, email, userName, firstName, lastName, roles } = user;
7153
- const { name, customProperties, domainName } = tenant;
7154
- const { externalReference } = customProperties || {};
7155
- windowRef[this.GAINSIGHT_GLOBAL_SCOPE]('identify', {
7156
- id: `${userId}_${name}_${instanceId}`,
7157
- email,
7158
- userName,
7159
- firstName,
7160
- lastName,
7183
+ const { id: userId, email, roles } = identifyData.user;
7184
+ const { name: tenantID, customProperties, domainName } = identifyData.currentTenant;
7185
+ const { instanceId, versionUI, versionBE } = identifyData;
7186
+ /**
7187
+ * Passing ID is a minimum required data to make an identify call to Gainsight.
7188
+ * isUserCreatedAfterAnonymizationWasActivated parameter is passed to later distinguish between users created before and after data anonymization done by Gainsight.
7189
+ * tenantID Used to distinguish between tenants when same email is used for different tenants.
7190
+ *
7191
+ * Due to GS limitations (GS does not allow clearing user attr/preferences via the GS tag!),
7192
+ * we always need to initialize fields related to PII to prevent leaking this data to GS when the user has disabled functional cookies.
7193
+ */
7194
+ const requiredIdentify = {
7195
+ /**
7196
+ * Email was not mandatory form field until 10.14
7197
+ */
7198
+ id: email ? email : `${userId}_${tenantID}_${instanceId}`,
7199
+ isUserCreatedAfterAnonymizationWasActivated: true,
7200
+ tenantID: tenantID,
7201
+ email: '--',
7202
+ userName: '--',
7203
+ firstName: '--',
7204
+ lastName: '--',
7161
7205
  domainName,
7162
7206
  versionUI,
7163
7207
  versionBE,
7164
7208
  userLanguage: this.translateService.currentLang,
7209
+ browserLanguage: this.translateService.getBrowserLang(),
7165
7210
  instanceId,
7166
- externalReference,
7167
- userRoles: this.transformUserRolesToStr(roles?.references)
7168
- }, {
7169
- id: `${name}_${instanceId}`,
7170
- instanceId
7171
- });
7211
+ externalReference: customProperties?.externalReference,
7212
+ userRoles: this.transformUserRolesToStr(roles?.references),
7213
+ customBranding: this.isCustomBranding(),
7214
+ fullTracking: sendPiiData
7215
+ };
7216
+ if (sendPiiData) {
7217
+ const { userName, firstName, lastName } = identifyData.user;
7218
+ const extendedIdentify = {
7219
+ ...requiredIdentify,
7220
+ email,
7221
+ userName,
7222
+ firstName,
7223
+ lastName
7224
+ };
7225
+ windowRef[this.GAINSIGHT_GLOBAL_SCOPE]('identify', extendedIdentify, {
7226
+ id: `${tenantID}_${instanceId}`,
7227
+ instanceId
7228
+ });
7229
+ return;
7230
+ }
7231
+ windowRef[this.GAINSIGHT_GLOBAL_SCOPE]('identify', requiredIdentify);
7172
7232
  }
7233
+ /**
7234
+ * Triggers an event to be recorded by Gainsight PX.
7235
+ * This method calls the Gainsight PX's tracking mechanism to log a specific event
7236
+ * along with its associated properties.
7237
+ * @param eventName - Name of the event to be triggered.
7238
+ * @param props - Optional properties associated with the event.
7239
+ */
7173
7240
  triggerEvent(eventName, props) {
7174
7241
  if (this.tagFunction && eventName) {
7175
7242
  eventName = this.prepareEventName(eventName);
@@ -7191,38 +7258,51 @@ class GainsightService {
7191
7258
  return this.getEnTranslation(textToTranslate, this.cachedRevertedTranslations);
7192
7259
  }
7193
7260
  /**
7194
- * Checks if the Gainsight's tag should be loaded.
7195
- * The decision to load Gainsight will depend on custom properties and functional cookies.
7196
- * @param customProperties Tenant's customProperties.
7261
+ * Determines whether personally identifiable information (PII) should be sent while loading a tag.
7262
+ * The decision to activate Gainsight and send PII relies on whether the cookiePreferences option is defined in the application settings,
7263
+ * if the functional cookie is enabled, and if the user grants permission.
7197
7264
  */
7198
- shouldLoadGainsightTag(customProperties) {
7265
+ async shouldSendPiiData() {
7199
7266
  return (this.cookieBannerService.isConfigCookiePreferencesDefined() &&
7200
7267
  this.cookieBannerService.isFunctionalCookieEnabled() &&
7201
- !this.isGainsightDisabled(customProperties) &&
7202
- !this.isCustomBranding());
7268
+ !(await this.isGainsightPreferenceDisabledInUserPreferences(this.USER_PREFERENCES_GAINSIGHT_KEY)));
7203
7269
  }
7270
+ /**
7271
+ * Updates a specific user attribute in the Gainsight global scope.
7272
+ * This method interfaces with the Gainsight global object to set a user's specific attribute with a provided value.
7273
+ * @param name - Name of the user attribute to be updated.
7274
+ * @param value - Value to set for the specified user attribute.
7275
+ */
7204
7276
  updateUserAttribute(name, value) {
7205
7277
  window[this.GAINSIGHT_GLOBAL_SCOPE]?.('set', 'user', { [name]: value });
7206
7278
  }
7279
+ /**
7280
+ * Determines if the current user has the capability to modify Gainsight PX settings.
7281
+ *
7282
+ * This method checks multiple conditions:
7283
+ * 1. Whether tracking has been disabled globally via application options.
7284
+ * 2. Whether Gainsight is disabled at the tenant level through custom properties.
7285
+ * 3. Whether a Gainsight key is available, either currently loaded or fetched asynchronously.
7286
+ * 4. Whether cookie preferences are defined and available for the user.
7287
+ *
7288
+ * @returns Promise that resolves to a boolean. True indicates the user can edit product experience settings, and false otherwise.
7289
+ */
7207
7290
  async canEditProductExperienceSettings() {
7208
7291
  const currentTenant = this.appState.currentTenant.value;
7209
7292
  const { customProperties } = currentTenant;
7293
+ if (this.isTrackingDisabled() ||
7294
+ this.isGainsightDisabledAtTenantCustomProperties(customProperties)) {
7295
+ return false;
7296
+ }
7210
7297
  const gainsightKey = !!this.gainsightKey || !!(await this.getGainsightKey());
7211
7298
  return (gainsightKey &&
7212
7299
  this.cookieBannerService.isConfigCookiePreferencesDefined() &&
7213
- !this.isGainsightDisabled(customProperties) &&
7214
- !!this.cookieBannerService.getUserCookiePreferences() &&
7215
- !this.isCustomBranding());
7216
- }
7217
- switchGainsightEngagementsVisibility(showGainsightEngagements) {
7218
- if (showGainsightEngagements) {
7219
- this.removeHidingStyle(this.HIDE_GAINSIGHT_BOT_STYLE_ID);
7220
- this.updateUserAttribute(this.ENGAGEMENTS, true);
7221
- return;
7222
- }
7223
- this.addHidingStyle(this.HIDE_GAINSIGHT_BOT_STYLE_ID, '#apt-widget { display:none }');
7224
- this.updateUserAttribute(this.ENGAGEMENTS, false);
7300
+ !!this.cookieBannerService.getUserCookiePreferences());
7225
7301
  }
7302
+ /**
7303
+ * Sets the global context for Gainsight with the current application name.
7304
+ * The global context can be utilized by Gainsight for various purposes, such as segmenting users.
7305
+ */
7226
7306
  setGlobalContext() {
7227
7307
  const currentAppState = this.appState.state$.value;
7228
7308
  const currentAppName = currentAppState.app.name;
@@ -7234,18 +7314,31 @@ class GainsightService {
7234
7314
  }
7235
7315
  return flatMap(userRoles, (userRole) => userRole.role.name).join();
7236
7316
  }
7237
- addHidingStyle(styleId, textContent) {
7238
- if (this.document.getElementById(styleId)) {
7239
- return;
7240
- }
7241
- const style = this.document.createElement('style');
7242
- style.id = styleId;
7243
- style.textContent = textContent;
7244
- this.document.head.appendChild(style);
7317
+ /**
7318
+ * Checks if Gainsight is disabled based on tenant custom properties.
7319
+ *
7320
+ * @param customProperties - The custom properties of the tenant.
7321
+ * @returns {boolean} - True if Gainsight is disabled, false otherwise.
7322
+ */
7323
+ isGainsightDisabledAtTenantCustomProperties(customProperties) {
7324
+ const gainsightEnabled = customProperties && customProperties.gainsightEnabled;
7325
+ return gainsightEnabled === false;
7245
7326
  }
7246
- removeHidingStyle(styleId) {
7247
- const style = this.document.getElementById(styleId);
7248
- style?.remove();
7327
+ /**
7328
+ * Determines if custom branding is enabled based on the presence of a brand logo.
7329
+ *
7330
+ * @returns {boolean} - True if custom branding is applied, false otherwise.
7331
+ */
7332
+ isCustomBranding() {
7333
+ const brandingCssVars = this.options.get('brandingCssVars') || {};
7334
+ return !!brandingCssVars['brand-logo-img'];
7335
+ }
7336
+ /**
7337
+ * Determines if tracking is disabled based on the application options.
7338
+ * @returns `true` if tracking is disabled, otherwise `false`.
7339
+ */
7340
+ isTrackingDisabled() {
7341
+ return this.options.disableTracking === true;
7249
7342
  }
7250
7343
  prepareEventName(baseEventName) {
7251
7344
  return baseEventName
@@ -7256,14 +7349,6 @@ class GainsightService {
7256
7349
  return eventNamePart.replace(/`[\w\W]*`/g, '');
7257
7350
  }
7258
7351
  }
7259
- isGainsightDisabled(customProperties) {
7260
- const gainsightEnabled = customProperties && customProperties.gainsightEnabled;
7261
- return gainsightEnabled === false;
7262
- }
7263
- isCustomBranding() {
7264
- const brandingCssVars = this.options.get('brandingCssVars') || {};
7265
- return !!brandingCssVars['brand-logo-img'];
7266
- }
7267
7352
  loadScriptTag(scriptTag, key) {
7268
7353
  try {
7269
7354
  const windowRef = window;
@@ -7326,17 +7411,14 @@ class GainsightService {
7326
7411
  return enTranslation;
7327
7412
  }
7328
7413
  }
7329
- GainsightService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: GainsightService, deps: [{ token: DOCUMENT }, { token: AppStateService }, { token: OptionsService }, { token: CookieBannerService }, { token: UserPreferencesService }, { token: i1$3.TranslateService }], target: i0.ɵɵFactoryTarget.Injectable });
7414
+ GainsightService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: GainsightService, deps: [{ token: AppStateService }, { token: OptionsService }, { token: CookieBannerService }, { token: UserPreferencesService }, { token: i1$3.TranslateService }], target: i0.ɵɵFactoryTarget.Injectable });
7330
7415
  GainsightService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: GainsightService, providedIn: 'root' });
7331
7416
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: GainsightService, decorators: [{
7332
7417
  type: Injectable,
7333
7418
  args: [{
7334
7419
  providedIn: 'root'
7335
7420
  }]
7336
- }], ctorParameters: function () { return [{ type: DOCUMENT, decorators: [{
7337
- type: Inject,
7338
- args: [DOCUMENT]
7339
- }] }, { type: AppStateService }, { type: OptionsService }, { type: CookieBannerService }, { type: UserPreferencesService }, { type: i1$3.TranslateService }]; } });
7421
+ }], ctorParameters: function () { return [{ type: AppStateService }, { type: OptionsService }, { type: CookieBannerService }, { type: UserPreferencesService }, { type: i1$3.TranslateService }]; } });
7340
7422
 
7341
7423
  /**
7342
7424
  * This component is used as the outlet to show the action bars.
@@ -12299,6 +12381,171 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImpor
12299
12381
  type: Injectable
12300
12382
  }], ctorParameters: function () { return [{ type: i1$8.BsModalService }]; } });
12301
12383
 
12384
+ class UserEngagementsService {
12385
+ constructor(document, userPreferencesService, gainsightService) {
12386
+ this.document = document;
12387
+ this.userPreferencesService = userPreferencesService;
12388
+ this.gainsightService = gainsightService;
12389
+ this.USER_PREFERENCES_GAINSIGHT_ENGAGEMENTS_KEY = 'gainsightBotEnabled';
12390
+ this.userEngagementsEnabled$ = new BehaviorSubject(false);
12391
+ this.HIDE_GAINSIGHT_BOT_STYLE_ID = 'hide-gs-bot';
12392
+ this.ENGAGEMENTS = 'engagements';
12393
+ this.handleUserEngagements();
12394
+ }
12395
+ /**
12396
+ * Handles user engagement settings based on various conditions.
12397
+ *
12398
+ * - Waits for the Gainsight tracking to be loaded.
12399
+ * - Retrieves the engagement settings.
12400
+ * - Updates the engagement settings based on the combined observations.
12401
+ * - Finally, toggles the Gainsight engagements based on the latest `userEngagementsEnabled$` value.
12402
+ */
12403
+ handleUserEngagements() {
12404
+ this.gainsightService.trackingLoaded$
12405
+ .pipe(take(1), switchMap(() => this.getEngagementSettingsObservable()), tap((settings) => this.updateUserEngagementSettings(...settings)), switchMap(() => this.userEngagementsEnabled$.pipe(take(1))))
12406
+ .subscribe(isEnabled => this.toggleGainsightEngagements(isEnabled));
12407
+ }
12408
+ /**
12409
+ * Updates the user's preference for Gainsight Engagements.
12410
+ * @param {boolean} isEnabled - The new value for the user's engagement preference.
12411
+ */
12412
+ updateUserEngagementPreference(isEnabled) {
12413
+ this.userEngagementsEnabled$.next(isEnabled);
12414
+ this.userPreferencesService.set(this.gainsightService.USER_PREFERENCES_GAINSIGHT_ENGAGEMENTS_KEY, this.userEngagementsEnabled$.value);
12415
+ }
12416
+ /**
12417
+ * Toggles the visibility of Gainsight Engagements based on the provided flag.
12418
+ *
12419
+ * @param isEnabled - A flag indicating whether Gainsight Engagements should be visible.
12420
+ */
12421
+ toggleGainsightEngagements(isEnabled) {
12422
+ isEnabled ? this.showGainsightEngagements() : this.hideGainsightEngagements();
12423
+ }
12424
+ /**
12425
+ * Constructs an observable that emits an array of boolean values representing
12426
+ * the current engagement settings. The observable combines the latest values from:
12427
+ *
12428
+ * 1. User's preferences for Gainsight engagements.
12429
+ * 2. A flag indicating if PII data should be sent.
12430
+ * 3. A flag indicating if the platform uses custom branding.
12431
+ *
12432
+ * @returns An observable emitting an array of boolean values.
12433
+ */
12434
+ getEngagementSettingsObservable() {
12435
+ return combineLatest([
12436
+ this.userPreferencesService.observe(this.USER_PREFERENCES_GAINSIGHT_ENGAGEMENTS_KEY),
12437
+ from(this.gainsightService.shouldSendPiiData()),
12438
+ of(this.gainsightService.isCustomBranding())
12439
+ ]);
12440
+ }
12441
+ /**
12442
+ * Updates user engagement settings based on provided preferences and settings.
12443
+ *
12444
+ * Based on the received values, the method decides to:
12445
+ * 1. Disable user engagements if PII data should not be shared or certain branding/settings conditions are met.
12446
+ * 2. Update the user engagement preference if the user engagement bot setting is undefined.
12447
+ *
12448
+ * @param userEngagementBotSetting - The user's setting for the engagement bot.
12449
+ * @param shouldSendPiiData - Indicates whether PII data should be shared.
12450
+ * @param hasCustomBranding - Indicates if custom branding is applied.
12451
+ */
12452
+ updateUserEngagementSettings(userEngagementBotSetting, shouldSendPiiData, hasCustomBranding) {
12453
+ if (this.shouldDisableUserEngagementsDueToPIIData(shouldSendPiiData)) {
12454
+ this.userEngagementsEnabled$.next(false);
12455
+ }
12456
+ else if (this.isUserEngagementBotSettingUndefined(userEngagementBotSetting)) {
12457
+ /**
12458
+ * Case where the user is new (freshly created) and has not changed the user engagement settings in the user edit modal (untouched state).
12459
+ * When custom branding is not set, we will set the user engagements in the user preferences to true by default.
12460
+ */
12461
+ this.updateUserEngagementPreference(!hasCustomBranding);
12462
+ }
12463
+ else {
12464
+ this.userEngagementsEnabled$.next(userEngagementBotSetting);
12465
+ }
12466
+ }
12467
+ /**
12468
+ * Determines whether user engagements should be disabled due to PII data settings.
12469
+ *
12470
+ * If the `shouldSendPiiData` parameter is false, this indicates that the user engagements
12471
+ * should be disabled to prevent sharing personally identifiable information.
12472
+ *
12473
+ * @param {boolean} shouldSendPiiData - Indicates whether PII data is allowed to be sent.
12474
+ * @returns {boolean} Returns true if user engagements should be disabled, otherwise false.
12475
+ */
12476
+ shouldDisableUserEngagementsDueToPIIData(shouldSendPiiData) {
12477
+ return !shouldSendPiiData;
12478
+ }
12479
+ /**
12480
+ * Determines if the user engagement bot setting is undefined.
12481
+ *
12482
+ * @param {boolean | undefined} userEngagementBotSetting - The setting value to check.
12483
+ * @returns {boolean} Returns `true` if the setting is undefined; otherwise, `false`.
12484
+ *
12485
+ * This scenario occurs when a user is new and hasn't modified the bot settings in the user details UI yet.
12486
+ */
12487
+ isUserEngagementBotSettingUndefined(userEngagementBotSetting) {
12488
+ return userEngagementBotSetting === undefined;
12489
+ }
12490
+ /**
12491
+ * Enables the visibility of Gainsight engagements.
12492
+ *
12493
+ * This method removes the CSS styles that hide the Gainsight engagements
12494
+ * and updates the relevant user attribute to mark the engagements as visible.
12495
+ */
12496
+ showGainsightEngagements() {
12497
+ this.removeHidingStyle(this.HIDE_GAINSIGHT_BOT_STYLE_ID);
12498
+ this.gainsightService.updateUserAttribute(this.ENGAGEMENTS, true);
12499
+ }
12500
+ /**
12501
+ * Hides the Gainsight engagements.
12502
+ *
12503
+ * This method applies CSS styles to hide the Gainsight engagements
12504
+ * and updates the relevant user attribute to mark the engagements as hidden.
12505
+ */
12506
+ hideGainsightEngagements() {
12507
+ this.addHidingStyle(this.HIDE_GAINSIGHT_BOT_STYLE_ID, '#apt-widget { display:none }');
12508
+ this.gainsightService.updateUserAttribute(this.ENGAGEMENTS, false);
12509
+ }
12510
+ /**
12511
+ * Removes the specified CSS style from the document.
12512
+ *
12513
+ * @param {string} styleId - The ID of the CSS style element to remove.
12514
+ */
12515
+ removeHidingStyle(styleId) {
12516
+ const style = this.document.getElementById(styleId);
12517
+ style?.remove();
12518
+ }
12519
+ /**
12520
+ * Adds a new CSS style to the document.
12521
+ *
12522
+ * If the style with the specified ID already exists, the method will do nothing.
12523
+ * Otherwise, it creates a new `<style>` element with the given ID and content,
12524
+ * then appends it to the document head.
12525
+ *
12526
+ * @param {string} styleId - The ID to assign to the new style element.
12527
+ * @param {string} textContent - The CSS rules to be included in the style.
12528
+ */
12529
+ addHidingStyle(styleId, textContent) {
12530
+ if (this.document.getElementById(styleId)) {
12531
+ return;
12532
+ }
12533
+ const style = this.document.createElement('style');
12534
+ style.id = styleId;
12535
+ style.textContent = textContent;
12536
+ this.document.head.appendChild(style);
12537
+ }
12538
+ }
12539
+ UserEngagementsService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: UserEngagementsService, deps: [{ token: DOCUMENT }, { token: UserPreferencesService }, { token: GainsightService }], target: i0.ɵɵFactoryTarget.Injectable });
12540
+ UserEngagementsService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: UserEngagementsService, providedIn: 'root' });
12541
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: UserEngagementsService, decorators: [{
12542
+ type: Injectable,
12543
+ args: [{ providedIn: 'root' }]
12544
+ }], ctorParameters: function () { return [{ type: DOCUMENT, decorators: [{
12545
+ type: Inject,
12546
+ args: [DOCUMENT]
12547
+ }] }, { type: UserPreferencesService }, { type: GainsightService }]; } });
12548
+
12302
12549
  class TotpChallengeComponent {
12303
12550
  constructor(loginService, users, alert) {
12304
12551
  this.loginService = loginService;
@@ -12685,7 +12932,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImpor
12685
12932
  }] } });
12686
12933
 
12687
12934
  class UserEditComponent {
12688
- constructor(state, translate, bsModalService, alert, userService, tenantLoginOptionsService, tenantService, userPreferencesService, gainsightService) {
12935
+ constructor(state, translate, bsModalService, alert, userService, tenantLoginOptionsService, tenantService) {
12689
12936
  this.state = state;
12690
12937
  this.translate = translate;
12691
12938
  this.bsModalService = bsModalService;
@@ -12693,17 +12940,15 @@ class UserEditComponent {
12693
12940
  this.userService = userService;
12694
12941
  this.tenantLoginOptionsService = tenantLoginOptionsService;
12695
12942
  this.tenantService = tenantService;
12696
- this.userPreferencesService = userPreferencesService;
12697
- this.gainsightService = gainsightService;
12698
12943
  this.loading = false;
12699
- this.showProductUsageSetting = false;
12944
+ this.showProductExperienceOptions = false;
12700
12945
  this.isUsageTrackingEnabled = true;
12701
- this.isGainsightEngagementsEnabled = true;
12946
+ this.isUserEngagementPreferenceEnabled = true;
12702
12947
  this.onUser = new EventEmitter();
12703
- this.onLanguage = new EventEmitter();
12704
- this.onProductExperience = new EventEmitter();
12705
- this.onGainsightEngagements = new EventEmitter();
12948
+ this.onUsageTrackingChange = new EventEmitter();
12949
+ this.onUserEngagementPreferenceChange = new EventEmitter();
12706
12950
  this.onCancel = new EventEmitter();
12951
+ this.onLanguage = new EventEmitter();
12707
12952
  this.userHasActiveTotp = false;
12708
12953
  this.userCanSetupTotp = false;
12709
12954
  this.isPhoneRequired = false;
@@ -12727,16 +12972,6 @@ class UserEditComponent {
12727
12972
  this.isPhoneRequired = true;
12728
12973
  }
12729
12974
  }
12730
- async onEnablingProductUsageTracking(isUsageTrackingEnabled) {
12731
- if (isUsageTrackingEnabled && this.isGainsightEngagementsEnabled === undefined) {
12732
- this.isGainsightEngagementsEnabled = await this.userPreferencesService
12733
- .get(this.gainsightService.USER_PREFERENCES_GAINSIGHT_ENGAGEMENTS_KEY)
12734
- .toPromise();
12735
- }
12736
- }
12737
- get langs() {
12738
- return this.state.state.langs;
12739
- }
12740
12975
  setupTotp() {
12741
12976
  this.bsModalService.show(UserTotpSetupComponent, {
12742
12977
  class: 'modal-sm',
@@ -12752,15 +12987,22 @@ class UserEditComponent {
12752
12987
  if (this.loading) {
12753
12988
  return;
12754
12989
  }
12755
- if (this.showProductUsageSetting) {
12756
- this.onProductExperience.emit(this.isUsageTrackingEnabled);
12757
- this.onGainsightEngagements.emit(this.isGainsightEngagementsEnabled);
12990
+ if (this.showProductExperienceOptions) {
12991
+ this.onUsageTrackingChange.emit(this.isUsageTrackingEnabled);
12992
+ /**
12993
+ * Emits a user engagement preference change event.
12994
+ * If usage tracking is disabled, it emits `false`. Otherwise, it emits the current state of the user engagement preference.
12995
+ */
12996
+ this.onUserEngagementPreferenceChange.emit(this.isUsageTrackingEnabled === false ? false : this.isUserEngagementPreferenceEnabled);
12758
12997
  }
12759
12998
  this.onUser.emit(this._user);
12760
12999
  }
12761
13000
  onNewPasswordChanged(newPassword) {
12762
13001
  this._user.password = newPassword.password;
12763
13002
  }
13003
+ get langs() {
13004
+ return this.state.state.langs;
13005
+ }
12764
13006
  async initializeTotpSettings() {
12765
13007
  try {
12766
13008
  this.userCanSetupTotp = await this.canUserSetupTotp();
@@ -12779,37 +13021,37 @@ class UserEditComponent {
12779
13021
  return loginOptions.some(({ tfaStrategy = '' }) => tfaStrategy.toLowerCase() === 'totp');
12780
13022
  }
12781
13023
  }
12782
- UserEditComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: UserEditComponent, deps: [{ token: AppStateService }, { token: TranslateService }, { token: i1$8.BsModalService }, { token: AlertService }, { token: i1$2.UserService }, { token: i1$2.TenantLoginOptionsService }, { token: i1$2.TenantService }, { token: UserPreferencesService }, { token: GainsightService }], target: i0.ɵɵFactoryTarget.Component });
12783
- UserEditComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.6", type: UserEditComponent, selector: "c8y-user-edit", inputs: { lang: "lang", loading: "loading", user: "user", showProductUsageSetting: "showProductUsageSetting", isUsageTrackingEnabled: "isUsageTrackingEnabled", isGainsightEngagementsEnabled: "isGainsightEngagementsEnabled" }, outputs: { onUser: "onUser", onLanguage: "onLanguage", onProductExperience: "onProductExperience", onGainsightEngagements: "onGainsightEngagements", onCancel: "onCancel" }, ngImport: i0, template: "<form #userForm=\"ngForm\" (ngSubmit)=\"userForm.form.valid && save()\">\n <div class=\"d-block p-24 p-b-0\">\n <div class=\"alert alert-warning\" role=\"alert\" *ngIf=\"userIsExternal\" translate>\n Some of the user settings are not editable here because they are managed via your\n authorization server.\n </div>\n <c8y-form-group>\n <label translate for=\"userName\">Username (for example, email)</label>\n <input\n id=\"userName\"\n class=\"form-control\"\n [(ngModel)]=\"user.userName\"\n name=\"userName\"\n autocomplete=\"off\"\n required\n maxlength=\"254\"\n placeholder=\"{{ 'e.g. joe.doe@example.com`LOCALIZE`' | translate }}\"\n [disabled]=\"user.id\"\n c8yDefaultValidation=\"user\"\n />\n </c8y-form-group>\n\n <c8y-form-group>\n <label translate for=\"displayName\">Login alias</label>\n <input\n id=\"displayName\"\n class=\"form-control\"\n [(ngModel)]=\"user.displayName\"\n name=\"displayName\"\n autocomplete=\"off\"\n maxlength=\"254\"\n placeholder=\"{{ 'e.g. joe.doe`LOCALIZE`' | translate }}\"\n [disabled]=\"userIsExternal\"\n c8yDefaultValidation=\"loginAlias\"\n />\n </c8y-form-group>\n\n <c8y-form-group [hasWarning]=\"!user.email\">\n <label translate for=\"userEmail\">Email</label>\n <input\n id=\"userEmail\"\n class=\"form-control\"\n type=\"email\"\n name=\"email\"\n [maxlength]=\"254\"\n autocomplete=\"off\"\n placeholder=\"{{ 'e.g. joe.doe@example.com`LOCALIZE`' | translate }}\"\n [(ngModel)]=\"user.email\"\n email\n [required]=\"true\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n\n <div class=\"row\">\n <div class=\"col-sm-6\">\n <c8y-form-group>\n <label translate for=\"userFirstName\">First name</label>\n <input\n id=\"userFirstName\"\n class=\"form-control\"\n autocomplete=\"off\"\n maxlength=\"50\"\n name=\"firstName\"\n [(ngModel)]=\"user.firstName\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n </div>\n <div class=\"col-sm-6\">\n <c8y-form-group>\n <label translate for=\"userLastName\">Last name</label>\n <input\n id=\"userLastName\"\n class=\"form-control\"\n autocomplete=\"off\"\n maxlength=\"50\"\n name=\"lastName\"\n [(ngModel)]=\"user.lastName\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n </div>\n </div>\n\n <c8y-form-group>\n <label translate for=\"userTelephone\">Telephone</label>\n <input\n id=\"userTelephone\"\n class=\"form-control\"\n autocomplete=\"off\"\n name=\"phone\"\n maxlength=\"254\"\n [(ngModel)]=\"user.phone\"\n placeholder=\"{{ 'e.g. +49 9 876 543 210`LOCALIZE`' | translate }}\"\n c8yPhoneValidation\n [required]=\"isPhoneRequired\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n\n <c8y-form-group>\n <label translate for=\"userLang\">Language</label>\n <div class=\"c8y-select-wrapper\">\n <select\n id=\"userLang\"\n class=\"form-control\"\n #selectLang\n name=\"lang\"\n [(ngModel)]=\"lang\"\n (change)=\"onLanguage.emit(selectLang.value)\"\n >\n <option *ngFor=\"let lang of langs\" [value]=\"lang\">\n {{ translate.getNativeLanguage(lang) }}\n </option>\n </select>\n <span></span>\n </div>\n </c8y-form-group>\n\n <c8y-form-group class=\"p-t-16 separator-top\" *ngIf=\"showProductUsageSetting\">\n <label translate>Product experience</label>\n <label class=\"c8y-switch\" for=\"productUsageTracking\">\n <input\n id=\"productUsageTracking\"\n name=\"productUsageTracking\"\n type=\"checkbox\"\n (change)=\"onEnablingProductUsageTracking(isUsageTrackingEnabled)\"\n [(ngModel)]=\"isUsageTrackingEnabled\"\n />\n <span></span>\n {{ 'Enable tracking to enhance the product experience' | translate }}\n </label>\n <ng-container *ngIf=\"isUsageTrackingEnabled\">\n <label class=\"c8y-switch m-l-0\" for=\"gainsightEngagements\">\n <input\n id=\"gainsightEngagements\"\n name=\"gainsightEngagements\"\n type=\"checkbox\"\n [(ngModel)]=\"isGainsightEngagementsEnabled\"\n />\n <span></span>\n {{ 'Enable in-product information & communication' | translate }}\n </label>\n </ng-container>\n </c8y-form-group>\n\n <div class=\"form-group p-t-16 separator-top\" *ngIf=\"!userIsExternal\">\n <label class=\"control-label\">{{ 'Login options' | translate }}</label>\n <c8y-new-password (password)=\"onNewPasswordChanged($event)\"></c8y-new-password>\n <button\n title=\"{{ 'Set up two-factor authentication' | translate }}\"\n class=\"btn btn-default\"\n type=\"button\"\n (click)=\"setupTotp()\"\n *ngIf=\"userCanSetupTotp && !userHasActiveTotp && isTfaEnabled\"\n >\n {{ 'Set up two-factor authentication' | translate }}\n </button>\n </div>\n\n <c8y-form-group *ngIf=\"!!(state.state$ | async).newsletter\">\n <label translate>Newsletter</label>\n <label\n title=\"{{ 'Send me information about outages, maintenance or updates.' | translate }}\"\n class=\"c8y-checkbox\"\n >\n <input\n type=\"checkbox\"\n name=\"newsletter\"\n [(ngModel)]=\"user.newsletter\"\n [disabled]=\"userIsExternal\"\n />\n <span></span>\n <span>\n {{ 'Send me information about outages, maintenance or updates.' | translate }}\n </span>\n </label>\n </c8y-form-group>\n </div>\n <div class=\"modal-footer separator-top bg-level-0 sticky-bottom\">\n <button\n title=\"{{ 'Cancel' | translate }}\"\n class=\"btn btn-default\"\n type=\"button\"\n (click)=\"cancel()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n title=\"{{ 'Save' | translate }}\"\n class=\"btn btn-primary\"\n type=\"submit\"\n [disabled]=\"!userForm.form.valid || userForm.form.pristine || loading\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n</form>\n", dependencies: [{ kind: "directive", type: PhoneValidationDirective, selector: "[c8yPhoneValidation]" }, { kind: "directive", type: DefaultValidationDirective, selector: "[c8yDefaultValidation]", inputs: ["c8yDefaultValidation"] }, { kind: "directive", type: i2$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2$2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2$2.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i2$2.EmailValidator, selector: "[email][formControlName],[email][formControl],[email][ngModel]", inputs: ["email"] }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2$2.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: NewPasswordComponent, selector: "c8y-new-password", outputs: ["password"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }] });
13024
+ UserEditComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: UserEditComponent, deps: [{ token: AppStateService }, { token: TranslateService }, { token: i1$8.BsModalService }, { token: AlertService }, { token: i1$2.UserService }, { token: i1$2.TenantLoginOptionsService }, { token: i1$2.TenantService }], target: i0.ɵɵFactoryTarget.Component });
13025
+ UserEditComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.6", type: UserEditComponent, selector: "c8y-user-edit", inputs: { lang: "lang", loading: "loading", user: "user", showProductExperienceOptions: "showProductExperienceOptions", isUsageTrackingEnabled: "isUsageTrackingEnabled", isUserEngagementPreferenceEnabled: "isUserEngagementPreferenceEnabled" }, outputs: { onUser: "onUser", onUsageTrackingChange: "onUsageTrackingChange", onUserEngagementPreferenceChange: "onUserEngagementPreferenceChange", onCancel: "onCancel", onLanguage: "onLanguage" }, ngImport: i0, template: "<form #userForm=\"ngForm\" (ngSubmit)=\"userForm.form.valid && save()\">\n <div class=\"d-block p-24 p-b-0\">\n <div class=\"alert alert-warning\" role=\"alert\" *ngIf=\"userIsExternal\" translate>\n Some of the user settings are not editable here because they are managed via your\n authorization server.\n </div>\n <c8y-form-group>\n <label translate for=\"userName\">Username (for example, email)</label>\n <input\n id=\"userName\"\n class=\"form-control\"\n [(ngModel)]=\"user.userName\"\n name=\"userName\"\n autocomplete=\"off\"\n required\n maxlength=\"254\"\n placeholder=\"{{ 'e.g. joe.doe@example.com`LOCALIZE`' | translate }}\"\n [disabled]=\"user.id\"\n c8yDefaultValidation=\"user\"\n />\n </c8y-form-group>\n\n <c8y-form-group>\n <label translate for=\"displayName\">Login alias</label>\n <input\n id=\"displayName\"\n class=\"form-control\"\n [(ngModel)]=\"user.displayName\"\n name=\"displayName\"\n autocomplete=\"off\"\n maxlength=\"254\"\n placeholder=\"{{ 'e.g. joe.doe`LOCALIZE`' | translate }}\"\n [disabled]=\"userIsExternal\"\n c8yDefaultValidation=\"loginAlias\"\n />\n </c8y-form-group>\n\n <c8y-form-group [hasWarning]=\"!user.email\">\n <label translate for=\"userEmail\">Email</label>\n <input\n id=\"userEmail\"\n class=\"form-control\"\n type=\"email\"\n name=\"email\"\n [maxlength]=\"254\"\n autocomplete=\"off\"\n placeholder=\"{{ 'e.g. joe.doe@example.com`LOCALIZE`' | translate }}\"\n [(ngModel)]=\"user.email\"\n email\n [required]=\"true\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n\n <div class=\"row\">\n <div class=\"col-sm-6\">\n <c8y-form-group>\n <label translate for=\"userFirstName\">First name</label>\n <input\n id=\"userFirstName\"\n class=\"form-control\"\n autocomplete=\"off\"\n maxlength=\"50\"\n name=\"firstName\"\n [(ngModel)]=\"user.firstName\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n </div>\n <div class=\"col-sm-6\">\n <c8y-form-group>\n <label translate for=\"userLastName\">Last name</label>\n <input\n id=\"userLastName\"\n class=\"form-control\"\n autocomplete=\"off\"\n maxlength=\"50\"\n name=\"lastName\"\n [(ngModel)]=\"user.lastName\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n </div>\n </div>\n\n <c8y-form-group>\n <label translate for=\"userTelephone\">Telephone</label>\n <input\n id=\"userTelephone\"\n class=\"form-control\"\n autocomplete=\"off\"\n name=\"phone\"\n maxlength=\"254\"\n [(ngModel)]=\"user.phone\"\n placeholder=\"{{ 'e.g. +49 9 876 543 210`LOCALIZE`' | translate }}\"\n c8yPhoneValidation\n [required]=\"isPhoneRequired\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n\n <c8y-form-group>\n <label translate for=\"userLang\">Language</label>\n <div class=\"c8y-select-wrapper\">\n <select\n id=\"userLang\"\n class=\"form-control\"\n #selectLang\n name=\"lang\"\n [(ngModel)]=\"lang\"\n (change)=\"onLanguage.emit(selectLang.value)\"\n >\n <option *ngFor=\"let lang of langs\" [value]=\"lang\">\n {{ translate.getNativeLanguage(lang) }}\n </option>\n </select>\n <span></span>\n </div>\n </c8y-form-group>\n\n <c8y-form-group class=\"p-t-16 separator-top\" *ngIf=\"showProductExperienceOptions\">\n <label translate>Product experience</label>\n <label class=\"c8y-switch\" for=\"productUsageTracking\">\n <input\n id=\"productUsageTracking\"\n name=\"productUsageTracking\"\n type=\"checkbox\"\n [(ngModel)]=\"isUsageTrackingEnabled\"\n />\n <span></span>\n {{ 'Enable personalized product experience tracking' | translate }}\n </label>\n <ng-container *ngIf=\"isUsageTrackingEnabled\">\n <label class=\"c8y-switch m-l-0\" for=\"userEngagementPreference\">\n <input\n id=\"userEngagementPreference\"\n name=\"userEngagementPreference\"\n type=\"checkbox\"\n [(ngModel)]=\"isUserEngagementPreferenceEnabled\"\n />\n <span></span>\n {{ 'Enable in-product information & communication' | translate }}\n </label>\n </ng-container>\n </c8y-form-group>\n\n <div class=\"form-group p-t-16 separator-top\" *ngIf=\"!userIsExternal\">\n <label class=\"control-label\">{{ 'Login options' | translate }}</label>\n <c8y-new-password (password)=\"onNewPasswordChanged($event)\"></c8y-new-password>\n <button\n title=\"{{ 'Set up two-factor authentication' | translate }}\"\n class=\"btn btn-default\"\n type=\"button\"\n (click)=\"setupTotp()\"\n *ngIf=\"userCanSetupTotp && !userHasActiveTotp && isTfaEnabled\"\n >\n {{ 'Set up two-factor authentication' | translate }}\n </button>\n </div>\n\n <c8y-form-group *ngIf=\"!!(state.state$ | async).newsletter\">\n <label translate>Newsletter</label>\n <label\n title=\"{{ 'Send me information about outages, maintenance or updates.' | translate }}\"\n class=\"c8y-checkbox\"\n >\n <input\n type=\"checkbox\"\n name=\"newsletter\"\n [(ngModel)]=\"user.newsletter\"\n [disabled]=\"userIsExternal\"\n />\n <span></span>\n <span>\n {{ 'Send me information about outages, maintenance or updates.' | translate }}\n </span>\n </label>\n </c8y-form-group>\n </div>\n <div class=\"modal-footer separator-top bg-level-0 sticky-bottom\">\n <button\n title=\"{{ 'Cancel' | translate }}\"\n class=\"btn btn-default\"\n type=\"button\"\n (click)=\"cancel()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n title=\"{{ 'Save' | translate }}\"\n class=\"btn btn-primary\"\n type=\"submit\"\n [disabled]=\"!userForm.form.valid || userForm.form.pristine || loading\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n</form>\n", dependencies: [{ kind: "directive", type: PhoneValidationDirective, selector: "[c8yPhoneValidation]" }, { kind: "directive", type: DefaultValidationDirective, selector: "[c8yDefaultValidation]", inputs: ["c8yDefaultValidation"] }, { kind: "directive", type: i2$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2$2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2$2.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i2$2.EmailValidator, selector: "[email][formControlName],[email][formControl],[email][ngModel]", inputs: ["email"] }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2$2.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: NewPasswordComponent, selector: "c8y-new-password", outputs: ["password"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }] });
12784
13026
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: UserEditComponent, decorators: [{
12785
13027
  type: Component,
12786
- args: [{ selector: 'c8y-user-edit', template: "<form #userForm=\"ngForm\" (ngSubmit)=\"userForm.form.valid && save()\">\n <div class=\"d-block p-24 p-b-0\">\n <div class=\"alert alert-warning\" role=\"alert\" *ngIf=\"userIsExternal\" translate>\n Some of the user settings are not editable here because they are managed via your\n authorization server.\n </div>\n <c8y-form-group>\n <label translate for=\"userName\">Username (for example, email)</label>\n <input\n id=\"userName\"\n class=\"form-control\"\n [(ngModel)]=\"user.userName\"\n name=\"userName\"\n autocomplete=\"off\"\n required\n maxlength=\"254\"\n placeholder=\"{{ 'e.g. joe.doe@example.com`LOCALIZE`' | translate }}\"\n [disabled]=\"user.id\"\n c8yDefaultValidation=\"user\"\n />\n </c8y-form-group>\n\n <c8y-form-group>\n <label translate for=\"displayName\">Login alias</label>\n <input\n id=\"displayName\"\n class=\"form-control\"\n [(ngModel)]=\"user.displayName\"\n name=\"displayName\"\n autocomplete=\"off\"\n maxlength=\"254\"\n placeholder=\"{{ 'e.g. joe.doe`LOCALIZE`' | translate }}\"\n [disabled]=\"userIsExternal\"\n c8yDefaultValidation=\"loginAlias\"\n />\n </c8y-form-group>\n\n <c8y-form-group [hasWarning]=\"!user.email\">\n <label translate for=\"userEmail\">Email</label>\n <input\n id=\"userEmail\"\n class=\"form-control\"\n type=\"email\"\n name=\"email\"\n [maxlength]=\"254\"\n autocomplete=\"off\"\n placeholder=\"{{ 'e.g. joe.doe@example.com`LOCALIZE`' | translate }}\"\n [(ngModel)]=\"user.email\"\n email\n [required]=\"true\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n\n <div class=\"row\">\n <div class=\"col-sm-6\">\n <c8y-form-group>\n <label translate for=\"userFirstName\">First name</label>\n <input\n id=\"userFirstName\"\n class=\"form-control\"\n autocomplete=\"off\"\n maxlength=\"50\"\n name=\"firstName\"\n [(ngModel)]=\"user.firstName\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n </div>\n <div class=\"col-sm-6\">\n <c8y-form-group>\n <label translate for=\"userLastName\">Last name</label>\n <input\n id=\"userLastName\"\n class=\"form-control\"\n autocomplete=\"off\"\n maxlength=\"50\"\n name=\"lastName\"\n [(ngModel)]=\"user.lastName\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n </div>\n </div>\n\n <c8y-form-group>\n <label translate for=\"userTelephone\">Telephone</label>\n <input\n id=\"userTelephone\"\n class=\"form-control\"\n autocomplete=\"off\"\n name=\"phone\"\n maxlength=\"254\"\n [(ngModel)]=\"user.phone\"\n placeholder=\"{{ 'e.g. +49 9 876 543 210`LOCALIZE`' | translate }}\"\n c8yPhoneValidation\n [required]=\"isPhoneRequired\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n\n <c8y-form-group>\n <label translate for=\"userLang\">Language</label>\n <div class=\"c8y-select-wrapper\">\n <select\n id=\"userLang\"\n class=\"form-control\"\n #selectLang\n name=\"lang\"\n [(ngModel)]=\"lang\"\n (change)=\"onLanguage.emit(selectLang.value)\"\n >\n <option *ngFor=\"let lang of langs\" [value]=\"lang\">\n {{ translate.getNativeLanguage(lang) }}\n </option>\n </select>\n <span></span>\n </div>\n </c8y-form-group>\n\n <c8y-form-group class=\"p-t-16 separator-top\" *ngIf=\"showProductUsageSetting\">\n <label translate>Product experience</label>\n <label class=\"c8y-switch\" for=\"productUsageTracking\">\n <input\n id=\"productUsageTracking\"\n name=\"productUsageTracking\"\n type=\"checkbox\"\n (change)=\"onEnablingProductUsageTracking(isUsageTrackingEnabled)\"\n [(ngModel)]=\"isUsageTrackingEnabled\"\n />\n <span></span>\n {{ 'Enable tracking to enhance the product experience' | translate }}\n </label>\n <ng-container *ngIf=\"isUsageTrackingEnabled\">\n <label class=\"c8y-switch m-l-0\" for=\"gainsightEngagements\">\n <input\n id=\"gainsightEngagements\"\n name=\"gainsightEngagements\"\n type=\"checkbox\"\n [(ngModel)]=\"isGainsightEngagementsEnabled\"\n />\n <span></span>\n {{ 'Enable in-product information & communication' | translate }}\n </label>\n </ng-container>\n </c8y-form-group>\n\n <div class=\"form-group p-t-16 separator-top\" *ngIf=\"!userIsExternal\">\n <label class=\"control-label\">{{ 'Login options' | translate }}</label>\n <c8y-new-password (password)=\"onNewPasswordChanged($event)\"></c8y-new-password>\n <button\n title=\"{{ 'Set up two-factor authentication' | translate }}\"\n class=\"btn btn-default\"\n type=\"button\"\n (click)=\"setupTotp()\"\n *ngIf=\"userCanSetupTotp && !userHasActiveTotp && isTfaEnabled\"\n >\n {{ 'Set up two-factor authentication' | translate }}\n </button>\n </div>\n\n <c8y-form-group *ngIf=\"!!(state.state$ | async).newsletter\">\n <label translate>Newsletter</label>\n <label\n title=\"{{ 'Send me information about outages, maintenance or updates.' | translate }}\"\n class=\"c8y-checkbox\"\n >\n <input\n type=\"checkbox\"\n name=\"newsletter\"\n [(ngModel)]=\"user.newsletter\"\n [disabled]=\"userIsExternal\"\n />\n <span></span>\n <span>\n {{ 'Send me information about outages, maintenance or updates.' | translate }}\n </span>\n </label>\n </c8y-form-group>\n </div>\n <div class=\"modal-footer separator-top bg-level-0 sticky-bottom\">\n <button\n title=\"{{ 'Cancel' | translate }}\"\n class=\"btn btn-default\"\n type=\"button\"\n (click)=\"cancel()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n title=\"{{ 'Save' | translate }}\"\n class=\"btn btn-primary\"\n type=\"submit\"\n [disabled]=\"!userForm.form.valid || userForm.form.pristine || loading\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n</form>\n" }]
12787
- }], ctorParameters: function () { return [{ type: AppStateService }, { type: TranslateService }, { type: i1$8.BsModalService }, { type: AlertService }, { type: i1$2.UserService }, { type: i1$2.TenantLoginOptionsService }, { type: i1$2.TenantService }, { type: UserPreferencesService }, { type: GainsightService }]; }, propDecorators: { lang: [{
13028
+ args: [{ selector: 'c8y-user-edit', template: "<form #userForm=\"ngForm\" (ngSubmit)=\"userForm.form.valid && save()\">\n <div class=\"d-block p-24 p-b-0\">\n <div class=\"alert alert-warning\" role=\"alert\" *ngIf=\"userIsExternal\" translate>\n Some of the user settings are not editable here because they are managed via your\n authorization server.\n </div>\n <c8y-form-group>\n <label translate for=\"userName\">Username (for example, email)</label>\n <input\n id=\"userName\"\n class=\"form-control\"\n [(ngModel)]=\"user.userName\"\n name=\"userName\"\n autocomplete=\"off\"\n required\n maxlength=\"254\"\n placeholder=\"{{ 'e.g. joe.doe@example.com`LOCALIZE`' | translate }}\"\n [disabled]=\"user.id\"\n c8yDefaultValidation=\"user\"\n />\n </c8y-form-group>\n\n <c8y-form-group>\n <label translate for=\"displayName\">Login alias</label>\n <input\n id=\"displayName\"\n class=\"form-control\"\n [(ngModel)]=\"user.displayName\"\n name=\"displayName\"\n autocomplete=\"off\"\n maxlength=\"254\"\n placeholder=\"{{ 'e.g. joe.doe`LOCALIZE`' | translate }}\"\n [disabled]=\"userIsExternal\"\n c8yDefaultValidation=\"loginAlias\"\n />\n </c8y-form-group>\n\n <c8y-form-group [hasWarning]=\"!user.email\">\n <label translate for=\"userEmail\">Email</label>\n <input\n id=\"userEmail\"\n class=\"form-control\"\n type=\"email\"\n name=\"email\"\n [maxlength]=\"254\"\n autocomplete=\"off\"\n placeholder=\"{{ 'e.g. joe.doe@example.com`LOCALIZE`' | translate }}\"\n [(ngModel)]=\"user.email\"\n email\n [required]=\"true\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n\n <div class=\"row\">\n <div class=\"col-sm-6\">\n <c8y-form-group>\n <label translate for=\"userFirstName\">First name</label>\n <input\n id=\"userFirstName\"\n class=\"form-control\"\n autocomplete=\"off\"\n maxlength=\"50\"\n name=\"firstName\"\n [(ngModel)]=\"user.firstName\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n </div>\n <div class=\"col-sm-6\">\n <c8y-form-group>\n <label translate for=\"userLastName\">Last name</label>\n <input\n id=\"userLastName\"\n class=\"form-control\"\n autocomplete=\"off\"\n maxlength=\"50\"\n name=\"lastName\"\n [(ngModel)]=\"user.lastName\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n </div>\n </div>\n\n <c8y-form-group>\n <label translate for=\"userTelephone\">Telephone</label>\n <input\n id=\"userTelephone\"\n class=\"form-control\"\n autocomplete=\"off\"\n name=\"phone\"\n maxlength=\"254\"\n [(ngModel)]=\"user.phone\"\n placeholder=\"{{ 'e.g. +49 9 876 543 210`LOCALIZE`' | translate }}\"\n c8yPhoneValidation\n [required]=\"isPhoneRequired\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n\n <c8y-form-group>\n <label translate for=\"userLang\">Language</label>\n <div class=\"c8y-select-wrapper\">\n <select\n id=\"userLang\"\n class=\"form-control\"\n #selectLang\n name=\"lang\"\n [(ngModel)]=\"lang\"\n (change)=\"onLanguage.emit(selectLang.value)\"\n >\n <option *ngFor=\"let lang of langs\" [value]=\"lang\">\n {{ translate.getNativeLanguage(lang) }}\n </option>\n </select>\n <span></span>\n </div>\n </c8y-form-group>\n\n <c8y-form-group class=\"p-t-16 separator-top\" *ngIf=\"showProductExperienceOptions\">\n <label translate>Product experience</label>\n <label class=\"c8y-switch\" for=\"productUsageTracking\">\n <input\n id=\"productUsageTracking\"\n name=\"productUsageTracking\"\n type=\"checkbox\"\n [(ngModel)]=\"isUsageTrackingEnabled\"\n />\n <span></span>\n {{ 'Enable personalized product experience tracking' | translate }}\n </label>\n <ng-container *ngIf=\"isUsageTrackingEnabled\">\n <label class=\"c8y-switch m-l-0\" for=\"userEngagementPreference\">\n <input\n id=\"userEngagementPreference\"\n name=\"userEngagementPreference\"\n type=\"checkbox\"\n [(ngModel)]=\"isUserEngagementPreferenceEnabled\"\n />\n <span></span>\n {{ 'Enable in-product information & communication' | translate }}\n </label>\n </ng-container>\n </c8y-form-group>\n\n <div class=\"form-group p-t-16 separator-top\" *ngIf=\"!userIsExternal\">\n <label class=\"control-label\">{{ 'Login options' | translate }}</label>\n <c8y-new-password (password)=\"onNewPasswordChanged($event)\"></c8y-new-password>\n <button\n title=\"{{ 'Set up two-factor authentication' | translate }}\"\n class=\"btn btn-default\"\n type=\"button\"\n (click)=\"setupTotp()\"\n *ngIf=\"userCanSetupTotp && !userHasActiveTotp && isTfaEnabled\"\n >\n {{ 'Set up two-factor authentication' | translate }}\n </button>\n </div>\n\n <c8y-form-group *ngIf=\"!!(state.state$ | async).newsletter\">\n <label translate>Newsletter</label>\n <label\n title=\"{{ 'Send me information about outages, maintenance or updates.' | translate }}\"\n class=\"c8y-checkbox\"\n >\n <input\n type=\"checkbox\"\n name=\"newsletter\"\n [(ngModel)]=\"user.newsletter\"\n [disabled]=\"userIsExternal\"\n />\n <span></span>\n <span>\n {{ 'Send me information about outages, maintenance or updates.' | translate }}\n </span>\n </label>\n </c8y-form-group>\n </div>\n <div class=\"modal-footer separator-top bg-level-0 sticky-bottom\">\n <button\n title=\"{{ 'Cancel' | translate }}\"\n class=\"btn btn-default\"\n type=\"button\"\n (click)=\"cancel()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n title=\"{{ 'Save' | translate }}\"\n class=\"btn btn-primary\"\n type=\"submit\"\n [disabled]=\"!userForm.form.valid || userForm.form.pristine || loading\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n</form>\n" }]
13029
+ }], ctorParameters: function () { return [{ type: AppStateService }, { type: TranslateService }, { type: i1$8.BsModalService }, { type: AlertService }, { type: i1$2.UserService }, { type: i1$2.TenantLoginOptionsService }, { type: i1$2.TenantService }]; }, propDecorators: { lang: [{
12788
13030
  type: Input
12789
13031
  }], loading: [{
12790
13032
  type: Input
12791
13033
  }], user: [{
12792
13034
  type: Input
12793
- }], showProductUsageSetting: [{
13035
+ }], showProductExperienceOptions: [{
12794
13036
  type: Input
12795
13037
  }], isUsageTrackingEnabled: [{
12796
13038
  type: Input
12797
- }], isGainsightEngagementsEnabled: [{
13039
+ }], isUserEngagementPreferenceEnabled: [{
12798
13040
  type: Input
12799
13041
  }], onUser: [{
12800
13042
  type: Output
12801
- }], onLanguage: [{
13043
+ }], onUsageTrackingChange: [{
12802
13044
  type: Output
12803
- }], onProductExperience: [{
12804
- type: Output
12805
- }], onGainsightEngagements: [{
13045
+ }], onUserEngagementPreferenceChange: [{
12806
13046
  type: Output
12807
13047
  }], onCancel: [{
12808
13048
  type: Output
13049
+ }], onLanguage: [{
13050
+ type: Output
12809
13051
  }] } });
12810
13052
 
12811
13053
  class UserEditModalComponent {
12812
- constructor(modal, user, ui, auth, client, alert, translate, userPreferences, c8yModalService, gainsightService, cookieBannerService, loginService, passwordService) {
13054
+ constructor(modal, user, ui, auth, client, alert, translate, userPreferences, c8yModalService, gainsightService, cookieBannerService, loginService, passwordService, userEngagementsService) {
12813
13055
  this.modal = modal;
12814
13056
  this.user = user;
12815
13057
  this.ui = ui;
@@ -12823,20 +13065,34 @@ class UserEditModalComponent {
12823
13065
  this.cookieBannerService = cookieBannerService;
12824
13066
  this.loginService = loginService;
12825
13067
  this.passwordService = passwordService;
13068
+ this.userEngagementsService = userEngagementsService;
12826
13069
  this.loading = false;
12827
- this.showProductUsageSetting = false;
13070
+ this.showProductExperienceOptions = false;
12828
13071
  this.lang = this.ui.state.lang;
12829
13072
  }
12830
13073
  async ngOnInit() {
12831
13074
  this.updateUserInAppState();
12832
- this.showProductUsageSetting = await this.gainsightService.canEditProductExperienceSettings();
12833
- if (this.showProductUsageSetting) {
12834
- if (this.cookieBannerService.isFunctionalCookieEnabled()) {
12835
- this.currentUsageTrackingState =
12836
- !(await this.gainsightService.isGainsightPreferenceDisabledInUserPreferences(this.gainsightService.USER_PREFERENCES_GAINSIGHT_KEY));
12837
- this.currentGainsightEngagementsState =
12838
- !(await this.gainsightService.isGainsightPreferenceDisabledInUserPreferences(this.gainsightService.USER_PREFERENCES_GAINSIGHT_ENGAGEMENTS_KEY));
12839
- }
13075
+ await this.setInitialProductExperienceOptions();
13076
+ }
13077
+ /**
13078
+ * Initializes product experience options for the user.
13079
+ *
13080
+ * This function performs the following operations:
13081
+ * - Determines if the user has the permission to edit product experience options.
13082
+ * - If the user has the permission and functional cookies are enabled:
13083
+ * - Checks whether personalized product experience tracking is active.
13084
+ * - Checks whether in-product information and communication is active.
13085
+ */
13086
+ async setInitialProductExperienceOptions() {
13087
+ this.showProductExperienceOptions =
13088
+ await this.gainsightService.canEditProductExperienceSettings();
13089
+ if (this.showProductExperienceOptions && this.cookieBannerService.isFunctionalCookieEnabled()) {
13090
+ // Enable personalized product experience tracking option
13091
+ this.currentUsageTrackingState =
13092
+ !(await this.gainsightService.isGainsightPreferenceDisabledInUserPreferences(this.gainsightService.USER_PREFERENCES_GAINSIGHT_KEY));
13093
+ // Enable in-product information & communication option
13094
+ this.currentUserEngagementPreferenceInitialState =
13095
+ this.userEngagementsService.userEngagementsEnabled$.value;
12840
13096
  }
12841
13097
  }
12842
13098
  async onDismiss() {
@@ -12849,11 +13105,11 @@ class UserEditModalComponent {
12849
13105
  this.changedLang = lang;
12850
13106
  await this.translate.switchToLanguage(this.changedLang);
12851
13107
  }
12852
- onProductExperience(option) {
12853
- this.usageTrackingState = option;
13108
+ onUsageTrackingChange(isEnabled) {
13109
+ this.usageTrackingState = isEnabled;
12854
13110
  }
12855
- onGainsightEngagements(option) {
12856
- this.gainsightEngagementsState = option;
13111
+ onUserEngagementPreferenceChange(isEnabled) {
13112
+ this.userEngagementPreferenceNewState = isEnabled;
12857
13113
  }
12858
13114
  async updateAndClose(user) {
12859
13115
  this.loading = true;
@@ -12875,16 +13131,7 @@ class UserEditModalComponent {
12875
13131
  if (this.changedLang && this.changedLang !== this.lang) {
12876
13132
  reloadRequired = await this.persistLanguage(this.changedLang);
12877
13133
  }
12878
- if (this.currentGainsightEngagementsState !== this.gainsightEngagementsState) {
12879
- await this.userPreferences.set(this.gainsightService.USER_PREFERENCES_GAINSIGHT_ENGAGEMENTS_KEY, this.gainsightEngagementsState);
12880
- }
12881
- if (this.currentUsageTrackingState !== this.usageTrackingState) {
12882
- await this.userPreferences.set(this.gainsightService.USER_PREFERENCES_GAINSIGHT_KEY, this.usageTrackingState);
12883
- this.gainsightService.setFunctionalCookie(this.usageTrackingState);
12884
- this.usageTrackingState
12885
- ? await this.gainsightService.loadTag(this.client.tenant)
12886
- : await this.gainsightTrackingAppReload();
12887
- }
13134
+ await this.updateProductExperienceOptions();
12888
13135
  if (user.customProperties.userOrigin !== 'OAUTH2') {
12889
13136
  await this.user.updateCurrent(omit(user, 'password'));
12890
13137
  await this.updateUserInAppState();
@@ -12936,6 +13183,34 @@ class UserEditModalComponent {
12936
13183
  // do nothing
12937
13184
  }
12938
13185
  }
13186
+ async updateProductExperienceOptions() {
13187
+ this.updateUserEngagementsPreference();
13188
+ await this.updateTrackingOption();
13189
+ }
13190
+ /**
13191
+ * Updates the user engagement preference if it has changed from the initial state.
13192
+ * Calls the user engagements service to update the preference.
13193
+ *
13194
+ * The update only occurs if the current preference differs from the new state.
13195
+ */
13196
+ updateUserEngagementsPreference() {
13197
+ if (this.currentUserEngagementPreferenceInitialState !== this.userEngagementPreferenceNewState) {
13198
+ this.userEngagementsService.updateUserEngagementPreference(this.userEngagementPreferenceNewState);
13199
+ }
13200
+ }
13201
+ /**
13202
+ * Asynchronously updates the tracking option for user preferences.
13203
+ * If the current usage tracking state differs from the new state,
13204
+ * it updates the Gainsight preferences and sets a functional cookie
13205
+ * before triggering a reload of the application.
13206
+ */
13207
+ async updateTrackingOption() {
13208
+ if (this.currentUsageTrackingState !== this.usageTrackingState) {
13209
+ await this.userPreferences.set(this.gainsightService.USER_PREFERENCES_GAINSIGHT_KEY, this.usageTrackingState);
13210
+ this.gainsightService.setFunctionalCookie(this.usageTrackingState);
13211
+ await this.gainsightTrackingAppReload();
13212
+ }
13213
+ }
12939
13214
  async updateUserInAppState() {
12940
13215
  const currentUserResult = await this.user.current();
12941
13216
  this.ui.currentUser.next(currentUserResult.data);
@@ -12949,12 +13224,12 @@ class UserEditModalComponent {
12949
13224
  this.auth.updateCredentials(newCredentials);
12950
13225
  }
12951
13226
  }
12952
- UserEditModalComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: UserEditModalComponent, deps: [{ token: i1$8.BsModalRef }, { token: i1$2.UserService }, { token: AppStateService }, { token: i1$2.BasicAuth }, { token: i1$2.FetchClient }, { token: AlertService }, { token: TranslateService }, { token: UserPreferencesService }, { token: ModalService }, { token: GainsightService }, { token: CookieBannerService }, { token: LoginService }, { token: PasswordService }], target: i0.ɵɵFactoryTarget.Component });
12953
- UserEditModalComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.6", type: UserEditModalComponent, selector: "c8y-user-edit-modal", ngImport: i0, template: "<c8y-modal [customFooter]=\"true\" [title]=\"'Edit user' | translate\" (onDismiss)=\"onDismiss()\">\n <c8y-user-edit\n [lang]=\"lang\"\n [user]=\"ui.currentUser | async\"\n [loading]=\"loading\"\n [isUsageTrackingEnabled]=\"currentUsageTrackingState\"\n [isGainsightEngagementsEnabled]=\"currentGainsightEngagementsState\"\n [showProductUsageSetting]=\"showProductUsageSetting\"\n (onLanguage)=\"onLanguage($event)\"\n (onProductExperience)=\"onProductExperience($event)\"\n (onGainsightEngagements)=\"onGainsightEngagements($event)\"\n (onUser)=\"updateAndClose($event)\"\n (onCancel)=\"onDismiss()\"\n >\n </c8y-user-edit>\n</c8y-modal>\n", dependencies: [{ kind: "component", type: ModalComponent, selector: "c8y-modal", inputs: ["disabled", "close", "dismiss", "title", "body", "customFooter", "headerClasses", "labels"], outputs: ["onDismiss", "onClose"] }, { kind: "component", type: UserEditComponent, selector: "c8y-user-edit", inputs: ["lang", "loading", "user", "showProductUsageSetting", "isUsageTrackingEnabled", "isGainsightEngagementsEnabled"], outputs: ["onUser", "onLanguage", "onProductExperience", "onGainsightEngagements", "onCancel"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }] });
13227
+ UserEditModalComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: UserEditModalComponent, deps: [{ token: i1$8.BsModalRef }, { token: i1$2.UserService }, { token: AppStateService }, { token: i1$2.BasicAuth }, { token: i1$2.FetchClient }, { token: AlertService }, { token: TranslateService }, { token: UserPreferencesService }, { token: ModalService }, { token: GainsightService }, { token: CookieBannerService }, { token: LoginService }, { token: PasswordService }, { token: UserEngagementsService }], target: i0.ɵɵFactoryTarget.Component });
13228
+ UserEditModalComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.6", type: UserEditModalComponent, selector: "c8y-user-edit-modal", ngImport: i0, template: "<c8y-modal\n [title]=\"'Edit user' | translate\"\n [customFooter]=\"true\"\n (onDismiss)=\"onDismiss()\"\n>\n <c8y-user-edit\n [lang]=\"lang\"\n [user]=\"ui.currentUser | async\"\n [loading]=\"loading\"\n [isUsageTrackingEnabled]=\"currentUsageTrackingState\"\n [isUserEngagementPreferenceEnabled]=\"currentUserEngagementPreferenceInitialState\"\n [showProductExperienceOptions]=\"showProductExperienceOptions\"\n (onUsageTrackingChange)=\"onUsageTrackingChange($event)\"\n (onUserEngagementPreferenceChange)=\"onUserEngagementPreferenceChange($event)\"\n (onUser)=\"updateAndClose($event)\"\n (onCancel)=\"onDismiss()\"\n (onLanguage)=\"onLanguage($event)\"\n ></c8y-user-edit>\n</c8y-modal>\n", dependencies: [{ kind: "component", type: ModalComponent, selector: "c8y-modal", inputs: ["disabled", "close", "dismiss", "title", "body", "customFooter", "headerClasses", "labels"], outputs: ["onDismiss", "onClose"] }, { kind: "component", type: UserEditComponent, selector: "c8y-user-edit", inputs: ["lang", "loading", "user", "showProductExperienceOptions", "isUsageTrackingEnabled", "isUserEngagementPreferenceEnabled"], outputs: ["onUser", "onUsageTrackingChange", "onUserEngagementPreferenceChange", "onCancel", "onLanguage"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }] });
12954
13229
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: UserEditModalComponent, decorators: [{
12955
13230
  type: Component,
12956
- args: [{ selector: 'c8y-user-edit-modal', template: "<c8y-modal [customFooter]=\"true\" [title]=\"'Edit user' | translate\" (onDismiss)=\"onDismiss()\">\n <c8y-user-edit\n [lang]=\"lang\"\n [user]=\"ui.currentUser | async\"\n [loading]=\"loading\"\n [isUsageTrackingEnabled]=\"currentUsageTrackingState\"\n [isGainsightEngagementsEnabled]=\"currentGainsightEngagementsState\"\n [showProductUsageSetting]=\"showProductUsageSetting\"\n (onLanguage)=\"onLanguage($event)\"\n (onProductExperience)=\"onProductExperience($event)\"\n (onGainsightEngagements)=\"onGainsightEngagements($event)\"\n (onUser)=\"updateAndClose($event)\"\n (onCancel)=\"onDismiss()\"\n >\n </c8y-user-edit>\n</c8y-modal>\n" }]
12957
- }], ctorParameters: function () { return [{ type: i1$8.BsModalRef }, { type: i1$2.UserService }, { type: AppStateService }, { type: i1$2.BasicAuth }, { type: i1$2.FetchClient }, { type: AlertService }, { type: TranslateService }, { type: UserPreferencesService }, { type: ModalService }, { type: GainsightService }, { type: CookieBannerService }, { type: LoginService }, { type: PasswordService }]; } });
13231
+ args: [{ selector: 'c8y-user-edit-modal', template: "<c8y-modal\n [title]=\"'Edit user' | translate\"\n [customFooter]=\"true\"\n (onDismiss)=\"onDismiss()\"\n>\n <c8y-user-edit\n [lang]=\"lang\"\n [user]=\"ui.currentUser | async\"\n [loading]=\"loading\"\n [isUsageTrackingEnabled]=\"currentUsageTrackingState\"\n [isUserEngagementPreferenceEnabled]=\"currentUserEngagementPreferenceInitialState\"\n [showProductExperienceOptions]=\"showProductExperienceOptions\"\n (onUsageTrackingChange)=\"onUsageTrackingChange($event)\"\n (onUserEngagementPreferenceChange)=\"onUserEngagementPreferenceChange($event)\"\n (onUser)=\"updateAndClose($event)\"\n (onCancel)=\"onDismiss()\"\n (onLanguage)=\"onLanguage($event)\"\n ></c8y-user-edit>\n</c8y-modal>\n" }]
13232
+ }], ctorParameters: function () { return [{ type: i1$8.BsModalRef }, { type: i1$2.UserService }, { type: AppStateService }, { type: i1$2.BasicAuth }, { type: i1$2.FetchClient }, { type: AlertService }, { type: TranslateService }, { type: UserPreferencesService }, { type: ModalService }, { type: GainsightService }, { type: CookieBannerService }, { type: LoginService }, { type: PasswordService }, { type: UserEngagementsService }]; } });
12958
13233
 
12959
13234
  class UserMenuItemComponent {
12960
13235
  constructor(userService) {
@@ -16594,50 +16869,68 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImpor
16594
16869
  /**
16595
16870
  * This module enables an tenant to activate the product experience
16596
16871
  * software [Gainsight](https://www.gainsight.com/product-experience/) to help
16597
- * and track user actions. Gainsight is only activated, if the tenant custom
16598
- * property `gainsightEnabled` is set to true.
16872
+ * and track user actions.
16599
16873
  */
16600
16874
  class ProductExperienceModule {
16601
- constructor(appState, gainsightService, cookieBannerService, userPreferencesService) {
16875
+ constructor(appState, gainsightService, cookieBannerService,
16876
+ // Don't remove it, otherwise UserEngagementsService won't be initialized.
16877
+ userEngagementsService) {
16602
16878
  this.appState = appState;
16603
16879
  this.gainsightService = gainsightService;
16604
16880
  this.cookieBannerService = cookieBannerService;
16605
- this.userPreferencesService = userPreferencesService;
16881
+ this.userEngagementsService = userEngagementsService;
16882
+ /**
16883
+ * Check if the Gainsight tracking is disabled in the application apptions. If so, exit early without processing further.
16884
+ */
16885
+ if (this.gainsightService.isTrackingDisabled()) {
16886
+ return;
16887
+ }
16888
+ this.toggleUserTrackingObservable();
16889
+ }
16890
+ /**
16891
+ * Observes several factors to determine the state of user tracking and manages the visibility of Gainsight engagements.
16892
+ * It watches for changes in the current tenant, the state of the cookie banner, and user's preferences for Gainsight engagements.
16893
+ *
16894
+ * 1. If the cookie banner is being displayed, it returns without making any changes.
16895
+ * 2. If Gainsight is disabled at the tenant level via custom properties, it returns without making any changes.
16896
+ * 3. If the conditions are met for loading the Gainsight tag, it loads the tag.
16897
+ */
16898
+ toggleUserTrackingObservable() {
16606
16899
  combineLatest([
16607
16900
  this.appState.currentTenant.pipe(filter(Boolean)),
16608
- this.cookieBannerService.isCookieBannerShowed$,
16609
- this.userPreferencesService.observe(this.gainsightService.USER_PREFERENCES_GAINSIGHT_ENGAGEMENTS_KEY)
16610
- ]).subscribe(async ([currentTenant, isCookieBannerShowed, showGainsightEngagements]) => {
16901
+ this.cookieBannerService.isCookieBannerShowed$
16902
+ ]).subscribe(async ([currentTenant, isCookieBannerShowed]) => {
16611
16903
  if (isCookieBannerShowed) {
16612
16904
  return;
16613
16905
  }
16614
16906
  const { customProperties } = currentTenant;
16615
- if (this.gainsightService.shouldLoadGainsightTag(customProperties) &&
16616
- !(await this.gainsightService.isGainsightPreferenceDisabledInUserPreferences(this.gainsightService.USER_PREFERENCES_GAINSIGHT_KEY))) {
16617
- this.gainsightService.loadTag(currentTenant);
16907
+ if (this.gainsightService.isGainsightDisabledAtTenantCustomProperties(customProperties)) {
16908
+ return;
16618
16909
  }
16619
- /**
16620
- * In case the user preference for Gainsight bot does not exist the default value is set to true and saved in user preferences
16621
- */
16622
- if (showGainsightEngagements === undefined) {
16623
- showGainsightEngagements = true;
16624
- userPreferencesService.set(this.gainsightService.USER_PREFERENCES_GAINSIGHT_ENGAGEMENTS_KEY, showGainsightEngagements);
16910
+ if (this.shouldLoadTag()) {
16911
+ await this.gainsightService.loadTag(currentTenant, await this.gainsightService.shouldSendPiiData());
16625
16912
  }
16626
- this.gainsightService.switchGainsightEngagementsVisibility(showGainsightEngagements);
16627
16913
  });
16628
16914
  }
16915
+ /**
16916
+ * Determines if a tracking tag should be loaded based on cookie preferences.
16917
+ * @returns `true` if user cookie preferences exist, otherwise `false`.
16918
+ */
16919
+ shouldLoadTag() {
16920
+ return !!this.cookieBannerService.getUserCookiePreferences();
16921
+ }
16629
16922
  }
16630
- ProductExperienceModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: ProductExperienceModule, deps: [{ token: AppStateService }, { token: GainsightService }, { token: CookieBannerService }, { token: UserPreferencesService }], target: i0.ɵɵFactoryTarget.NgModule });
16923
+ ProductExperienceModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: ProductExperienceModule, deps: [{ token: AppStateService }, { token: GainsightService }, { token: CookieBannerService }, { token: UserEngagementsService }], target: i0.ɵɵFactoryTarget.NgModule });
16631
16924
  ProductExperienceModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.0.6", ngImport: i0, type: ProductExperienceModule, declarations: [ProductExperienceDirective], exports: [ProductExperienceDirective] });
16632
- ProductExperienceModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: ProductExperienceModule, providers: [GainsightService] });
16925
+ ProductExperienceModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: ProductExperienceModule, providers: [GainsightService, UserEngagementsService] });
16633
16926
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: ProductExperienceModule, decorators: [{
16634
16927
  type: NgModule,
16635
16928
  args: [{
16636
16929
  declarations: [ProductExperienceDirective],
16637
16930
  exports: [ProductExperienceDirective],
16638
- providers: [GainsightService]
16931
+ providers: [GainsightService, UserEngagementsService]
16639
16932
  }]
16640
- }], ctorParameters: function () { return [{ type: AppStateService }, { type: GainsightService }, { type: CookieBannerService }, { type: UserPreferencesService }]; } });
16933
+ }], ctorParameters: function () { return [{ type: AppStateService }, { type: GainsightService }, { type: CookieBannerService }, { type: UserEngagementsService }]; } });
16641
16934
 
16642
16935
  class SearchComponent {
16643
16936
  constructor(searchService) {
@@ -27479,5 +27772,5 @@ class RealtimeMessage {
27479
27772
  * Generated bundle index. Do not edit.
27480
27773
  */
27481
27774
 
27482
- export { ACTIONS, AbstractConfigurationStrategy, ActionBarComponent, ActionBarItemComponent, ActionBarModule, ActionBarService, ActionComponent, ActionModule, ActionOutletComponent, ActionService, AlarmRealtimeService, AlertComponent, AlertDetailsComponent, AlertModule, AlertOutletBase, AlertOutletComponent, AlertService, AlertTextComponent, AppIconComponent, AppStateService, AppSwitcherComponent, AppSwitcherService, ApplicationOptions, ApplicationPluginStatus, AssetTypesModule, AssetTypesService, AuditLogComponent, AuditLogModule, AuthenticationModule, BackendVersionFactory, BaseColumn, BaseFilteringFormRendererComponent, BootstrapComponent, BootstrapModule, BreadcrumbComponent, BreadcrumbItemComponent, BreadcrumbModule, BreadcrumbOutletComponent, BreadcrumbService, BytesPipe, C8yJSONSchema, C8yStepper, C8yStepperButtons, C8yStepperIcon, C8yStepperProgress, C8yTranslateDirective, C8yTranslateModule, C8yTranslatePipe, C8yValidators, CachedLocaleDictionaryService, CellRendererComponent, CellRendererContext, CellRendererDefDirective, ChangePasswordComponent, ClipboardModule, ClipboardService, ColorService, ColumnDirective, CommonModule, ConditionalTabsOutletComponent, ConfigureCustomColumnComponent, ConfirmModalComponent, ContextRouteComponent, ContextRouteGuard, ContextRouteService, CookieBannerComponent, CoreModule, CredentialsComponent, CurrentPasswordModalComponent, CustomColumn, DATA_GRID_CONFIGURATION_CONTEXT, DATA_GRID_CONFIGURATION_CONTEXT_PROVIDER, DATA_GRID_CONFIGURATION_STRATEGY, DashboardChildActionComponent, DashboardChildChange, DashboardChildComponent, DashboardChildTitleComponent, DashboardComponent, DashboardModule, DataGridComponent, DataGridModule, DataGridService, DatapointLibraryValidationErrors, DatePickerComponent, DatePickerModule, DatePipe, DateTimePickerComponent, DateTimePickerModule, DefaultValidationDirective, DeviceBootstrapRealtimeService, DeviceService, DeviceStatusComponent, DeviceStatusModule, DismissAlertStrategy, DocsModule, DocsService, DropAreaComponent, DropAreaModule, DropdownDirectionDirective, DynamicBulkDetailsResolver, DynamicBulkIIdentifiedResolver, DynamicComponentAlert, DynamicComponentAlertAggregator, DynamicComponentComponent, DynamicComponentErrorStrategy, DynamicComponentModule, DynamicComponentService, DynamicDatapointsResolver, DynamicFormsModule, DynamicManagedObjectResolver, DynamicResolverService, ES_MAX_TIME_MILLISECONDS, EmailsValidatorDirective, EmptyComponent, EmptyStateComponent, EventRealtimeService, FilePickerComponent, FilePickerModule, FilesService, FilterInputComponent, FilteringActionType, FilteringFormRendererComponent, FilteringFormRendererContext, FilteringFormRendererDefDirective, ForOfDirective, FormGroupComponent, FormsModule, GENERIC_FILE_TYPE, GainsightService, GetGroupIconPipe, GridDataSource, GroupFragment, GroupService, GuideDocsComponent, GuideHrefDirective, HOOK_ACTION, HOOK_ACTION_BAR, HOOK_BREADCRUMB, HOOK_COMPONENTS, HOOK_DOCS, HOOK_DYNAMIC_PROVIDER_CONFIG, HOOK_NAVIGATOR_NODES, HOOK_OPTIONS, HOOK_PATTERN_MESSAGES, HOOK_ROUTE, HOOK_SEARCH, HOOK_STEPPER, HOOK_TABS, HOOK_VERSION, HOOK_WIZARD, HeaderBarComponent, HeaderCellRendererDefDirective, HeaderModule, HeaderService, HelpComponent, HelpModule, HighlightComponent, HookProviderTypes, HumanizeAppNamePipe, HumanizePipe, I18nModule$1 as I18nModule, ICONS, ICON_LIST, IconDirective, IfAllowedDirective, InjectionType, InputGroupListComponent, InputGroupListContainerDirective, InventorySearchService, IpRangeInputListComponent, IsControlVisiblePipe, JsonValidationPrettifierDirective, LANGUAGES, LOCALE_PATH, LegacyGridConfigMapperService, ListDisplaySwitchComponent, ListDisplaySwitchModule, ListGroupComponent, ListGroupModule, ListItemActionComponent, ListItemBodyComponent, ListItemCheckboxComponent, ListItemCollapseComponent, ListItemComponent, ListItemDragHandleComponent, ListItemFooterComponent, ListItemIconComponent, ListItemRadioComponent, ListItemTimelineComponent, LoadMoreComponent, LoadingComponent, LoginComponent, LoginModule, LoginService, LoginViews, MAX_PAGE_SIZE, MESSAGES, ManagedObjectRealtimeService, ManagedObjectType, MapFunctionPipe, MarkdownToHtmlPipe, MaxValidationDirective, MeasurementRealtimeService, MessageDirective, MessagesComponent, MinValidationDirective, MissingTranslationCustomHandler, ModalComponent, ModalModule, ModalSelectionMode, ModalService, NEEDED_ROLE_FOR_SETUP, NUMBER_FORMAT_REGEXP, NavigatorIconComponent, NavigatorModule, NavigatorNode, NavigatorNodeComponent, NavigatorNodeRoot, NavigatorOutletComponent, NavigatorService, NewPasswordComponent, NumberPipe, OperationBulkRealtimeService, OperationRealtimeService, OperationResultComponent, OptionsService, OutletDirective, PRODUCT_EXPERIENCE_EVENT_SOURCE, PX_ACTIONS, PX_EVENT_NAME, PasswordCheckListComponent, PasswordConfirm, PasswordConfirmModalComponent, PasswordService, PasswordStrengthCheckerService, PasswordStrengthComponent, PasswordStrengthValidatorDirective, PatternMessagesService, Permissions, PhoneValidationDirective, PlatformDetailsService, PluginsModule, PluginsResolveService, PluginsService, PopoverConfirmComponent, ProductExperienceDirective, ProductExperienceModule, ProgressBarComponent, PropertiesListComponent, PropertiesListModule, ProvidePhoneNumberComponent, ProviderConfigurationComponent, ProviderConfigurationModule, ProviderConfigurationNodeFactory, ProviderConfigurationRouteFactory, ProviderConfigurationService, ProviderDefinitionsService, PushStatus, PushStatusLabels, QuickLinkComponent, QuickLinkModule, RESOLVING_COMPONENT_WAIT_TIME, RangeComponent, RangeDirective, RangeDisplayComponent, RangeDisplayModule, RealtimeButtonComponent, RealtimeMessage, RealtimeModule, RealtimeService, RealtimeSubjectService, RecoverPasswordComponent, RequiredInputPlaceholderDirective, RightDrawerComponent, RouterModule, RouterService, RouterTabsResolver, SETUP_FINISHED_STEP_ID, SearchComponent, SearchFilters, SearchInputComponent, SearchModule, SearchOutletComponent, SearchResultEmptyComponent, SearchService, SelectComponent$1 as SelectComponent, SelectModalComponent, SelectModalFilterPipe, SelectModalModule, SelectModule, SendStatus, SendStatusLabels, SetupCompletedComponent, SetupComponent, SetupModule, SetupService, SetupState, SetupStepperFactory, ShortenUserNamePipe, ShouldShowMoPipe, ShowIfFilterPipe, SimpleJsonPathValidatorDirective, SkipLinkDirective, SmsChallengeComponent, StateService, Status, StepperModule, StepperOutletComponent, StepperService, Steppers, StrengthValidatorService, StringifyObjectPipe, TabComponent, TabsModule, TabsOutletComponent, TabsService, TabsetAriaDirective, TenantUiService, TextAreaRowHeightDirective, TextareaAutoresizeDirective, TitleComponent, TitleOutletComponent, TotpAuthComponent, TotpChallengeComponent, TotpSetupComponent, TranslateCustomLoader, TranslateParserCustom, TranslateService, TypeaheadComponent, UniqueInCollectionByPathValidationDirective, UserEditComponent, UserEditModalComponent, UserMenuItemComponent, UserMenuOutletComponent, UserMenuService, UserModule, UserNameInitialsPipe, UserPreferencesConfigurationStrategy, UserPreferencesService, UserPreferencesStorageInventory, UserPreferencesStorageLocal, UserTotpRevokeComponent, UserTotpSetupComponent, VERSION_MODULE_CONFIG, ValidationPattern, VersionDetailsModalComponent, VersionListComponent, VersionModule, VersionService, ViewContext, ViewContextServices, VirtualScrollWindowDirective, VirtualScrollWindowStrategy, VirtualScrollerWrapperComponent, WebSDKVersionFactory, WidgetTimeContextComponent, WidgetsDashboardComponent, WizardBodyComponent, WizardComponent, WizardFooterComponent, WizardHeaderComponent, WizardModalService, WizardModule, WizardOutletComponent, WizardService, ZipService, _, _virtualScrollWindowStrategyFactory, allEntriesAreEqual, deviceAvailabilityIconMap, extraRoutes, fromFactories, fromTrigger, fromTriggerOnce, getActivatedRoute, getAngularLocalesLanguageString, getBasicInputArrayFormFieldConfig, getInjectedHooks, gettext, hookAction, hookActionBar, hookBreadcrumb, hookComponent, hookDocs, hookDynamicProviderConfig, hookGeneric, hookNavigator, hookOptions, hookPatternMessages, hookRoute, hookSearch, hookStepper, hookTab, hookVersion, hookWizard, initializeServices, isEagerDynamicComponents, isExtensionFactory, isLazyDynamicComponents, isPromise, languagesFactory, loadLocale, localeId, localePathFactory, memoize, minColumnGridTrackSize, operationStatusClasses, operationStatusIcons, ratiosByColumnTypes, resolveInjectedFactories, retryWithDelay, simpleJsonPathValidator, sortByPriority, stateToFactory, statusAlert, statusClasses, statusIcons, throttle, toObservable, toObservableOfArrays, tooltips, translateLoaderFactory, trimTranslationKey, uniqueInCollectionByPathValidator };
27775
+ export { ACTIONS, AbstractConfigurationStrategy, ActionBarComponent, ActionBarItemComponent, ActionBarModule, ActionBarService, ActionComponent, ActionModule, ActionOutletComponent, ActionService, AlarmRealtimeService, AlertComponent, AlertDetailsComponent, AlertModule, AlertOutletBase, AlertOutletComponent, AlertService, AlertTextComponent, AppIconComponent, AppStateService, AppSwitcherComponent, AppSwitcherService, ApplicationOptions, ApplicationPluginStatus, AssetTypesModule, AssetTypesService, AuditLogComponent, AuditLogModule, AuthenticationModule, BackendVersionFactory, BaseColumn, BaseFilteringFormRendererComponent, BootstrapComponent, BootstrapModule, BreadcrumbComponent, BreadcrumbItemComponent, BreadcrumbModule, BreadcrumbOutletComponent, BreadcrumbService, BytesPipe, C8yJSONSchema, C8yStepper, C8yStepperButtons, C8yStepperIcon, C8yStepperProgress, C8yTranslateDirective, C8yTranslateModule, C8yTranslatePipe, C8yValidators, CachedLocaleDictionaryService, CellRendererComponent, CellRendererContext, CellRendererDefDirective, ChangePasswordComponent, ClipboardModule, ClipboardService, ColorService, ColumnDirective, CommonModule, ConditionalTabsOutletComponent, ConfigureCustomColumnComponent, ConfirmModalComponent, ContextRouteComponent, ContextRouteGuard, ContextRouteService, CookieBannerComponent, CoreModule, CredentialsComponent, CurrentPasswordModalComponent, CustomColumn, DATA_GRID_CONFIGURATION_CONTEXT, DATA_GRID_CONFIGURATION_CONTEXT_PROVIDER, DATA_GRID_CONFIGURATION_STRATEGY, DashboardChildActionComponent, DashboardChildChange, DashboardChildComponent, DashboardChildTitleComponent, DashboardComponent, DashboardModule, DataGridComponent, DataGridModule, DataGridService, DatapointLibraryValidationErrors, DatePickerComponent, DatePickerModule, DatePipe, DateTimePickerComponent, DateTimePickerModule, DefaultValidationDirective, DeviceBootstrapRealtimeService, DeviceService, DeviceStatusComponent, DeviceStatusModule, DismissAlertStrategy, DocsModule, DocsService, DropAreaComponent, DropAreaModule, DropdownDirectionDirective, DynamicBulkDetailsResolver, DynamicBulkIIdentifiedResolver, DynamicComponentAlert, DynamicComponentAlertAggregator, DynamicComponentComponent, DynamicComponentErrorStrategy, DynamicComponentModule, DynamicComponentService, DynamicDatapointsResolver, DynamicFormsModule, DynamicManagedObjectResolver, DynamicResolverService, ES_MAX_TIME_MILLISECONDS, EmailsValidatorDirective, EmptyComponent, EmptyStateComponent, EventRealtimeService, FilePickerComponent, FilePickerModule, FilesService, FilterInputComponent, FilteringActionType, FilteringFormRendererComponent, FilteringFormRendererContext, FilteringFormRendererDefDirective, ForOfDirective, FormGroupComponent, FormsModule, GENERIC_FILE_TYPE, GainsightService, GetGroupIconPipe, GridDataSource, GroupFragment, GroupService, GuideDocsComponent, GuideHrefDirective, HOOK_ACTION, HOOK_ACTION_BAR, HOOK_BREADCRUMB, HOOK_COMPONENTS, HOOK_DOCS, HOOK_DYNAMIC_PROVIDER_CONFIG, HOOK_NAVIGATOR_NODES, HOOK_OPTIONS, HOOK_PATTERN_MESSAGES, HOOK_ROUTE, HOOK_SEARCH, HOOK_STEPPER, HOOK_TABS, HOOK_VERSION, HOOK_WIZARD, HeaderBarComponent, HeaderCellRendererDefDirective, HeaderModule, HeaderService, HelpComponent, HelpModule, HighlightComponent, HookProviderTypes, HumanizeAppNamePipe, HumanizePipe, I18nModule$1 as I18nModule, ICONS, ICON_LIST, IconDirective, IfAllowedDirective, InjectionType, InputGroupListComponent, InputGroupListContainerDirective, InventorySearchService, IpRangeInputListComponent, IsControlVisiblePipe, JsonValidationPrettifierDirective, LANGUAGES, LOCALE_PATH, LegacyGridConfigMapperService, ListDisplaySwitchComponent, ListDisplaySwitchModule, ListGroupComponent, ListGroupModule, ListItemActionComponent, ListItemBodyComponent, ListItemCheckboxComponent, ListItemCollapseComponent, ListItemComponent, ListItemDragHandleComponent, ListItemFooterComponent, ListItemIconComponent, ListItemRadioComponent, ListItemTimelineComponent, LoadMoreComponent, LoadingComponent, LoginComponent, LoginModule, LoginService, LoginViews, MAX_PAGE_SIZE, MESSAGES, ManagedObjectRealtimeService, ManagedObjectType, MapFunctionPipe, MarkdownToHtmlPipe, MaxValidationDirective, MeasurementRealtimeService, MessageDirective, MessagesComponent, MinValidationDirective, MissingTranslationCustomHandler, ModalComponent, ModalModule, ModalSelectionMode, ModalService, NEEDED_ROLE_FOR_SETUP, NUMBER_FORMAT_REGEXP, NavigatorIconComponent, NavigatorModule, NavigatorNode, NavigatorNodeComponent, NavigatorNodeRoot, NavigatorOutletComponent, NavigatorService, NewPasswordComponent, NumberPipe, OperationBulkRealtimeService, OperationRealtimeService, OperationResultComponent, OptionsService, OutletDirective, PRODUCT_EXPERIENCE_EVENT_SOURCE, PX_ACTIONS, PX_EVENT_NAME, PasswordCheckListComponent, PasswordConfirm, PasswordConfirmModalComponent, PasswordService, PasswordStrengthCheckerService, PasswordStrengthComponent, PasswordStrengthValidatorDirective, PatternMessagesService, Permissions, PhoneValidationDirective, PlatformDetailsService, PluginsModule, PluginsResolveService, PluginsService, PopoverConfirmComponent, ProductExperienceDirective, ProductExperienceModule, ProgressBarComponent, PropertiesListComponent, PropertiesListModule, ProvidePhoneNumberComponent, ProviderConfigurationComponent, ProviderConfigurationModule, ProviderConfigurationNodeFactory, ProviderConfigurationRouteFactory, ProviderConfigurationService, ProviderDefinitionsService, PushStatus, PushStatusLabels, QuickLinkComponent, QuickLinkModule, RESOLVING_COMPONENT_WAIT_TIME, RangeComponent, RangeDirective, RangeDisplayComponent, RangeDisplayModule, RealtimeButtonComponent, RealtimeMessage, RealtimeModule, RealtimeService, RealtimeSubjectService, RecoverPasswordComponent, RequiredInputPlaceholderDirective, RightDrawerComponent, RouterModule, RouterService, RouterTabsResolver, SETUP_FINISHED_STEP_ID, SearchComponent, SearchFilters, SearchInputComponent, SearchModule, SearchOutletComponent, SearchResultEmptyComponent, SearchService, SelectComponent$1 as SelectComponent, SelectModalComponent, SelectModalFilterPipe, SelectModalModule, SelectModule, SendStatus, SendStatusLabels, SetupCompletedComponent, SetupComponent, SetupModule, SetupService, SetupState, SetupStepperFactory, ShortenUserNamePipe, ShouldShowMoPipe, ShowIfFilterPipe, SimpleJsonPathValidatorDirective, SkipLinkDirective, SmsChallengeComponent, StateService, Status, StepperModule, StepperOutletComponent, StepperService, Steppers, StrengthValidatorService, StringifyObjectPipe, TabComponent, TabsModule, TabsOutletComponent, TabsService, TabsetAriaDirective, TenantUiService, TextAreaRowHeightDirective, TextareaAutoresizeDirective, TitleComponent, TitleOutletComponent, TotpAuthComponent, TotpChallengeComponent, TotpSetupComponent, TranslateCustomLoader, TranslateParserCustom, TranslateService, TypeaheadComponent, UniqueInCollectionByPathValidationDirective, UserEditComponent, UserEditModalComponent, UserEngagementsService, UserMenuItemComponent, UserMenuOutletComponent, UserMenuService, UserModule, UserNameInitialsPipe, UserPreferencesConfigurationStrategy, UserPreferencesService, UserPreferencesStorageInventory, UserPreferencesStorageLocal, UserTotpRevokeComponent, UserTotpSetupComponent, VERSION_MODULE_CONFIG, ValidationPattern, VersionDetailsModalComponent, VersionListComponent, VersionModule, VersionService, ViewContext, ViewContextServices, VirtualScrollWindowDirective, VirtualScrollWindowStrategy, VirtualScrollerWrapperComponent, WebSDKVersionFactory, WidgetTimeContextComponent, WidgetsDashboardComponent, WizardBodyComponent, WizardComponent, WizardFooterComponent, WizardHeaderComponent, WizardModalService, WizardModule, WizardOutletComponent, WizardService, ZipService, _, _virtualScrollWindowStrategyFactory, allEntriesAreEqual, deviceAvailabilityIconMap, extraRoutes, fromFactories, fromTrigger, fromTriggerOnce, getActivatedRoute, getAngularLocalesLanguageString, getBasicInputArrayFormFieldConfig, getInjectedHooks, gettext, hookAction, hookActionBar, hookBreadcrumb, hookComponent, hookDocs, hookDynamicProviderConfig, hookGeneric, hookNavigator, hookOptions, hookPatternMessages, hookRoute, hookSearch, hookStepper, hookTab, hookVersion, hookWizard, initializeServices, isEagerDynamicComponents, isExtensionFactory, isLazyDynamicComponents, isPromise, languagesFactory, loadLocale, localeId, localePathFactory, memoize, minColumnGridTrackSize, operationStatusClasses, operationStatusIcons, ratiosByColumnTypes, resolveInjectedFactories, retryWithDelay, simpleJsonPathValidator, sortByPriority, stateToFactory, statusAlert, statusClasses, statusIcons, throttle, toObservable, toObservableOfArrays, tooltips, translateLoaderFactory, trimTranslationKey, uniqueInCollectionByPathValidator };
27483
27776
  //# sourceMappingURL=c8y-ngx-components.mjs.map