@newheap/platform-common 0.20.7 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,12 +6,11 @@ import * as i0 from '@angular/core';
6
6
  import { Injectable, PLATFORM_ID, REQUEST_CONTEXT, Inject, Optional, inject, NgZone, forwardRef, InjectionToken, afterNextRender, makeStateKey, Directive, input, Input, Component, viewChild, output, HostListener, EventEmitter, Pipe, HostBinding, ChangeDetectionStrategy, Output, ViewChild, ViewEncapsulation, model, ErrorHandler, provideAppInitializer, SkipSelf, NgModule } from '@angular/core';
7
7
  import * as i1$3 from '@angular/common';
8
8
  import { isPlatformServer, DOCUMENT, isPlatformBrowser, DatePipe, CommonModule } from '@angular/common';
9
- import { BehaviorSubject, lastValueFrom, tap, filter, map, Subject, share, switchMap, EMPTY, timer, from, Observable, of } from 'rxjs';
9
+ import { BehaviorSubject, lastValueFrom, tap, filter, map, Subject, share, switchMap, EMPTY, timer, exhaustMap, from, Subscription, defer, finalize as finalize$1, catchError, of, Observable } from 'rxjs';
10
10
  import * as i1 from 'ngx-cookie-service';
11
11
  import { DateTime } from 'luxon';
12
12
  import { Base64 } from 'js-base64';
13
13
  import { finalize, shareReplay, takeUntil, debounceTime, distinctUntilChanged } from 'rxjs/operators';
14
- import * as Sentry from '@sentry/angular';
15
14
  import * as i1$1 from '@angular/router';
16
15
  import { NavigationEnd, NavigationSkipped, NavigationSkippedCode, NavigationStart, Router, ActivatedRoute, RouterLink } from '@angular/router';
17
16
  import * as i2 from '@ngx-translate/core';
@@ -54,19 +53,13 @@ class UserNotificationNhCommonModuleConfig {
54
53
  Object.assign(this, init);
55
54
  }
56
55
  }
57
- class NhSentryErrorLoggingNhCommonModuleConfig {
56
+ class BackgroundOperationsNhCommonModuleConfig {
58
57
  constructor(init) {
59
- this.errorLoggingEnabled = false;
60
- this.tracingEnabled = false;
61
- this.beforeSendAddAuthServiceInformation = true;
62
- this.options = {};
63
- this.errorHandlerOptions = {};
64
- Object.assign(this, init);
65
- }
66
- }
67
- class NhErrorLoggingNhCommonModuleConfig {
68
- constructor(init) {
69
- this.sentry = new NhSentryErrorLoggingNhCommonModuleConfig();
58
+ this.urlSuffix = '/background-operations';
59
+ this.hubUrlSuffix = '/hub/background-operations';
60
+ this.pollingInterval = 5000;
61
+ this.liveUpdatesEnabled = true;
62
+ this.listPageSize = 100;
70
63
  Object.assign(this, init);
71
64
  }
72
65
  }
@@ -116,7 +109,7 @@ class NhCommonModuleConfig {
116
109
  this.defaultDateTimeFormat = 'dd-MM-yyyy HH:mm:ss';
117
110
  this.authentication = new AuthenticationNhCommonModuleConfig();
118
111
  this.userNotification = new UserNotificationNhCommonModuleConfig();
119
- this.errorLogging = new NhErrorLoggingNhCommonModuleConfig();
112
+ this.backgroundOperations = new BackgroundOperationsNhCommonModuleConfig();
120
113
  this.translation = new NhTranslationNhCommonModuleConfig();
121
114
  this.formDropdown = new NhFormDropDownNhCommonModuleConfig();
122
115
  this.http = new NhHttpNhCommonModuleConfig();
@@ -676,6 +669,51 @@ class NhUserNotificationData {
676
669
  }
677
670
  }
678
671
 
672
+ class NhBackgroundOperationCollectionHttpRequestOptions extends CollectionHttpRequestOptions {
673
+ constructor(init) {
674
+ super(init);
675
+ Object.assign(this, init);
676
+ }
677
+ }
678
+ function nhBackgroundOperationStatusName(status) {
679
+ if (typeof status === 'string') {
680
+ return status;
681
+ }
682
+ return {
683
+ 0: 'PendingDispatch',
684
+ 10: 'Queued',
685
+ 20: 'Running',
686
+ 25: 'WaitingForChildren',
687
+ 27: 'WaitingForSignal',
688
+ 30: 'CancelRequested',
689
+ 40: 'RetryScheduled',
690
+ 100: 'Succeeded',
691
+ 110: 'Failed',
692
+ 120: 'Cancelled',
693
+ 130: 'TimedOut'
694
+ }[status] ?? 'Unknown';
695
+ }
696
+ function nhBackgroundOperationStepStatusName(status) {
697
+ if (typeof status === 'string') {
698
+ return status;
699
+ }
700
+ return {
701
+ 0: 'Pending',
702
+ 10: 'Running',
703
+ 100: 'Succeeded',
704
+ 110: 'Failed',
705
+ 120: 'Skipped',
706
+ 130: 'Cancelled',
707
+ 140: 'Indeterminate'
708
+ }[status] ?? 'Unknown';
709
+ }
710
+ function nhBackgroundOperationTranslationSegment(value) {
711
+ return value
712
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
713
+ .replace(/[_\s]+/g, '-')
714
+ .toLowerCase();
715
+ }
716
+
679
717
  const languageToCultureMap = {
680
718
  'nl': 'nl-NL',
681
719
  'en': 'en-US',
@@ -2901,7 +2939,15 @@ class NhErrorHandlerService {
2901
2939
  }
2902
2940
  handleError(error) {
2903
2941
  const handlers = this.injector.get(NH_ERROR_HANDLERS, []);
2904
- handlers.forEach(handle => handle.handleError(error));
2942
+ for (const handler of handlers) {
2943
+ try {
2944
+ handler.handleError(error);
2945
+ }
2946
+ catch {
2947
+ // A failing telemetry provider must not prevent the remaining handlers
2948
+ // from observing the original application error.
2949
+ }
2950
+ }
2905
2951
  }
2906
2952
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhErrorHandlerService, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable }); }
2907
2953
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhErrorHandlerService, providedIn: 'root' }); }
@@ -2911,322 +2957,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImpo
2911
2957
  args: [{ providedIn: 'root' }]
2912
2958
  }], ctorParameters: () => [{ type: i0.Injector }] });
2913
2959
 
2914
- const NH_SENTRY_BEFORE_SEND_HOOKS = new InjectionToken("NH_SENTRY_BEFORE_SEND_HOOKS");
2915
- const NH_SENTRY_BEFORE_SEND_LOG_HOOKS = new InjectionToken("NH_SENTRY_BEFORE_SEND_LOG_HOOKS");
2916
- const NH_SENTRY_BEFORE_SEND_SPAN_HOOKS = new InjectionToken("NH_SENTRY_BEFORE_SEND_SPAN_HOOKS");
2917
- const NH_SENTRY_BEFORE_SEND_TRANSACTION_HOOKS = new InjectionToken("NH_SENTRY_BEFORE_SEND_TRANSACTION_HOOKS");
2918
- const NH_SENTRY_BEFORE_BREADCRUMB_HOOKS = new InjectionToken("NH_SENTRY_BEFORE_BREADCRUMB_HOOKS");
2919
- class NhSentryInitializerService {
2920
- get hooksBeforeSend() {
2921
- return [...this.providedHooksBeforeSend, ...this.runtimeHooksBeforeSend];
2922
- }
2923
- get hooksBeforeSendLog() {
2924
- return [...this.providedHooksBeforeSendLog, ...this.runtimeHooksBeforeSendLog];
2925
- }
2926
- get hooksBeforeSendSpan() {
2927
- return [...this.providedHooksBeforeSendSpan, ...this.runtimeHooksBeforeSendSpan];
2928
- }
2929
- get hooksBeforeSendTransaction() {
2930
- return [...this.providedHooksBeforeSendTransaction, ...this.runtimeHooksBeforeSendTransaction];
2931
- }
2932
- get hooksBeforeBreadcrumb() {
2933
- return [...this.providedHooksBeforeBreadcrumb, ...this.runtimeHooksBeforeBreadcrumb];
2934
- }
2935
- get isEnabled() {
2936
- return this._sentryConfig.errorLoggingEnabled || this._sentryConfig.tracingEnabled;
2937
- }
2938
- get isInitializedAndEnabled() {
2939
- return this._didInitialize && this.isEnabled;
2940
- }
2941
- get sentry() {
2942
- return Sentry;
2943
- }
2944
- constructor(moduleConfig, authService, providedHooksBeforeSend = [], providedHooksBeforeSendLog = [], providedHooksBeforeSendSpan = [], providedHooksBeforeSendTransaction = [], providedHooksBeforeBreadcrumb = []) {
2945
- this.moduleConfig = moduleConfig;
2946
- this.authService = authService;
2947
- this.providedHooksBeforeSend = providedHooksBeforeSend;
2948
- this.providedHooksBeforeSendLog = providedHooksBeforeSendLog;
2949
- this.providedHooksBeforeSendSpan = providedHooksBeforeSendSpan;
2950
- this.providedHooksBeforeSendTransaction = providedHooksBeforeSendTransaction;
2951
- this.providedHooksBeforeBreadcrumb = providedHooksBeforeBreadcrumb;
2952
- this._didInitialize = false;
2953
- this.runtimeHooksBeforeSend = [];
2954
- this.runtimeHooksBeforeSendLog = [];
2955
- this.runtimeHooksBeforeSendSpan = [];
2956
- this.runtimeHooksBeforeSendTransaction = [];
2957
- this.runtimeHooksBeforeBreadcrumb = [];
2958
- this._sentryConfig = this.moduleConfig.errorLogging.sentry;
2959
- this.$auth = this.authService.authSubject.subscribe(async (authorization) => {
2960
- await this.authChanged(authorization);
2961
- });
2962
- this.initialize();
2963
- }
2964
- async authChanged(authorization) {
2965
- this.authorization = authorization;
2966
- if (!this.isInitializedAndEnabled) {
2967
- return;
2968
- }
2969
- }
2970
- registerHookBeforeSend(hook) {
2971
- this.runtimeHooksBeforeSend.push(hook);
2972
- }
2973
- registerHookBeforeSendLog(hook) {
2974
- this.runtimeHooksBeforeSendLog.push(hook);
2975
- }
2976
- registerHookBeforeSendSpan(hook) {
2977
- this.runtimeHooksBeforeSendSpan.push(hook);
2978
- }
2979
- registerHookBeforeSendTransaction(hook) {
2980
- this.runtimeHooksBeforeSendTransaction.push(hook);
2981
- }
2982
- registerHookBeforeBreadcrumb(hook) {
2983
- this.runtimeHooksBeforeBreadcrumb.push(hook);
2984
- }
2985
- async ourBeforeSendHook(event, hint) {
2986
- if (this._sentryConfig.beforeSendAddAuthServiceInformation
2987
- && this.authorization) {
2988
- const user = event.user ?? {};
2989
- user.id = this.authorization.user.id,
2990
- user.email = this.authorization.user.email,
2991
- user.username = this.authorization.user.email,
2992
- user.roles = (this.authorization.user.roles ?? []).join(', ');
2993
- if (this.authorization.activeDivision) {
2994
- user.activeDivisionId = this.authorization.activeDivision.id;
2995
- user.activeDivisionName = this.authorization.activeDivision.name;
2996
- }
2997
- event.user = user;
2998
- }
2999
- return event;
3000
- }
3001
- initialize() {
3002
- if (this._didInitialize) {
3003
- return;
3004
- }
3005
- this.registerHookBeforeSend(this.ourBeforeSendHook.bind(this));
3006
- if (!this.isEnabled) {
3007
- if (!Sentry.isInitialized()) {
3008
- const options = this.moduleConfig.errorLogging.sentry.options;
3009
- Sentry.init({
3010
- ...options,
3011
- beforeSend: (event, hint) => this.runHooksBeforeSend(event, hint),
3012
- beforeSendLog: (event) => this.runHooksBeforeSendLog(event),
3013
- beforeSendSpan: (event) => this.runHooksBeforeSendSpan(event),
3014
- beforeSendTransaction: (event, hint) => this.runHooksBeforeSendTransaction(event, hint),
3015
- beforeBreadcrumb: (event, hint) => this.runHooksBeforeBreadcrumb(event, hint),
3016
- });
3017
- }
3018
- }
3019
- this._didInitialize = true;
3020
- }
3021
- async runHooksBeforeSend(initialEvent, hint) {
3022
- const all = [...this.providedHooksBeforeSend, ...this.runtimeHooksBeforeSend];
3023
- let current = initialEvent;
3024
- for (const hook of all) {
3025
- if (!current)
3026
- return null;
3027
- current = await hook(current, hint);
3028
- }
3029
- return current;
3030
- }
3031
- runHooksBeforeSendLog(initialEvent) {
3032
- const all = [...this.providedHooksBeforeSendLog, ...this.runtimeHooksBeforeSendLog];
3033
- let current = initialEvent;
3034
- for (const hook of all) {
3035
- if (!current)
3036
- return null;
3037
- current = hook(current);
3038
- }
3039
- return current;
3040
- }
3041
- runHooksBeforeSendSpan(initialSpan) {
3042
- const all = [...this.providedHooksBeforeSendSpan, ...this.runtimeHooksBeforeSendSpan];
3043
- let current = initialSpan;
3044
- for (const hook of all) {
3045
- current = hook(current);
3046
- }
3047
- return current;
3048
- }
3049
- async runHooksBeforeSendTransaction(initialEvent, hint) {
3050
- const all = [...this.providedHooksBeforeSendTransaction, ...this.runtimeHooksBeforeSendTransaction];
3051
- let current = initialEvent;
3052
- for (const hook of all) {
3053
- if (!current)
3054
- return null;
3055
- current = await hook(current, hint);
3056
- }
3057
- return current;
3058
- }
3059
- runHooksBeforeBreadcrumb(initialBreadcrumb, hint) {
3060
- const all = [...this.providedHooksBeforeBreadcrumb, ...this.runtimeHooksBeforeBreadcrumb];
3061
- let current = initialBreadcrumb;
3062
- for (const hook of all) {
3063
- if (!current)
3064
- return null;
3065
- current = hook(current, hint);
3066
- }
3067
- return current;
3068
- }
3069
- ngOnDestroy() {
3070
- this.$auth?.unsubscribe();
3071
- }
3072
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhSentryInitializerService, deps: [{ token: NhCommonModuleConfig }, { token: NhAuthService }, { token: NH_SENTRY_BEFORE_SEND_HOOKS, optional: true }, { token: NH_SENTRY_BEFORE_SEND_LOG_HOOKS, optional: true }, { token: NH_SENTRY_BEFORE_SEND_SPAN_HOOKS, optional: true }, { token: NH_SENTRY_BEFORE_SEND_TRANSACTION_HOOKS, optional: true }, { token: NH_SENTRY_BEFORE_BREADCRUMB_HOOKS, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
3073
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhSentryInitializerService, providedIn: "root" }); }
3074
- }
3075
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhSentryInitializerService, decorators: [{
3076
- type: Injectable,
3077
- args: [{
3078
- providedIn: "root"
3079
- }]
3080
- }], ctorParameters: () => [{ type: NhCommonModuleConfig }, { type: NhAuthService }, { type: undefined, decorators: [{
3081
- type: Optional
3082
- }, {
3083
- type: Inject,
3084
- args: [NH_SENTRY_BEFORE_SEND_HOOKS]
3085
- }] }, { type: undefined, decorators: [{
3086
- type: Optional
3087
- }, {
3088
- type: Inject,
3089
- args: [NH_SENTRY_BEFORE_SEND_LOG_HOOKS]
3090
- }] }, { type: undefined, decorators: [{
3091
- type: Optional
3092
- }, {
3093
- type: Inject,
3094
- args: [NH_SENTRY_BEFORE_SEND_SPAN_HOOKS]
3095
- }] }, { type: undefined, decorators: [{
3096
- type: Optional
3097
- }, {
3098
- type: Inject,
3099
- args: [NH_SENTRY_BEFORE_SEND_TRANSACTION_HOOKS]
3100
- }] }, { type: undefined, decorators: [{
3101
- type: Optional
3102
- }, {
3103
- type: Inject,
3104
- args: [NH_SENTRY_BEFORE_BREADCRUMB_HOOKS]
3105
- }] }] });
3106
- class NhSentryService {
3107
- get isEnabled() {
3108
- return this.sentryInitializerService.isEnabled;
3109
- }
3110
- get isInitializedAndEnabled() {
3111
- return this.sentryInitializerService.isInitializedAndEnabled;
3112
- }
3113
- get sentry() {
3114
- return Sentry;
3115
- }
3116
- constructor(moduleConfig, sentryInitializerService) {
3117
- this.moduleConfig = moduleConfig;
3118
- this.sentryInitializerService = sentryInitializerService;
3119
- this._sentryConfig = this.moduleConfig.errorLogging.sentry;
3120
- }
3121
- registerHookBeforeSend(hook) {
3122
- this.sentryInitializerService.registerHookBeforeSend(hook);
3123
- }
3124
- registerHookBeforeSendLog(hook) {
3125
- this.sentryInitializerService.registerHookBeforeSendLog(hook);
3126
- }
3127
- registerHookBeforeSendSpan(hook) {
3128
- this.sentryInitializerService.registerHookBeforeSendSpan(hook);
3129
- }
3130
- registerHookBeforeSendTransaction(hook) {
3131
- this.sentryInitializerService.registerHookBeforeSendTransaction(hook);
3132
- }
3133
- registerHookBeforeBreadcrumb(hook) {
3134
- this.sentryInitializerService.registerHookBeforeBreadcrumb(hook);
3135
- }
3136
- ngOnDestroy() {
3137
- }
3138
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhSentryService, deps: [{ token: NhCommonModuleConfig }, { token: NhSentryInitializerService }], target: i0.ɵɵFactoryTarget.Injectable }); }
3139
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhSentryService, providedIn: "root" }); }
3140
- }
3141
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhSentryService, decorators: [{
3142
- type: Injectable,
3143
- args: [{
3144
- providedIn: "root"
3145
- }]
3146
- }], ctorParameters: () => [{ type: NhCommonModuleConfig }, { type: NhSentryInitializerService }] });
3147
-
3148
- class NhErrorHandlerSentryService {
3149
- get sentryErrorHandler() {
3150
- return this._sentryErrorHandler;
3151
- }
3152
- constructor(moduleConfig, sentryInitializerService) {
3153
- this.moduleConfig = moduleConfig;
3154
- this.sentryInitializerService = sentryInitializerService;
3155
- this._didInitialize = false;
3156
- this.initialize();
3157
- }
3158
- initialize() {
3159
- if (!this.moduleConfig.errorLogging.sentry.errorLoggingEnabled) {
3160
- this._sentryErrorHandler?.ngOnDestroy();
3161
- this._sentryErrorHandler = undefined;
3162
- return;
3163
- }
3164
- if (this._didInitialize) {
3165
- return;
3166
- }
3167
- this.sentryInitializerService.initialize();
3168
- this._sentryErrorHandler = Sentry.createErrorHandler(this.moduleConfig.errorLogging.sentry.errorHandlerOptions);
3169
- this._didInitialize = true;
3170
- }
3171
- handleError(error) {
3172
- this.initialize();
3173
- if (!this.moduleConfig.errorLogging.sentry.errorLoggingEnabled) {
3174
- return;
3175
- }
3176
- this._sentryErrorHandler?.handleError(error);
3177
- }
3178
- ngOnDestroy() {
3179
- this._sentryErrorHandler?.ngOnDestroy();
3180
- this._sentryErrorHandler = undefined;
3181
- }
3182
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhErrorHandlerSentryService, deps: [{ token: NhCommonModuleConfig }, { token: NhSentryInitializerService }], target: i0.ɵɵFactoryTarget.Injectable }); }
3183
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhErrorHandlerSentryService, providedIn: "root" }); }
3184
- }
3185
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhErrorHandlerSentryService, decorators: [{
3186
- type: Injectable,
3187
- args: [{
3188
- providedIn: "root"
3189
- }]
3190
- }], ctorParameters: () => [{ type: NhCommonModuleConfig }, { type: NhSentryInitializerService }] });
3191
-
3192
- class NhSentryTraceService {
3193
- get sentryTraceService() {
3194
- return this._sentryTraceService;
3195
- }
3196
- constructor(moduleConfig, sentryInitializerService, router) {
3197
- this.moduleConfig = moduleConfig;
3198
- this.sentryInitializerService = sentryInitializerService;
3199
- this.router = router;
3200
- this._didInitialize = false;
3201
- this.initialize();
3202
- }
3203
- initialize() {
3204
- if (!this.moduleConfig.errorLogging.sentry.tracingEnabled) {
3205
- this._sentryTraceService?.ngOnDestroy();
3206
- this._sentryTraceService = undefined;
3207
- return;
3208
- }
3209
- if (this._didInitialize) {
3210
- return;
3211
- }
3212
- this.sentryInitializerService.initialize();
3213
- this._sentryTraceService = new Sentry.TraceService(this.router);
3214
- this._didInitialize = true;
3215
- }
3216
- ngOnDestroy() {
3217
- this._sentryTraceService?.ngOnDestroy();
3218
- this._sentryTraceService = undefined;
3219
- }
3220
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhSentryTraceService, deps: [{ token: NhCommonModuleConfig }, { token: NhSentryInitializerService }, { token: i1$1.Router }], target: i0.ɵɵFactoryTarget.Injectable }); }
3221
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhSentryTraceService, providedIn: "root" }); }
3222
- }
3223
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhSentryTraceService, decorators: [{
3224
- type: Injectable,
3225
- args: [{
3226
- providedIn: "root"
3227
- }]
3228
- }], ctorParameters: () => [{ type: NhCommonModuleConfig }, { type: NhSentryInitializerService }, { type: i1$1.Router }] });
3229
-
3230
2960
  const APP_ORIGINATED_FROM_SERVER = 'APP_ORIGINATED_FROM_SERVER';
3231
2961
  class NhAppService {
3232
2962
  constructor(document, platformId, appRef, transferState, router) {
@@ -5613,7 +5343,7 @@ class NhUserNotificationService {
5613
5343
  ngOnDestroy() {
5614
5344
  this.taskSub?.unsubscribe();
5615
5345
  this.runningSubject.complete();
5616
- this.userNotificationStateSubject?.unsubscribe();
5346
+ this.userNotificationStateSubject.complete();
5617
5347
  }
5618
5348
  constructor(appService) {
5619
5349
  this.appService = appService;
@@ -5622,23 +5352,33 @@ class NhUserNotificationService {
5622
5352
  this.apiService = inject(NhApiService);
5623
5353
  this.runningSubject = new BehaviorSubject(false);
5624
5354
  this.isRunning$ = this.runningSubject.asObservable();
5355
+ this.runningConsumers = 0;
5625
5356
  this.userNotificationState = new NhUserNotificationState();
5626
5357
  this.userNotificationStateSubject = new BehaviorSubject(this.userNotificationState);
5627
5358
  this.userNotificationState$ = this.userNotificationStateSubject.asObservable();
5628
5359
  if (!this.appService.isPlatformServer()) {
5629
5360
  this.task$ = this.runningSubject.pipe(switchMap(isRunning => isRunning
5630
- ? timer(0, this.moduleConfig.userNotification.pollingInterval).pipe(tap(() => from(this.doWork())), takeUntil(this.runningSubject.pipe(switchMap(r => r ? EMPTY : EMPTY))))
5361
+ ? timer(0, this.moduleConfig.userNotification.pollingInterval).pipe(exhaustMap(() => from(this.doWork())))
5631
5362
  : EMPTY));
5632
5363
  this.taskSub = this.task$.subscribe();
5633
5364
  }
5634
5365
  this.baseUrl = this.moduleConfig.apiBaseUrl + this.urlSuffix;
5635
5366
  }
5636
- start() { if (this.isRunning()) {
5637
- return;
5638
- } this.runningSubject.next(true); }
5639
- stop() { if (!this.isRunning()) {
5640
- return;
5641
- } this.runningSubject.next(false); }
5367
+ start() {
5368
+ this.runningConsumers++;
5369
+ if (this.runningConsumers === 1) {
5370
+ this.runningSubject.next(true);
5371
+ }
5372
+ }
5373
+ stop() {
5374
+ if (this.runningConsumers === 0) {
5375
+ return;
5376
+ }
5377
+ this.runningConsumers--;
5378
+ if (this.runningConsumers === 0) {
5379
+ this.runningSubject.next(false);
5380
+ }
5381
+ }
5642
5382
  async doWork() {
5643
5383
  if (!this.isRunning()) {
5644
5384
  return;
@@ -5684,6 +5424,479 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImpo
5684
5424
  }]
5685
5425
  }], ctorParameters: () => [{ type: NhAppService }] });
5686
5426
 
5427
+ class NhBackgroundOperationService {
5428
+ constructor() {
5429
+ this.config = inject(NhCommonModuleConfig);
5430
+ this.api = inject(NhApiService);
5431
+ this.baseUrl = this.joinUrl(this.config.apiBaseUrl, this.config.backgroundOperations.urlSuffix);
5432
+ }
5433
+ list(request = new NhBackgroundOperationCollectionHttpRequestOptions({
5434
+ page: 1,
5435
+ itemsPerPage: this.config.backgroundOperations.listPageSize
5436
+ })) {
5437
+ return this.api.getCollection(this.baseUrl, request);
5438
+ }
5439
+ get(operationId, eventsAfterSequence) {
5440
+ let params = new HttpParams();
5441
+ if (eventsAfterSequence !== undefined) {
5442
+ params = params.set('eventsAfterSequence', eventsAfterSequence.toString());
5443
+ }
5444
+ return this.api.get(`${this.baseUrl}/${encodeURIComponent(operationId)}`, new HttpRequestOptions({ params }));
5445
+ }
5446
+ cancel(operationId) {
5447
+ return this.api.post(`${this.baseUrl}/${encodeURIComponent(operationId)}/cancel`, {});
5448
+ }
5449
+ retry(operationId) {
5450
+ return this.api.post(`${this.baseUrl}/${encodeURIComponent(operationId)}/retry`, {});
5451
+ }
5452
+ joinUrl(base, suffix) {
5453
+ return `${base.replace(/\/$/, '')}/${suffix.replace(/^\//, '')}`;
5454
+ }
5455
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhBackgroundOperationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
5456
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhBackgroundOperationService, providedIn: 'root' }); }
5457
+ }
5458
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhBackgroundOperationService, decorators: [{
5459
+ type: Injectable,
5460
+ args: [{ providedIn: 'root' }]
5461
+ }] });
5462
+
5463
+ class NhBackgroundOperationLiveUpdateService {
5464
+ constructor(config, auth, app) {
5465
+ this.config = config;
5466
+ this.auth = auth;
5467
+ this.app = app;
5468
+ this.changedSubject = new Subject();
5469
+ this.resyncSubject = new Subject();
5470
+ this.connectionStateSubject = new BehaviorSubject('disconnected');
5471
+ this.consumers = 0;
5472
+ this.changed$ = this.changedSubject.asObservable();
5473
+ this.resync$ = this.resyncSubject.asObservable();
5474
+ this.connectionState$ = this.connectionStateSubject.asObservable();
5475
+ this.authorizationScopeKey = this.getAuthorizationScopeKey();
5476
+ if (!config.backgroundOperations.liveUpdatesEnabled || app.isPlatformServer()) {
5477
+ this.connectionStateSubject.next('disabled');
5478
+ }
5479
+ this.authSubscription = auth.authSubject.subscribe(() => {
5480
+ const nextScopeKey = this.getAuthorizationScopeKey();
5481
+ if (nextScopeKey === this.authorizationScopeKey) {
5482
+ return;
5483
+ }
5484
+ this.authorizationScopeKey = nextScopeKey;
5485
+ if (this.consumers > 0 && this.isEnabled()) {
5486
+ void this.restart();
5487
+ }
5488
+ });
5489
+ }
5490
+ start() {
5491
+ this.consumers++;
5492
+ if (this.consumers === 1 && this.isEnabled()) {
5493
+ void this.ensureConnected();
5494
+ }
5495
+ }
5496
+ stop() {
5497
+ if (this.consumers === 0) {
5498
+ return;
5499
+ }
5500
+ this.consumers--;
5501
+ if (this.consumers === 0) {
5502
+ this.clearReconnectTimer();
5503
+ void this.stopConnection();
5504
+ }
5505
+ }
5506
+ ngOnDestroy() {
5507
+ this.consumers = 0;
5508
+ this.clearReconnectTimer();
5509
+ this.authSubscription.unsubscribe();
5510
+ void this.stopConnection();
5511
+ this.changedSubject.complete();
5512
+ this.resyncSubject.complete();
5513
+ this.connectionStateSubject.complete();
5514
+ }
5515
+ isEnabled() {
5516
+ return this.config.backgroundOperations.liveUpdatesEnabled && !this.app.isPlatformServer();
5517
+ }
5518
+ async restart() {
5519
+ await this.stopConnection();
5520
+ if (this.consumers > 0) {
5521
+ await this.ensureConnected();
5522
+ }
5523
+ }
5524
+ ensureConnected() {
5525
+ if (this.connectPromise) {
5526
+ return this.connectPromise;
5527
+ }
5528
+ if (this.connection?.state === 'Connected') {
5529
+ return Promise.resolve();
5530
+ }
5531
+ this.connectPromise = this.connectCore().finally(() => {
5532
+ this.connectPromise = undefined;
5533
+ });
5534
+ return this.connectPromise;
5535
+ }
5536
+ async connectCore() {
5537
+ this.connectionStateSubject.next('connecting');
5538
+ try {
5539
+ this.connection ??= await this.buildConnection();
5540
+ await this.connection.start();
5541
+ this.connectionStateSubject.next('connected');
5542
+ this.resyncSubject.next();
5543
+ }
5544
+ catch {
5545
+ this.connectionStateSubject.next('disconnected');
5546
+ this.scheduleReconnect();
5547
+ }
5548
+ }
5549
+ async buildConnection() {
5550
+ const signalR = await import('@microsoft/signalr');
5551
+ const baseHubUrl = this.joinUrl(this.config.backgroundOperations.hubBaseUrl || this.config.baseUrl || this.config.apiBaseUrl, this.config.backgroundOperations.hubUrlSuffix);
5552
+ const divisionId = this.auth.getAuthorization()?.activeDivision?.id;
5553
+ const hubUrl = divisionId
5554
+ ? this.appendQueryParameter(baseHubUrl, 'divisionId', divisionId)
5555
+ : baseHubUrl;
5556
+ const connection = new signalR.HubConnectionBuilder()
5557
+ .withUrl(hubUrl, {
5558
+ withCredentials: true,
5559
+ accessTokenFactory: () => this.config.authType === 'header'
5560
+ ? this.auth.getAuthorization()?.token ?? ''
5561
+ : ''
5562
+ })
5563
+ .withAutomaticReconnect([0, 2000, 5000, 10000, 30000])
5564
+ .configureLogging(signalR.LogLevel.Warning)
5565
+ .build();
5566
+ connection.on('OperationChanged', (change) => {
5567
+ this.changedSubject.next(change);
5568
+ });
5569
+ connection.onreconnecting(() => this.connectionStateSubject.next('reconnecting'));
5570
+ connection.onreconnected(() => {
5571
+ this.connectionStateSubject.next('connected');
5572
+ this.resyncSubject.next();
5573
+ });
5574
+ connection.onclose(() => {
5575
+ this.connectionStateSubject.next('disconnected');
5576
+ this.scheduleReconnect();
5577
+ });
5578
+ return connection;
5579
+ }
5580
+ async stopConnection() {
5581
+ const connection = this.connection;
5582
+ this.connection = undefined;
5583
+ this.connectPromise = undefined;
5584
+ if (connection && connection.state !== 'Disconnected') {
5585
+ try {
5586
+ await connection.stop();
5587
+ }
5588
+ catch {
5589
+ // A failed transport is already disconnected from the application's
5590
+ // perspective. Polling remains the durable fallback.
5591
+ }
5592
+ }
5593
+ if (this.isEnabled()) {
5594
+ this.connectionStateSubject.next('disconnected');
5595
+ }
5596
+ }
5597
+ scheduleReconnect() {
5598
+ if (this.consumers === 0 || this.reconnectTimer || !this.isEnabled()) {
5599
+ return;
5600
+ }
5601
+ this.reconnectTimer = setTimeout(() => {
5602
+ this.reconnectTimer = undefined;
5603
+ void this.ensureConnected();
5604
+ }, 5000);
5605
+ }
5606
+ clearReconnectTimer() {
5607
+ if (this.reconnectTimer) {
5608
+ clearTimeout(this.reconnectTimer);
5609
+ this.reconnectTimer = undefined;
5610
+ }
5611
+ }
5612
+ joinUrl(base, suffix) {
5613
+ return `${base.replace(/\/$/, '')}/${suffix.replace(/^\//, '')}`;
5614
+ }
5615
+ appendQueryParameter(url, name, value) {
5616
+ const separator = url.includes('?') ? '&' : '?';
5617
+ return `${url}${separator}${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
5618
+ }
5619
+ getAuthorizationScopeKey() {
5620
+ const authorization = this.auth.getAuthorization();
5621
+ return `${authorization?.user?.id ?? 'anonymous'}:${authorization?.activeDivision?.id ?? 'global'}`;
5622
+ }
5623
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhBackgroundOperationLiveUpdateService, deps: [{ token: NhCommonModuleConfig }, { token: NhAuthService }, { token: NhAppService }], target: i0.ɵɵFactoryTarget.Injectable }); }
5624
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhBackgroundOperationLiveUpdateService, providedIn: 'root' }); }
5625
+ }
5626
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhBackgroundOperationLiveUpdateService, decorators: [{
5627
+ type: Injectable,
5628
+ args: [{ providedIn: 'root' }]
5629
+ }], ctorParameters: () => [{ type: NhCommonModuleConfig }, { type: NhAuthService }, { type: NhAppService }] });
5630
+
5631
+ class NhBackgroundOperationStore {
5632
+ constructor(api, live, config, auth) {
5633
+ this.api = api;
5634
+ this.live = live;
5635
+ this.config = config;
5636
+ this.auth = auth;
5637
+ this.operationsSubject = new BehaviorSubject(new Map());
5638
+ this.errorSubject = new BehaviorSubject(undefined);
5639
+ this.subscriptions = new Subscription();
5640
+ this.watchedIds = new Map();
5641
+ this.refreshing = new Set();
5642
+ this.refreshAgain = new Set();
5643
+ this.consumers = 0;
5644
+ this.scopeGeneration = 0;
5645
+ this.operations$ = this.operationsSubject.pipe(map(items => [...items.values()].sort((left, right) => right.lastModifiedDateTime.localeCompare(left.lastModifiedDateTime))));
5646
+ this.error$ = this.errorSubject.asObservable();
5647
+ this.connectionState$ = this.live.connectionState$;
5648
+ this.scopeKey = this.getScopeKey(this.auth.getAuthorization());
5649
+ this.subscriptions.add(this.auth.authSubject.subscribe(authorization => this.onAuthorizationChanged(authorization)));
5650
+ this.subscriptions.add(this.live.changed$.subscribe(change => this.onLiveChange(change)));
5651
+ this.subscriptions.add(this.live.resync$.subscribe(() => {
5652
+ void this.refreshList();
5653
+ for (const id of this.watchedIds.keys()) {
5654
+ this.refreshOperation(id);
5655
+ }
5656
+ }));
5657
+ }
5658
+ watchAll() {
5659
+ return defer(() => {
5660
+ this.start();
5661
+ return this.operations$.pipe(finalize$1(() => this.stop()));
5662
+ });
5663
+ }
5664
+ watch(operationId) {
5665
+ return defer(() => {
5666
+ this.start();
5667
+ this.watchedIds.set(operationId, (this.watchedIds.get(operationId) ?? 0) + 1);
5668
+ this.refreshOperation(operationId);
5669
+ return this.operationsSubject.pipe(map(operations => operations.get(operationId)), finalize$1(() => {
5670
+ const watchers = (this.watchedIds.get(operationId) ?? 1) - 1;
5671
+ if (watchers <= 0) {
5672
+ this.watchedIds.delete(operationId);
5673
+ }
5674
+ else {
5675
+ this.watchedIds.set(operationId, watchers);
5676
+ }
5677
+ this.stop();
5678
+ }));
5679
+ });
5680
+ }
5681
+ start() {
5682
+ this.consumers++;
5683
+ if (this.consumers !== 1) {
5684
+ return;
5685
+ }
5686
+ this.live.start();
5687
+ this.pollingSubscription = timer(0, Math.max(1000, this.config.backgroundOperations.pollingInterval))
5688
+ .pipe(exhaustMap(() => this.loadSnapshot()))
5689
+ .subscribe();
5690
+ }
5691
+ stop() {
5692
+ if (this.consumers === 0) {
5693
+ return;
5694
+ }
5695
+ this.consumers--;
5696
+ if (this.consumers === 0) {
5697
+ this.pollingSubscription?.unsubscribe();
5698
+ this.pollingSubscription = undefined;
5699
+ this.live.stop();
5700
+ }
5701
+ }
5702
+ refreshList() {
5703
+ return new Promise(resolve => this.loadList().subscribe({ complete: resolve }));
5704
+ }
5705
+ cancel(operationId) {
5706
+ const generation = this.scopeGeneration;
5707
+ return this.api.cancel(operationId).pipe(map(operation => this.mergeDetail(operation, generation)));
5708
+ }
5709
+ retry(operationId) {
5710
+ const generation = this.scopeGeneration;
5711
+ return this.api.retry(operationId).pipe(map(operation => this.mergeDetail(operation, generation)));
5712
+ }
5713
+ ngOnDestroy() {
5714
+ this.pollingSubscription?.unsubscribe();
5715
+ this.subscriptions.unsubscribe();
5716
+ if (this.consumers > 0) {
5717
+ this.consumers = 0;
5718
+ this.live.stop();
5719
+ }
5720
+ this.operationsSubject.complete();
5721
+ this.errorSubject.complete();
5722
+ }
5723
+ loadList() {
5724
+ const generation = this.scopeGeneration;
5725
+ const request = new NhBackgroundOperationCollectionHttpRequestOptions({
5726
+ page: 1,
5727
+ itemsPerPage: this.config.backgroundOperations.listPageSize
5728
+ });
5729
+ return this.api.list(request).pipe(map(response => {
5730
+ if (generation !== this.scopeGeneration) {
5731
+ return;
5732
+ }
5733
+ this.replaceListSnapshot(response.items ?? []);
5734
+ this.errorSubject.next(undefined);
5735
+ }), catchError(error => {
5736
+ if (generation === this.scopeGeneration) {
5737
+ this.errorSubject.next(error);
5738
+ }
5739
+ return of(undefined);
5740
+ }));
5741
+ }
5742
+ loadSnapshot() {
5743
+ return this.loadList().pipe(map(() => {
5744
+ for (const id of this.watchedIds.keys()) {
5745
+ this.refreshOperation(id);
5746
+ }
5747
+ }));
5748
+ }
5749
+ onLiveChange(change) {
5750
+ const existing = this.operationsSubject.value.get(change.operationId);
5751
+ if (existing && existing.version >= change.version && existing.latestEventSequence >= change.latestEventSequence) {
5752
+ return;
5753
+ }
5754
+ this.refreshOperation(change.operationId);
5755
+ }
5756
+ refreshOperation(operationId) {
5757
+ const generation = this.scopeGeneration;
5758
+ const refreshKey = `${generation}:${operationId}`;
5759
+ if (this.refreshing.has(refreshKey)) {
5760
+ this.refreshAgain.add(refreshKey);
5761
+ return;
5762
+ }
5763
+ this.refreshing.add(refreshKey);
5764
+ const existing = this.operationsSubject.value.get(operationId);
5765
+ this.api
5766
+ .get(operationId, existing?.latestEventSequence)
5767
+ .pipe(catchError(error => {
5768
+ if (generation === this.scopeGeneration) {
5769
+ if (this.shouldRemoveAfterError(error)) {
5770
+ this.removeOperation(operationId);
5771
+ }
5772
+ this.errorSubject.next(error);
5773
+ }
5774
+ return EMPTY;
5775
+ }), finalize$1(() => {
5776
+ this.refreshing.delete(refreshKey);
5777
+ if (this.refreshAgain.delete(refreshKey) && generation === this.scopeGeneration) {
5778
+ this.refreshOperation(operationId);
5779
+ }
5780
+ }))
5781
+ .subscribe(operation => {
5782
+ if (generation === this.scopeGeneration) {
5783
+ this.mergeDetail(operation, generation);
5784
+ this.errorSubject.next(undefined);
5785
+ }
5786
+ });
5787
+ }
5788
+ mergeSummary(incoming) {
5789
+ const existing = this.operationsSubject.value.get(incoming.id);
5790
+ if (existing && incoming.version < existing.version) {
5791
+ return existing;
5792
+ }
5793
+ const merged = existing
5794
+ ? {
5795
+ ...incoming,
5796
+ attempts: existing.attempts,
5797
+ steps: existing.steps,
5798
+ children: existing.children,
5799
+ events: existing.events
5800
+ }
5801
+ : incoming;
5802
+ this.setOperation(merged);
5803
+ return merged;
5804
+ }
5805
+ mergeDetail(incoming, generation = this.scopeGeneration) {
5806
+ if (generation !== this.scopeGeneration) {
5807
+ return incoming;
5808
+ }
5809
+ const existing = this.operationsSubject.value.get(incoming.id);
5810
+ if (existing && incoming.version < existing.version) {
5811
+ return existing;
5812
+ }
5813
+ const events = new Map();
5814
+ for (const event of existing?.events ?? []) {
5815
+ events.set(event.sequence, event);
5816
+ }
5817
+ for (const event of incoming.events ?? []) {
5818
+ events.set(event.sequence, event);
5819
+ }
5820
+ const merged = {
5821
+ ...incoming,
5822
+ events: [...events.values()].sort((left, right) => left.sequence - right.sequence)
5823
+ };
5824
+ this.setOperation(merged);
5825
+ return merged;
5826
+ }
5827
+ setOperation(operation) {
5828
+ const operations = new Map(this.operationsSubject.value);
5829
+ operations.set(operation.id, operation);
5830
+ this.operationsSubject.next(operations);
5831
+ }
5832
+ replaceListSnapshot(incoming) {
5833
+ const current = this.operationsSubject.value;
5834
+ const next = new Map();
5835
+ for (const watchedId of this.watchedIds.keys()) {
5836
+ const watched = current.get(watchedId);
5837
+ if (watched) {
5838
+ next.set(watchedId, watched);
5839
+ }
5840
+ }
5841
+ for (const operation of incoming) {
5842
+ const existing = current.get(operation.id);
5843
+ if (existing && operation.version < existing.version) {
5844
+ next.set(operation.id, existing);
5845
+ continue;
5846
+ }
5847
+ next.set(operation.id, existing
5848
+ ? {
5849
+ ...operation,
5850
+ attempts: existing.attempts,
5851
+ steps: existing.steps,
5852
+ children: existing.children,
5853
+ events: existing.events
5854
+ }
5855
+ : operation);
5856
+ }
5857
+ this.operationsSubject.next(next);
5858
+ }
5859
+ onAuthorizationChanged(authorization) {
5860
+ const nextScopeKey = this.getScopeKey(authorization);
5861
+ if (nextScopeKey === this.scopeKey) {
5862
+ return;
5863
+ }
5864
+ this.scopeKey = nextScopeKey;
5865
+ this.scopeGeneration++;
5866
+ this.operationsSubject.next(new Map());
5867
+ this.errorSubject.next(undefined);
5868
+ if (this.consumers > 0) {
5869
+ void this.refreshList();
5870
+ for (const id of this.watchedIds.keys()) {
5871
+ this.refreshOperation(id);
5872
+ }
5873
+ }
5874
+ }
5875
+ getScopeKey(authorization) {
5876
+ const userId = authorization?.user?.id ?? 'anonymous';
5877
+ const divisionId = authorization?.activeDivision?.id ?? 'global';
5878
+ return `${userId}:${divisionId}`;
5879
+ }
5880
+ shouldRemoveAfterError(error) {
5881
+ return error instanceof HttpErrorResponse
5882
+ && [401, 403, 404].includes(error.status);
5883
+ }
5884
+ removeOperation(operationId) {
5885
+ if (!this.operationsSubject.value.has(operationId)) {
5886
+ return;
5887
+ }
5888
+ const operations = new Map(this.operationsSubject.value);
5889
+ operations.delete(operationId);
5890
+ this.operationsSubject.next(operations);
5891
+ }
5892
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhBackgroundOperationStore, deps: [{ token: NhBackgroundOperationService }, { token: NhBackgroundOperationLiveUpdateService }, { token: NhCommonModuleConfig }, { token: NhAuthService }], target: i0.ɵɵFactoryTarget.Injectable }); }
5893
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhBackgroundOperationStore, providedIn: 'root' }); }
5894
+ }
5895
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhBackgroundOperationStore, decorators: [{
5896
+ type: Injectable,
5897
+ args: [{ providedIn: 'root' }]
5898
+ }], ctorParameters: () => [{ type: NhBackgroundOperationService }, { type: NhBackgroundOperationLiveUpdateService }, { type: NhCommonModuleConfig }, { type: NhAuthService }] });
5899
+
5687
5900
  class NhContextMenuItem {
5688
5901
  constructor(init) {
5689
5902
  this.type = 'item';
@@ -7747,9 +7960,9 @@ class NhUserNotificationsAbstractComponent extends NhCollectionBaseComponent {
7747
7960
  this.lastUnreadNotificationCount = this.userNotificationState.overview.unreadCount;
7748
7961
  this.userNotificationSubscription = this.userNotificationService.userNotificationState$.subscribe(async (state) => {
7749
7962
  this.userNotificationState = state;
7750
- const hasNewNotifications = this.lastNotificationDate && this.lastNotificationDate != this.userNotificationState.overview.lastNotificationDate;
7751
- const countChanged = this.lastNotificationCount && this.lastNotificationCount != this.userNotificationState.overview.totalCount;
7752
- const unreadCountChanged = this.lastUnreadNotificationCount && this.lastUnreadNotificationCount != this.userNotificationState.overview.unreadCount;
7963
+ const hasNewNotifications = this.lastNotificationDate !== this.userNotificationState.overview.lastNotificationDate;
7964
+ const countChanged = this.lastNotificationCount !== this.userNotificationState.overview.totalCount;
7965
+ const unreadCountChanged = this.lastUnreadNotificationCount !== this.userNotificationState.overview.unreadCount;
7753
7966
  if (hasNewNotifications || countChanged || unreadCountChanged) {
7754
7967
  if (this.autoLoadNotificationChanges) {
7755
7968
  try {
@@ -7762,24 +7975,25 @@ class NhUserNotificationsAbstractComponent extends NhCollectionBaseComponent {
7762
7975
  this.onNewNotificationsAvailable(state).catch((error) => {
7763
7976
  console.error('Error handling user notification state change:', error);
7764
7977
  });
7978
+ this.captureOverview(state);
7765
7979
  }
7766
7980
  });
7767
7981
  }
7768
7982
  onNewNotificationsAvailable(state) {
7769
7983
  return Promise.resolve();
7770
7984
  }
7771
- ngOnInit() {
7772
- super.ngOnInit();
7985
+ async appOnInit() {
7986
+ await super.appOnInit();
7773
7987
  if (this.allowStart) {
7774
7988
  this.userNotificationService.start();
7775
7989
  }
7776
7990
  }
7777
- ngOnDestroy() {
7778
- super.ngOnDestroy();
7991
+ async appOnDestroy() {
7779
7992
  if (this.allowStop) {
7780
7993
  this.userNotificationService.stop();
7781
7994
  }
7782
7995
  this.userNotificationSubscription?.unsubscribe();
7996
+ await super.appOnDestroy();
7783
7997
  }
7784
7998
  getInitialRequestOptions() {
7785
7999
  return new NhUserNotificationCollectionHttpRequestOptions({
@@ -7793,9 +8007,7 @@ class NhUserNotificationsAbstractComponent extends NhCollectionBaseComponent {
7793
8007
  return this.userNotificationService.getNotifications(requestOptions);
7794
8008
  }
7795
8009
  async afterLoad() {
7796
- this.lastNotificationDate = this.userNotificationState.overview.lastNotificationDate;
7797
- this.lastNotificationCount = this.userNotificationState.overview.totalCount;
7798
- this.lastUnreadNotificationCount = this.userNotificationState.overview.unreadCount;
8010
+ this.captureOverview(this.userNotificationState);
7799
8011
  }
7800
8012
  async reload() {
7801
8013
  const currentPage = this.requestOptions.page;
@@ -7868,6 +8080,11 @@ class NhUserNotificationsAbstractComponent extends NhCollectionBaseComponent {
7868
8080
  await this.firstPage();
7869
8081
  return taskResult;
7870
8082
  }
8083
+ captureOverview(state) {
8084
+ this.lastNotificationDate = state.overview.lastNotificationDate;
8085
+ this.lastNotificationCount = state.overview.totalCount;
8086
+ this.lastUnreadNotificationCount = state.overview.unreadCount;
8087
+ }
7871
8088
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhUserNotificationsAbstractComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7872
8089
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.28", type: NhUserNotificationsAbstractComponent, isStandalone: false, selector: "nh-user-notifications-abstract", inputs: { allowStart: { classPropertyName: "allowStart", publicName: "allowStart", isSignal: false, isRequired: false, transformFunction: null }, allowStop: { classPropertyName: "allowStop", publicName: "allowStop", isSignal: false, isRequired: false, transformFunction: null }, itemsPerPage: { classPropertyName: "itemsPerPage", publicName: "itemsPerPage", isSignal: false, isRequired: false, transformFunction: null }, autoLoadNotificationChanges: { classPropertyName: "autoLoadNotificationChanges", publicName: "autoLoadNotificationChanges", isSignal: false, isRequired: false, transformFunction: null }, partialLocalStorageKey: { classPropertyName: "partialLocalStorageKey", publicName: "partialLocalStorageKey", isSignal: true, isRequired: false, transformFunction: null } }, usesInheritance: true, ngImport: i0, template: ``, isInline: true }); }
7873
8090
  }
@@ -7888,6 +8105,72 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImpo
7888
8105
  type: Input
7889
8106
  }], partialLocalStorageKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "partialLocalStorageKey", required: false }] }] } });
7890
8107
 
8108
+ class NhBackgroundOperationProgressComponent {
8109
+ constructor() {
8110
+ this.showEvents = true;
8111
+ this.showActions = false;
8112
+ this.cancelRequested = new EventEmitter();
8113
+ this.retryRequested = new EventEmitter();
8114
+ }
8115
+ statusKey(status) {
8116
+ return `nh-background-operations.status.${nhBackgroundOperationTranslationSegment(nhBackgroundOperationStatusName(status))}`;
8117
+ }
8118
+ stepStatusKey(status) {
8119
+ return `nh-background-operations.step-status.${nhBackgroundOperationTranslationSegment(nhBackgroundOperationStepStatusName(status))}`;
8120
+ }
8121
+ stepTitle(step) {
8122
+ return step.titleKey || step.stepKey;
8123
+ }
8124
+ percentage(value) {
8125
+ if (value === undefined || value === null || !Number.isFinite(value)) {
8126
+ return 0;
8127
+ }
8128
+ return Math.max(0, Math.min(100, value));
8129
+ }
8130
+ arguments(json) {
8131
+ if (!json) {
8132
+ return {};
8133
+ }
8134
+ try {
8135
+ const value = JSON.parse(json);
8136
+ return typeof value === 'object' && value !== null ? value : {};
8137
+ }
8138
+ catch {
8139
+ return {};
8140
+ }
8141
+ }
8142
+ hasBatch(step) {
8143
+ return step.discoveredItems > 0 || step.processedItems > 0 || step.activeItems > 0;
8144
+ }
8145
+ childLabel(child) {
8146
+ return child.fanOutItemKey || child.operationType;
8147
+ }
8148
+ canCancel(status) {
8149
+ return !['Succeeded', 'Failed', 'Cancelled', 'TimedOut'].includes(nhBackgroundOperationStatusName(status));
8150
+ }
8151
+ canRetry(operation) {
8152
+ return !operation.sensitiveDataRedactedAt
8153
+ && ['Failed', 'Cancelled', 'TimedOut'].includes(nhBackgroundOperationStatusName(operation.status));
8154
+ }
8155
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhBackgroundOperationProgressComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8156
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: NhBackgroundOperationProgressComponent, isStandalone: true, selector: "nh-background-operation-progress", inputs: { operation: "operation", showEvents: "showEvents", showActions: "showActions" }, outputs: { cancelRequested: "cancelRequested", retryRequested: "retryRequested" }, ngImport: i0, template: "<section class=\"nh-operation\" [attr.data-status]=\"statusKey(operation.status)\">\n <header class=\"nh-operation__header\">\n <div>\n <p class=\"nh-operation__eyebrow\">{{ 'nh-background-operations.operation' | translate }}</p>\n <h2>{{ operation.operationType }}</h2>\n </div>\n <span class=\"nh-operation__status\">{{ statusKey(operation.status) | translate }}</span>\n </header>\n\n <div class=\"nh-operation__overall\">\n <div class=\"nh-operation__progress-label\">\n <span>{{ operation.progressMessageKey ? (operation.progressMessageKey | translate: arguments(operation.progressMessageArgumentsJson)) : ('nh-background-operations.progress' | translate) }}</span>\n <strong>{{ percentage(operation.progressPercentage) | number:'1.0-1' }}%</strong>\n </div>\n <div class=\"nh-operation__progress\" role=\"progressbar\"\n [attr.aria-label]=\"'nh-background-operations.overall-progress' | translate\"\n aria-valuemin=\"0\" aria-valuemax=\"100\"\n [attr.aria-valuenow]=\"percentage(operation.progressPercentage)\">\n <span [style.width.%]=\"percentage(operation.progressPercentage)\"></span>\n </div>\n </div>\n\n @if (showActions && (canCancel(operation.status) || canRetry(operation))) {\n <div class=\"nh-operation__actions\">\n @if (canCancel(operation.status)) {\n <button type=\"button\" (click)=\"cancelRequested.emit(operation.id)\">\n {{ 'nh-background-operations.cancel' | translate }}\n </button>\n }\n @if (canRetry(operation)) {\n <button type=\"button\" (click)=\"retryRequested.emit(operation.id)\">\n {{ 'nh-background-operations.retry' | translate }}\n </button>\n }\n </div>\n }\n\n <div class=\"nh-operation__steps\" role=\"list\" [attr.aria-label]=\"'nh-background-operations.steps' | translate\">\n <ng-template #renderStep let-step let-depth=\"depth\">\n <article class=\"nh-step\" role=\"listitem\" [style.--nh-step-depth]=\"depth\">\n <div class=\"nh-step__heading\">\n <div>\n <strong>{{ stepTitle(step) | translate: arguments(step.titleArgumentsJson) }}</strong>\n @if (step.messageKey) {\n <span>{{ step.messageKey | translate: arguments(step.messageArgumentsJson) }}</span>\n }\n </div>\n <span class=\"nh-step__status\">{{ stepStatusKey(step.status) | translate }}</span>\n </div>\n <div class=\"nh-step__progress\" role=\"progressbar\"\n [attr.aria-label]=\"stepTitle(step) | translate: arguments(step.titleArgumentsJson)\"\n aria-valuemin=\"0\" aria-valuemax=\"100\"\n [attr.aria-valuenow]=\"percentage(step.percentage)\">\n <span [style.width.%]=\"percentage(step.percentage)\"></span>\n </div>\n @if (hasBatch(step)) {\n <dl class=\"nh-step__batch\">\n <div><dt>{{ 'nh-background-operations.batch.processed' | translate }}</dt><dd>{{ step.processedItems }} / {{ step.discoveredItems }}</dd></div>\n <div><dt>{{ 'nh-background-operations.batch.succeeded' | translate }}</dt><dd>{{ step.succeededItems }}</dd></div>\n <div><dt>{{ 'nh-background-operations.batch.failed' | translate }}</dt><dd>{{ step.failedItems }}</dd></div>\n <div><dt>{{ 'nh-background-operations.batch.skipped' | translate }}</dt><dd>{{ step.skippedItems }}</dd></div>\n @if (step.retriedItems > 0) {\n <div><dt>{{ 'nh-background-operations.batch.retried' | translate }}</dt><dd>{{ step.retriedItems }}</dd></div>\n }\n @if (step.activeItems > 0) {\n <div><dt>{{ 'nh-background-operations.batch.active' | translate }}</dt><dd>{{ step.activeItems }}</dd></div>\n }\n </dl>\n }\n </article>\n @for (child of step.children; track child.id) {\n <ng-container *ngTemplateOutlet=\"renderStep; context: {$implicit: child, depth: depth + 1}\"></ng-container>\n }\n </ng-template>\n\n @for (step of operation.steps; track step.id) {\n <ng-container *ngTemplateOutlet=\"renderStep; context: {$implicit: step, depth: 0}\"></ng-container>\n } @empty {\n <p class=\"nh-operation__empty\">{{ 'nh-background-operations.no-steps' | translate }}</p>\n }\n </div>\n\n @if (operation.children.length > 0) {\n <section class=\"nh-operation__children\">\n <h3>{{ 'nh-background-operations.children' | translate }}</h3>\n <div role=\"list\" [attr.aria-label]=\"'nh-background-operations.children' | translate\">\n <ng-template #renderChild let-child let-depth=\"depth\">\n <article class=\"nh-child\" role=\"listitem\" [style.--nh-child-depth]=\"depth\">\n <div class=\"nh-child__heading\">\n <div>\n <strong>{{ childLabel(child) }}</strong>\n <span>{{ child.operationType }}</span>\n </div>\n <span class=\"nh-child__status\">{{ statusKey(child.status) | translate }}</span>\n </div>\n <div class=\"nh-step__progress\" role=\"progressbar\"\n [attr.aria-label]=\"childLabel(child)\"\n aria-valuemin=\"0\" aria-valuemax=\"100\"\n [attr.aria-valuenow]=\"percentage(child.progressPercentage)\">\n <span [style.width.%]=\"percentage(child.progressPercentage)\"></span>\n </div>\n @if (child.resultUrl) {\n <a [href]=\"child.resultUrl\">{{ 'nh-background-operations.open-result' | translate }}</a>\n }\n </article>\n @for (nestedChild of child.children; track nestedChild.id) {\n <ng-container *ngTemplateOutlet=\"renderChild; context: {$implicit: nestedChild, depth: depth + 1}\"></ng-container>\n }\n </ng-template>\n\n @for (child of operation.children; track child.id) {\n <ng-container *ngTemplateOutlet=\"renderChild; context: {$implicit: child, depth: 0}\"></ng-container>\n }\n </div>\n </section>\n }\n\n @if (showEvents && operation.events.length > 0) {\n <section class=\"nh-operation__events\">\n <h3>{{ 'nh-background-operations.activity' | translate }}</h3>\n <ol>\n @for (event of operation.events; track event.id) {\n @if (event.messageKey) {\n <li>\n <time [attr.datetime]=\"event.creationDateTime\">{{ event.creationDateTime | date:'short' }}</time>\n <span>{{ event.messageKey | translate: arguments(event.messageArgumentsJson) }}</span>\n @if (event.resultUrl) {\n <a [href]=\"event.resultUrl\">{{ 'nh-background-operations.open-result' | translate }}</a>\n }\n </li>\n }\n }\n </ol>\n </section>\n }\n</section>\n", styles: [":host{display:block;color:var(--nh-color-text, #fff)}.nh-operation{display:grid;gap:1rem;padding:1rem;border:1px solid var(--nh-color-border, rgba(255, 255, 255, .14));border-radius:1rem;background:var(--nh-color-surface, #101827)}.nh-operation__header,.nh-step__heading,.nh-child__heading,.nh-operation__progress-label,.nh-operation__actions{display:flex;align-items:center;justify-content:space-between;gap:.75rem}.nh-operation__header h2,.nh-operation__events h3,.nh-operation__children h3,.nh-operation__eyebrow,.nh-step__heading span,.nh-step__batch,.nh-operation__events ol{margin:0}.nh-operation__eyebrow{color:var(--nh-color-muted, #9aa8bc);font-size:.75rem;letter-spacing:.06em;text-transform:uppercase}.nh-operation__status,.nh-step__status{flex:none;padding:.25rem .5rem;border-radius:.4rem;background:var(--nh-color-status-background, rgba(2, 41, 98, .75));font-size:.75rem}.nh-operation__progress,.nh-step__progress{overflow:hidden;height:.5rem;border-radius:.25rem;background:var(--nh-color-progress-track, rgba(255, 255, 255, .1))}.nh-operation__progress span,.nh-step__progress span{display:block;height:100%;background:var(--nh-color-progress, #ff6700);transition:width .18s ease-out}.nh-operation__steps{display:grid;gap:.625rem}.nh-operation__children,.nh-operation__children>div{display:grid;gap:.625rem}.nh-child{display:grid;gap:.5rem;margin-inline-start:min(var(--nh-child-depth) * 1rem,3rem);padding:.75rem;border:1px solid var(--nh-color-border, rgba(255, 255, 255, .14));border-radius:.5rem;background:var(--nh-color-surface-subtle, rgba(255, 255, 255, .035))}.nh-child__heading{display:flex;align-items:center;justify-content:space-between;gap:.75rem}.nh-child__heading>div{display:grid;gap:.125rem}.nh-child__heading span{color:var(--nh-color-muted, #9aa8bc);font-size:.8rem}.nh-child__status{flex:none;padding:.25rem .5rem;border-radius:.4rem;background:var(--nh-color-status-background, rgba(2, 41, 98, .75));color:var(--nh-color-text, #fff);font-size:.75rem}.nh-child a{justify-self:start}.nh-step{display:grid;gap:.5rem;margin-inline-start:calc(var(--nh-step-depth) * 1rem);padding:.75rem;border-inline-start:2px solid var(--nh-color-progress, #ff6700);background:var(--nh-color-surface-subtle, rgba(255, 255, 255, .035))}.nh-step__heading>div{display:grid;gap:.125rem}.nh-step__heading span{color:var(--nh-color-muted, #9aa8bc);font-size:.8rem}.nh-step__batch{display:flex;flex-wrap:wrap;gap:.5rem 1rem;color:var(--nh-color-muted, #9aa8bc);font-size:.75rem}.nh-step__batch div{display:flex;gap:.25rem}.nh-step__batch dd{margin:0;color:var(--nh-color-text, #fff)}.nh-operation__actions{justify-content:flex-end}.nh-operation__actions button{min-height:2.25rem;padding:.4rem .75rem;border:1px solid var(--nh-color-border, rgba(255, 255, 255, .2));border-radius:.5rem;color:inherit;background:transparent}.nh-operation__actions button:focus-visible,.nh-child a:focus-visible,.nh-operation__events a:focus-visible{outline:2px solid var(--nh-color-progress, #ff6700);outline-offset:2px}.nh-operation__events ol{display:grid;gap:.5rem;padding:0;list-style:none}.nh-operation__events li{display:grid;grid-template-columns:minmax(7rem,auto) 1fr auto;gap:.75rem;color:var(--nh-color-muted, #9aa8bc);font-size:.8rem}.nh-operation__events li span{color:var(--nh-color-text, #fff)}@media(prefers-reduced-motion:reduce){.nh-operation__progress span,.nh-step__progress span{transition:none}}@media(max-width:40rem){.nh-operation__header,.nh-step__heading,.nh-child__heading,.nh-operation__events li{align-items:flex-start;grid-template-columns:1fr}.nh-operation__header,.nh-step__heading{flex-direction:column}.nh-child__heading{align-items:flex-start;flex-direction:column}.nh-step{margin-inline-start:min(var(--nh-step-depth) * .5rem,1.5rem)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1$3.DecimalPipe, name: "number" }, { kind: "pipe", type: i1$3.DatePipe, name: "date" }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
8157
+ }
8158
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhBackgroundOperationProgressComponent, decorators: [{
8159
+ type: Component,
8160
+ args: [{ selector: 'nh-background-operation-progress', standalone: true, imports: [CommonModule, TranslateModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<section class=\"nh-operation\" [attr.data-status]=\"statusKey(operation.status)\">\n <header class=\"nh-operation__header\">\n <div>\n <p class=\"nh-operation__eyebrow\">{{ 'nh-background-operations.operation' | translate }}</p>\n <h2>{{ operation.operationType }}</h2>\n </div>\n <span class=\"nh-operation__status\">{{ statusKey(operation.status) | translate }}</span>\n </header>\n\n <div class=\"nh-operation__overall\">\n <div class=\"nh-operation__progress-label\">\n <span>{{ operation.progressMessageKey ? (operation.progressMessageKey | translate: arguments(operation.progressMessageArgumentsJson)) : ('nh-background-operations.progress' | translate) }}</span>\n <strong>{{ percentage(operation.progressPercentage) | number:'1.0-1' }}%</strong>\n </div>\n <div class=\"nh-operation__progress\" role=\"progressbar\"\n [attr.aria-label]=\"'nh-background-operations.overall-progress' | translate\"\n aria-valuemin=\"0\" aria-valuemax=\"100\"\n [attr.aria-valuenow]=\"percentage(operation.progressPercentage)\">\n <span [style.width.%]=\"percentage(operation.progressPercentage)\"></span>\n </div>\n </div>\n\n @if (showActions && (canCancel(operation.status) || canRetry(operation))) {\n <div class=\"nh-operation__actions\">\n @if (canCancel(operation.status)) {\n <button type=\"button\" (click)=\"cancelRequested.emit(operation.id)\">\n {{ 'nh-background-operations.cancel' | translate }}\n </button>\n }\n @if (canRetry(operation)) {\n <button type=\"button\" (click)=\"retryRequested.emit(operation.id)\">\n {{ 'nh-background-operations.retry' | translate }}\n </button>\n }\n </div>\n }\n\n <div class=\"nh-operation__steps\" role=\"list\" [attr.aria-label]=\"'nh-background-operations.steps' | translate\">\n <ng-template #renderStep let-step let-depth=\"depth\">\n <article class=\"nh-step\" role=\"listitem\" [style.--nh-step-depth]=\"depth\">\n <div class=\"nh-step__heading\">\n <div>\n <strong>{{ stepTitle(step) | translate: arguments(step.titleArgumentsJson) }}</strong>\n @if (step.messageKey) {\n <span>{{ step.messageKey | translate: arguments(step.messageArgumentsJson) }}</span>\n }\n </div>\n <span class=\"nh-step__status\">{{ stepStatusKey(step.status) | translate }}</span>\n </div>\n <div class=\"nh-step__progress\" role=\"progressbar\"\n [attr.aria-label]=\"stepTitle(step) | translate: arguments(step.titleArgumentsJson)\"\n aria-valuemin=\"0\" aria-valuemax=\"100\"\n [attr.aria-valuenow]=\"percentage(step.percentage)\">\n <span [style.width.%]=\"percentage(step.percentage)\"></span>\n </div>\n @if (hasBatch(step)) {\n <dl class=\"nh-step__batch\">\n <div><dt>{{ 'nh-background-operations.batch.processed' | translate }}</dt><dd>{{ step.processedItems }} / {{ step.discoveredItems }}</dd></div>\n <div><dt>{{ 'nh-background-operations.batch.succeeded' | translate }}</dt><dd>{{ step.succeededItems }}</dd></div>\n <div><dt>{{ 'nh-background-operations.batch.failed' | translate }}</dt><dd>{{ step.failedItems }}</dd></div>\n <div><dt>{{ 'nh-background-operations.batch.skipped' | translate }}</dt><dd>{{ step.skippedItems }}</dd></div>\n @if (step.retriedItems > 0) {\n <div><dt>{{ 'nh-background-operations.batch.retried' | translate }}</dt><dd>{{ step.retriedItems }}</dd></div>\n }\n @if (step.activeItems > 0) {\n <div><dt>{{ 'nh-background-operations.batch.active' | translate }}</dt><dd>{{ step.activeItems }}</dd></div>\n }\n </dl>\n }\n </article>\n @for (child of step.children; track child.id) {\n <ng-container *ngTemplateOutlet=\"renderStep; context: {$implicit: child, depth: depth + 1}\"></ng-container>\n }\n </ng-template>\n\n @for (step of operation.steps; track step.id) {\n <ng-container *ngTemplateOutlet=\"renderStep; context: {$implicit: step, depth: 0}\"></ng-container>\n } @empty {\n <p class=\"nh-operation__empty\">{{ 'nh-background-operations.no-steps' | translate }}</p>\n }\n </div>\n\n @if (operation.children.length > 0) {\n <section class=\"nh-operation__children\">\n <h3>{{ 'nh-background-operations.children' | translate }}</h3>\n <div role=\"list\" [attr.aria-label]=\"'nh-background-operations.children' | translate\">\n <ng-template #renderChild let-child let-depth=\"depth\">\n <article class=\"nh-child\" role=\"listitem\" [style.--nh-child-depth]=\"depth\">\n <div class=\"nh-child__heading\">\n <div>\n <strong>{{ childLabel(child) }}</strong>\n <span>{{ child.operationType }}</span>\n </div>\n <span class=\"nh-child__status\">{{ statusKey(child.status) | translate }}</span>\n </div>\n <div class=\"nh-step__progress\" role=\"progressbar\"\n [attr.aria-label]=\"childLabel(child)\"\n aria-valuemin=\"0\" aria-valuemax=\"100\"\n [attr.aria-valuenow]=\"percentage(child.progressPercentage)\">\n <span [style.width.%]=\"percentage(child.progressPercentage)\"></span>\n </div>\n @if (child.resultUrl) {\n <a [href]=\"child.resultUrl\">{{ 'nh-background-operations.open-result' | translate }}</a>\n }\n </article>\n @for (nestedChild of child.children; track nestedChild.id) {\n <ng-container *ngTemplateOutlet=\"renderChild; context: {$implicit: nestedChild, depth: depth + 1}\"></ng-container>\n }\n </ng-template>\n\n @for (child of operation.children; track child.id) {\n <ng-container *ngTemplateOutlet=\"renderChild; context: {$implicit: child, depth: 0}\"></ng-container>\n }\n </div>\n </section>\n }\n\n @if (showEvents && operation.events.length > 0) {\n <section class=\"nh-operation__events\">\n <h3>{{ 'nh-background-operations.activity' | translate }}</h3>\n <ol>\n @for (event of operation.events; track event.id) {\n @if (event.messageKey) {\n <li>\n <time [attr.datetime]=\"event.creationDateTime\">{{ event.creationDateTime | date:'short' }}</time>\n <span>{{ event.messageKey | translate: arguments(event.messageArgumentsJson) }}</span>\n @if (event.resultUrl) {\n <a [href]=\"event.resultUrl\">{{ 'nh-background-operations.open-result' | translate }}</a>\n }\n </li>\n }\n }\n </ol>\n </section>\n }\n</section>\n", styles: [":host{display:block;color:var(--nh-color-text, #fff)}.nh-operation{display:grid;gap:1rem;padding:1rem;border:1px solid var(--nh-color-border, rgba(255, 255, 255, .14));border-radius:1rem;background:var(--nh-color-surface, #101827)}.nh-operation__header,.nh-step__heading,.nh-child__heading,.nh-operation__progress-label,.nh-operation__actions{display:flex;align-items:center;justify-content:space-between;gap:.75rem}.nh-operation__header h2,.nh-operation__events h3,.nh-operation__children h3,.nh-operation__eyebrow,.nh-step__heading span,.nh-step__batch,.nh-operation__events ol{margin:0}.nh-operation__eyebrow{color:var(--nh-color-muted, #9aa8bc);font-size:.75rem;letter-spacing:.06em;text-transform:uppercase}.nh-operation__status,.nh-step__status{flex:none;padding:.25rem .5rem;border-radius:.4rem;background:var(--nh-color-status-background, rgba(2, 41, 98, .75));font-size:.75rem}.nh-operation__progress,.nh-step__progress{overflow:hidden;height:.5rem;border-radius:.25rem;background:var(--nh-color-progress-track, rgba(255, 255, 255, .1))}.nh-operation__progress span,.nh-step__progress span{display:block;height:100%;background:var(--nh-color-progress, #ff6700);transition:width .18s ease-out}.nh-operation__steps{display:grid;gap:.625rem}.nh-operation__children,.nh-operation__children>div{display:grid;gap:.625rem}.nh-child{display:grid;gap:.5rem;margin-inline-start:min(var(--nh-child-depth) * 1rem,3rem);padding:.75rem;border:1px solid var(--nh-color-border, rgba(255, 255, 255, .14));border-radius:.5rem;background:var(--nh-color-surface-subtle, rgba(255, 255, 255, .035))}.nh-child__heading{display:flex;align-items:center;justify-content:space-between;gap:.75rem}.nh-child__heading>div{display:grid;gap:.125rem}.nh-child__heading span{color:var(--nh-color-muted, #9aa8bc);font-size:.8rem}.nh-child__status{flex:none;padding:.25rem .5rem;border-radius:.4rem;background:var(--nh-color-status-background, rgba(2, 41, 98, .75));color:var(--nh-color-text, #fff);font-size:.75rem}.nh-child a{justify-self:start}.nh-step{display:grid;gap:.5rem;margin-inline-start:calc(var(--nh-step-depth) * 1rem);padding:.75rem;border-inline-start:2px solid var(--nh-color-progress, #ff6700);background:var(--nh-color-surface-subtle, rgba(255, 255, 255, .035))}.nh-step__heading>div{display:grid;gap:.125rem}.nh-step__heading span{color:var(--nh-color-muted, #9aa8bc);font-size:.8rem}.nh-step__batch{display:flex;flex-wrap:wrap;gap:.5rem 1rem;color:var(--nh-color-muted, #9aa8bc);font-size:.75rem}.nh-step__batch div{display:flex;gap:.25rem}.nh-step__batch dd{margin:0;color:var(--nh-color-text, #fff)}.nh-operation__actions{justify-content:flex-end}.nh-operation__actions button{min-height:2.25rem;padding:.4rem .75rem;border:1px solid var(--nh-color-border, rgba(255, 255, 255, .2));border-radius:.5rem;color:inherit;background:transparent}.nh-operation__actions button:focus-visible,.nh-child a:focus-visible,.nh-operation__events a:focus-visible{outline:2px solid var(--nh-color-progress, #ff6700);outline-offset:2px}.nh-operation__events ol{display:grid;gap:.5rem;padding:0;list-style:none}.nh-operation__events li{display:grid;grid-template-columns:minmax(7rem,auto) 1fr auto;gap:.75rem;color:var(--nh-color-muted, #9aa8bc);font-size:.8rem}.nh-operation__events li span{color:var(--nh-color-text, #fff)}@media(prefers-reduced-motion:reduce){.nh-operation__progress span,.nh-step__progress span{transition:none}}@media(max-width:40rem){.nh-operation__header,.nh-step__heading,.nh-child__heading,.nh-operation__events li{align-items:flex-start;grid-template-columns:1fr}.nh-operation__header,.nh-step__heading{flex-direction:column}.nh-child__heading{align-items:flex-start;flex-direction:column}.nh-step{margin-inline-start:min(var(--nh-step-depth) * .5rem,1.5rem)}}\n"] }]
8161
+ }], propDecorators: { operation: [{
8162
+ type: Input,
8163
+ args: [{ required: true }]
8164
+ }], showEvents: [{
8165
+ type: Input
8166
+ }], showActions: [{
8167
+ type: Input
8168
+ }], cancelRequested: [{
8169
+ type: Output
8170
+ }], retryRequested: [{
8171
+ type: Output
8172
+ }] } });
8173
+
7891
8174
  class NhSearchInputComponent {
7892
8175
  constructor() {
7893
8176
  this.value = '';
@@ -8123,7 +8406,6 @@ class NhCommonModule {
8123
8406
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhCommonModule, providers: [
8124
8407
  provideAppInitializer(() => {
8125
8408
  const configService = inject(NhConfigCommonService);
8126
- const nhSentryTraceService = inject(NhSentryTraceService);
8127
8409
  return new Observable((observer) => {
8128
8410
  //
8129
8411
  // We use APP_INITIALIZER to load the configuration before the application starts. (Cuz DEPS calls for AppConfigService it is loaded soon in the lifecycle of the app.)
@@ -8163,14 +8445,6 @@ class NhCommonModule {
8163
8445
  useClass: NhDeduplicateGetRequestsInterceptor,
8164
8446
  multi: true
8165
8447
  },
8166
- // Tracing
8167
- {
8168
- provide: Sentry.TraceService,
8169
- useFactory: (nhSentryTraceService) => {
8170
- return nhSentryTraceService.sentryTraceService;
8171
- },
8172
- deps: [NhSentryTraceService]
8173
- },
8174
8448
  //Error handlers
8175
8449
  {
8176
8450
  provide: ErrorHandler, // Make support for multiple error handlers
@@ -8181,11 +8455,6 @@ class NhCommonModule {
8181
8455
  useClass: ErrorHandler,
8182
8456
  multi: true,
8183
8457
  },
8184
- {
8185
- provide: NH_ERROR_HANDLERS, // Register Sentry error handler
8186
- useClass: NhErrorHandlerSentryService,
8187
- multi: true,
8188
- },
8189
8458
  provideHttpClient(withInterceptorsFromDi(), withFetch()),
8190
8459
  // Guards
8191
8460
  NhCanCancelNavigationGuard,
@@ -8289,7 +8558,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImpo
8289
8558
  providers: [
8290
8559
  provideAppInitializer(() => {
8291
8560
  const configService = inject(NhConfigCommonService);
8292
- const nhSentryTraceService = inject(NhSentryTraceService);
8293
8561
  return new Observable((observer) => {
8294
8562
  //
8295
8563
  // We use APP_INITIALIZER to load the configuration before the application starts. (Cuz DEPS calls for AppConfigService it is loaded soon in the lifecycle of the app.)
@@ -8329,14 +8597,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImpo
8329
8597
  useClass: NhDeduplicateGetRequestsInterceptor,
8330
8598
  multi: true
8331
8599
  },
8332
- // Tracing
8333
- {
8334
- provide: Sentry.TraceService,
8335
- useFactory: (nhSentryTraceService) => {
8336
- return nhSentryTraceService.sentryTraceService;
8337
- },
8338
- deps: [NhSentryTraceService]
8339
- },
8340
8600
  //Error handlers
8341
8601
  {
8342
8602
  provide: ErrorHandler, // Make support for multiple error handlers
@@ -8347,11 +8607,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImpo
8347
8607
  useClass: ErrorHandler,
8348
8608
  multi: true,
8349
8609
  },
8350
- {
8351
- provide: NH_ERROR_HANDLERS, // Register Sentry error handler
8352
- useClass: NhErrorHandlerSentryService,
8353
- multi: true,
8354
- },
8355
8610
  provideHttpClient(withInterceptorsFromDi(), withFetch()),
8356
8611
  // Guards
8357
8612
  NhCanCancelNavigationGuard,
@@ -8448,5 +8703,5 @@ function nhTranslateBrowserLoaderFactory(httpClient, transferState) {
8448
8703
  * Generated bundle index. Do not edit.
8449
8704
  */
8450
8705
 
8451
- export { AbstractValueAccessor, AspMvcFormServerSideFormValidator, AuthSessionExpirationInformation, AuthenticateModel, AuthenticationNhCommonModuleConfig, AuthenticationSessionCreateResponse, BaseNhAuthService, CheckAuthenticateSessionModel, Claim, ClaimAuthenticateSessionAccountMutateModel, ClaimAuthenticateSessionAccountViewModel, ClaimTypes, CollectionHttpRequestOptions, CollectionHttpResponse, DefaultMultiSelectSettings, DefaultMultiSelectTexts, EndpointsAuthenticationNhCommonModuleConfig, FilterRequestOptions, FormError, FormValidationResult, HttpDownloadRequestOptions, HttpRequestOptions, HttpRequestOptionsArrayBuffer, HttpRequestOptionsText, ImpersonateAuthenticateModel, IsAllPermissionsGrantedPipe, IsAuthenticatedPipe, IsClaimGrantedPipe, IsDefined, IsGrantedRolePipe, IsOneActiveDivisionPermissionGrantedGuard, IsOneActiveDivisionPermissionGrantedPipe, IsOneActiveDivisionRoleGrantedGuard, IsOneActiveDivisionRoleGrantedPipe, IsOneClaimGrantedPipe, IsOneDivisionPermissionGrantedPipe, IsOneDivisionRoleGrantedPipe, IsOnePermissionGrantedPipe, IsOneRoleGrantedPipe, JsonLdDataChanged, ListSortDirection, MakeProvider, MutationType, NH_ERROR_HANDLERS, NH_ROUTER_LANGUAGE_CHANGE_METHOD, NH_ROUTER_ROOT_ROUTES, NH_SENTRY_BEFORE_BREADCRUMB_HOOKS, NH_SENTRY_BEFORE_SEND_HOOKS, NH_SENTRY_BEFORE_SEND_LOG_HOOKS, NH_SENTRY_BEFORE_SEND_SPAN_HOOKS, NH_SENTRY_BEFORE_SEND_TRANSACTION_HOOKS, NhAccountInformationResponse, NhAngularUtil, NhApiService, NhApiUtil, NhAppService, NhAsyncLock, NhAuthService, NhAuthorization, NhBaseApiService, NhBooleanToStringPipe, NhBreadCrumb, NhBreadCrumbItem, NhButtonDebounceDirective, NhCanCancelNavigationGuard, NhCollectionBaseComponent, NhCollectionTypeBaseComponent, NhCommonConfig, NhCommonConfigChanged, NhCommonModule, NhCommonModuleConfig, NhConfigCommonService, NhContextMenu, NhContextMenuItem, NhContextMenuService, NhContextMenuServiceConfig, NhCookieService, NhDatePipe, NhDateTimePipe, NhDateUtcPipe, NhDebounceDirective, NhDeduplicateGetRequestsInterceptor, NhDivision, NhDivisionRole, NhDivisionUser, NhEncodeHttpParamsInterceptor, NhEncodingUtil, NhErrorComponent, NhErrorHandlerSentryService, NhErrorHandlerService, NhErrorLoggingNhCommonModuleConfig, NhFormDropDownComponent, NhFormDropDownNhCommonModuleConfig, NhFormDropDownSettings, NhFormErrorMessageComponent, NhFormHelper, NhHeadService, NhHttpNhCommonModuleConfig, NhHttpUtil, NhInternetConnectionService, NhIsAllPermissionsGrantedGuard, NhIsAuthenticatedGuard, NhIsOneClaimGrantedGuard, NhIsOnePermissionGrantedGuard, NhIsOneRoleGrantedGuard, NhJsonLdComponent, NhJsonLdData, NhJsonLdDataItem, NhJsonLdService, NhLoaderComponent, NhMetaService, NhModalComponent, NhModalComponentClosed, NhModalComponentImpl, NhModalComponentRef, NhModalConfirmComponent, NhModalContentDirective, NhModalLoadingComponent, NhModalMutateBaseComponent, NhModalMutateBaseTypeComponent, NhModalOptions, NhModalService, NhMutateBaseComponent, NhMutateBaseTypeComponent, NhMutex, NhPageBaseComponent, NhPageService, NhPageSettings, NhPageSizeComponent, NhPageTypeBaseComponent, NhRegisterRoute, NhRequestScopedServerHttpInterceptorService, NhRoute, NhRouteInfo, NhRouteNavigationItem, NhRouterLinkDirective, NhRouterService, NhRouterSetupService, NhSearchInputComponent, NhSentryErrorLoggingNhCommonModuleConfig, NhSentryInitializerService, NhSentryService, NhSentryTraceService, NhServerHttpInterceptor, NhServerService, NhServerSideFormValidationService, NhSitemap, NhSitemapEntry, NhSitemapEntryItem, NhStringUtil, NhTaskResultFormValidationService, NhTitleService, NhToHeadDirective, NhTranslateBrowserLoader, NhTranslationNhCommonModuleConfig, NhTypedPageBaseComponent, NhTypedPageTypeBaseComponent, NhUrlEncodePipe, NhUser, NhUserNotification, NhUserNotificationCollectionHttpRequestOptions, NhUserNotificationData, NhUserNotificationMessage, NhUserNotificationOverview, NhUserNotificationService, NhUserNotificationState, NhUserNotificationsAbstractComponent, OrderByRequestOptions, PreConnectUrlItem, PreLoadUrlItem, REVIEW_AGGREGATE_RATING_KEY, REVIEW_KEY, RefreshTokenLoginAccountMutateModel, RevertImpersonateAuthenticateModel, SafeHtmlPipe, SafeResourceUrlPipe, SafeUrlPipe, SearchableCollectionHttpRequestOptions, SimpleCollectionHttpRequestOptions, SimpleCollectionHttpResponse, TaskResult, TaskResultItem, TypedNhPageSettings, UserNotificationNhCommonModuleConfig, enumIntValuesToArray, enumKeysToArray, enumStringValuesToArray, enumValuesToArray, getEmptyGuid, getRandomIdentifier, groupBy, languageToCultureMap, nameof, nhTranslateBrowserLoaderFactory, uppercaseFirst };
8706
+ export { AbstractValueAccessor, AspMvcFormServerSideFormValidator, AuthSessionExpirationInformation, AuthenticateModel, AuthenticationNhCommonModuleConfig, AuthenticationSessionCreateResponse, BackgroundOperationsNhCommonModuleConfig, BaseNhAuthService, CheckAuthenticateSessionModel, Claim, ClaimAuthenticateSessionAccountMutateModel, ClaimAuthenticateSessionAccountViewModel, ClaimTypes, CollectionHttpRequestOptions, CollectionHttpResponse, DefaultMultiSelectSettings, DefaultMultiSelectTexts, EndpointsAuthenticationNhCommonModuleConfig, FilterRequestOptions, FormError, FormValidationResult, HttpDownloadRequestOptions, HttpRequestOptions, HttpRequestOptionsArrayBuffer, HttpRequestOptionsText, ImpersonateAuthenticateModel, IsAllPermissionsGrantedPipe, IsAuthenticatedPipe, IsClaimGrantedPipe, IsDefined, IsGrantedRolePipe, IsOneActiveDivisionPermissionGrantedGuard, IsOneActiveDivisionPermissionGrantedPipe, IsOneActiveDivisionRoleGrantedGuard, IsOneActiveDivisionRoleGrantedPipe, IsOneClaimGrantedPipe, IsOneDivisionPermissionGrantedPipe, IsOneDivisionRoleGrantedPipe, IsOnePermissionGrantedPipe, IsOneRoleGrantedPipe, JsonLdDataChanged, ListSortDirection, MakeProvider, MutationType, NH_ERROR_HANDLERS, NH_ROUTER_LANGUAGE_CHANGE_METHOD, NH_ROUTER_ROOT_ROUTES, NhAccountInformationResponse, NhAngularUtil, NhApiService, NhApiUtil, NhAppService, NhAsyncLock, NhAuthService, NhAuthorization, NhBackgroundOperationCollectionHttpRequestOptions, NhBackgroundOperationLiveUpdateService, NhBackgroundOperationProgressComponent, NhBackgroundOperationService, NhBackgroundOperationStore, NhBaseApiService, NhBooleanToStringPipe, NhBreadCrumb, NhBreadCrumbItem, NhButtonDebounceDirective, NhCanCancelNavigationGuard, NhCollectionBaseComponent, NhCollectionTypeBaseComponent, NhCommonConfig, NhCommonConfigChanged, NhCommonModule, NhCommonModuleConfig, NhConfigCommonService, NhContextMenu, NhContextMenuItem, NhContextMenuService, NhContextMenuServiceConfig, NhCookieService, NhDatePipe, NhDateTimePipe, NhDateUtcPipe, NhDebounceDirective, NhDeduplicateGetRequestsInterceptor, NhDivision, NhDivisionRole, NhDivisionUser, NhEncodeHttpParamsInterceptor, NhEncodingUtil, NhErrorComponent, NhErrorHandlerService, NhFormDropDownComponent, NhFormDropDownNhCommonModuleConfig, NhFormDropDownSettings, NhFormErrorMessageComponent, NhFormHelper, NhHeadService, NhHttpNhCommonModuleConfig, NhHttpUtil, NhInternetConnectionService, NhIsAllPermissionsGrantedGuard, NhIsAuthenticatedGuard, NhIsOneClaimGrantedGuard, NhIsOnePermissionGrantedGuard, NhIsOneRoleGrantedGuard, NhJsonLdComponent, NhJsonLdData, NhJsonLdDataItem, NhJsonLdService, NhLoaderComponent, NhMetaService, NhModalComponent, NhModalComponentClosed, NhModalComponentImpl, NhModalComponentRef, NhModalConfirmComponent, NhModalContentDirective, NhModalLoadingComponent, NhModalMutateBaseComponent, NhModalMutateBaseTypeComponent, NhModalOptions, NhModalService, NhMutateBaseComponent, NhMutateBaseTypeComponent, NhMutex, NhPageBaseComponent, NhPageService, NhPageSettings, NhPageSizeComponent, NhPageTypeBaseComponent, NhRegisterRoute, NhRequestScopedServerHttpInterceptorService, NhRoute, NhRouteInfo, NhRouteNavigationItem, NhRouterLinkDirective, NhRouterService, NhRouterSetupService, NhSearchInputComponent, NhServerHttpInterceptor, NhServerService, NhServerSideFormValidationService, NhSitemap, NhSitemapEntry, NhSitemapEntryItem, NhStringUtil, NhTaskResultFormValidationService, NhTitleService, NhToHeadDirective, NhTranslateBrowserLoader, NhTranslationNhCommonModuleConfig, NhTypedPageBaseComponent, NhTypedPageTypeBaseComponent, NhUrlEncodePipe, NhUser, NhUserNotification, NhUserNotificationCollectionHttpRequestOptions, NhUserNotificationData, NhUserNotificationMessage, NhUserNotificationOverview, NhUserNotificationService, NhUserNotificationState, NhUserNotificationsAbstractComponent, OrderByRequestOptions, PreConnectUrlItem, PreLoadUrlItem, REVIEW_AGGREGATE_RATING_KEY, REVIEW_KEY, RefreshTokenLoginAccountMutateModel, RevertImpersonateAuthenticateModel, SafeHtmlPipe, SafeResourceUrlPipe, SafeUrlPipe, SearchableCollectionHttpRequestOptions, SimpleCollectionHttpRequestOptions, SimpleCollectionHttpResponse, TaskResult, TaskResultItem, TypedNhPageSettings, UserNotificationNhCommonModuleConfig, enumIntValuesToArray, enumKeysToArray, enumStringValuesToArray, enumValuesToArray, getEmptyGuid, getRandomIdentifier, groupBy, languageToCultureMap, nameof, nhBackgroundOperationStatusName, nhBackgroundOperationStepStatusName, nhBackgroundOperationTranslationSegment, nhTranslateBrowserLoaderFactory, uppercaseFirst };
8452
8707
  //# sourceMappingURL=newheap-platform-common.mjs.map