@byuhbll/components 5.0.1 → 5.0.2

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.
@@ -1,4 +1,4 @@
1
- import { Component, computed, EventEmitter, input, Output } from '@angular/core';
1
+ import { Component, computed, EventEmitter, input, Output, effect, runInInjectionContext, inject, Injector, } from '@angular/core';
2
2
  import { HbllHeaderComponent } from '../hbll-header/hbll-header.component';
3
3
  import { ImpersonationBannerComponent } from '../impersonation-banner/impersonation-banner.component';
4
4
  import { defaultOidcBaseUri, defaultOidcDefaultIdp, ImpersonateModalComponent, } from '../impersonate-modal/impersonate-modal.component';
@@ -24,6 +24,85 @@ export class HeaderWithImpersonationComponent {
24
24
  : false);
25
25
  this.showImpersonationModal = false;
26
26
  this.isImpersonating = computed(() => !!this.parsedToken()?.['impersonator']);
27
+ // Inactivity timeout for impersonation
28
+ this.activityEvents = [
29
+ 'keydown',
30
+ 'pointerdown',
31
+ 'wheel',
32
+ 'scroll',
33
+ ];
34
+ this.inactivityTimerId = null;
35
+ this.inactivityTimeoutMs = 5 * 60 * 1000; // 5 minutes
36
+ this.debounceTimerId = null;
37
+ this.debounceDelayMs = 250;
38
+ this.trackingActive = false;
39
+ this.injector = inject(Injector);
40
+ /** Reset the inactivity countdown (no-op if not impersonating) */
41
+ this.resetInactivityTimer = () => {
42
+ if (!this.isImpersonating())
43
+ return;
44
+ if (this.inactivityTimerId)
45
+ clearTimeout(this.inactivityTimerId);
46
+ this.inactivityTimerId = window.setTimeout(() => {
47
+ this.endImpersonation.emit();
48
+ this.stopInactivityTracking();
49
+ }, this.inactivityTimeoutMs);
50
+ };
51
+ /** Debounce activity to avoid hammering resets during event storms */
52
+ this.debouncedResetTimer = () => {
53
+ if (this.debounceTimerId)
54
+ clearTimeout(this.debounceTimerId);
55
+ this.debounceTimerId = window.setTimeout(() => {
56
+ this.resetInactivityTimer();
57
+ }, this.debounceDelayMs);
58
+ };
59
+ }
60
+ ngOnInit() {
61
+ // effect can only be used within an injection context (ex: constructor)
62
+ runInInjectionContext(this.injector, () => {
63
+ effect(() => {
64
+ if (this.isImpersonating()) {
65
+ if (!this.trackingActive)
66
+ this.startInactivityTracking();
67
+ }
68
+ else {
69
+ if (this.trackingActive)
70
+ this.stopInactivityTracking();
71
+ }
72
+ });
73
+ });
74
+ }
75
+ ngOnDestroy() {
76
+ this.stopInactivityTracking();
77
+ }
78
+ /** Begin listening and start countdown */
79
+ startInactivityTracking() {
80
+ this.activityEvents.forEach((event) => {
81
+ // 'scroll' on document doesn't bubble; use window + capture to catch nested scrolls
82
+ const target = event === 'scroll' ? window : document;
83
+ const options = event === 'scroll'
84
+ ? { passive: true, capture: true }
85
+ : { passive: true };
86
+ target.addEventListener(event, this.debouncedResetTimer, options);
87
+ });
88
+ this.trackingActive = true;
89
+ this.resetInactivityTimer();
90
+ }
91
+ /** Remove listeners and clear timers */
92
+ stopInactivityTracking() {
93
+ this.activityEvents.forEach((event) => {
94
+ const target = event === 'scroll' ? window : document;
95
+ target.removeEventListener(event, this.debouncedResetTimer);
96
+ });
97
+ if (this.inactivityTimerId) {
98
+ clearTimeout(this.inactivityTimerId);
99
+ this.inactivityTimerId = null;
100
+ }
101
+ if (this.debounceTimerId) {
102
+ clearTimeout(this.debounceTimerId);
103
+ this.debounceTimerId = null;
104
+ }
105
+ this.trackingActive = false;
27
106
  }
28
107
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: HeaderWithImpersonationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
29
108
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.1.0", type: HeaderWithImpersonationComponent, isStandalone: true, selector: "lib-header-with-impersonation", inputs: { accessTokenPayload: { classPropertyName: "accessTokenPayload", publicName: "accessTokenPayload", isSignal: true, isRequired: true, transformFunction: null }, oidcBaseUri: { classPropertyName: "oidcBaseUri", publicName: "oidcBaseUri", isSignal: true, isRequired: false, transformFunction: null }, oidcDefaultIdp: { classPropertyName: "oidcDefaultIdp", publicName: "oidcDefaultIdp", isSignal: true, isRequired: false, transformFunction: null }, mainSiteBaseUrl: { classPropertyName: "mainSiteBaseUrl", publicName: "mainSiteBaseUrl", isSignal: true, isRequired: false, transformFunction: null }, personBaseUri: { classPropertyName: "personBaseUri", publicName: "personBaseUri", isSignal: true, isRequired: false, transformFunction: null }, myAccountApiBaseUri: { classPropertyName: "myAccountApiBaseUri", publicName: "myAccountApiBaseUri", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { login: "login", logout: "logout", endImpersonation: "endImpersonation" }, ngImport: i0, template: "<lib-impersonation-banner\n [accessTokenPayload]=\"accessTokenPayload()\"\n (endImpersonation)=\"endImpersonation.emit()\"\n [personBaseUri]=\"personBaseUri()\"\n [myAccountApiBaseUri]=\"myAccountApiBaseUri()\"\n></lib-impersonation-banner>\n<lib-hbll-header\n [name]=\"name()\"\n [showImpersonateButton]=\"showImpersonateButton()\"\n (openImpersonationModal)=\"showImpersonationModal = true\"\n (login)=\"login.emit()\"\n (logout)=\"logout.emit()\"\n [mainsitebaseurl]=\"mainSiteBaseUrl()\"\n/>\n<lib-impersonate-modal\n [showModal]=\"showImpersonationModal\"\n [oidcBaseUri]=\"oidcBaseUri()\"\n [oidcDefaultIdp]=\"oidcDefaultIdp()\"\n [accessTokenPayload]=\"accessTokenPayload()\"\n (dismiss)=\"showImpersonationModal = false\"\n (init)=\"showImpersonationModal = true\"\n></lib-impersonate-modal>\n", styles: [""], dependencies: [{ kind: "component", type: HbllHeaderComponent, selector: "lib-hbll-header", inputs: ["name", "mainsitebaseurl", "showImpersonateButton"], outputs: ["openImpersonationModal", "login", "logout"] }, { kind: "component", type: ImpersonationBannerComponent, selector: "lib-impersonation-banner", inputs: ["accessTokenPayload", "personBaseUri", "myAccountApiBaseUri"], outputs: ["endImpersonation"] }, { kind: "component", type: ImpersonateModalComponent, selector: "lib-impersonate-modal", inputs: ["showModal", "oidcBaseUri", "oidcDefaultIdp", "accessTokenPayload"], outputs: ["dismiss", "init"] }] }); }
@@ -38,4 +117,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.0", ngImpor
38
117
  }], endImpersonation: [{
39
118
  type: Output
40
119
  }] } });
41
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVhZGVyLXdpdGgtaW1wZXJzb25hdGlvbi5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9jb21wb25lbnRzL3NyYy9saWIvaGVhZGVyLXdpdGgtaW1wZXJzb25hdGlvbi9oZWFkZXItd2l0aC1pbXBlcnNvbmF0aW9uLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL2NvbXBvbmVudHMvc3JjL2xpYi9oZWFkZXItd2l0aC1pbXBlcnNvbmF0aW9uL2hlYWRlci13aXRoLWltcGVyc29uYXRpb24uY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBRSxRQUFRLEVBQUUsWUFBWSxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQVUsTUFBTSxlQUFlLENBQUM7QUFDekYsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sc0NBQXNDLENBQUM7QUFDM0UsT0FBTyxFQUFFLDRCQUE0QixFQUFFLE1BQU0sd0RBQXdELENBQUM7QUFDdEcsT0FBTyxFQUNILGtCQUFrQixFQUNsQixxQkFBcUIsRUFDckIseUJBQXlCLEdBQzVCLE1BQU0sa0RBQWtELENBQUM7QUFFMUQsT0FBTyxFQUFFLFNBQVMsRUFBYyxNQUFNLFlBQVksQ0FBQzs7QUFTbkQsTUFBTSxPQUFPLGdDQUFnQztJQVA3QztRQVFJLHVCQUFrQixHQUFHLEtBQUssQ0FBQyxRQUFRLEVBQWdCLENBQUM7UUFDcEQsZ0JBQVcsR0FBRyxLQUFLLENBQUMsa0JBQWtCLENBQUMsQ0FBQztRQUN4QyxtQkFBYyxHQUFHLEtBQUssQ0FBQyxxQkFBcUIsQ0FBQyxDQUFDO1FBQzlDLG9CQUFlLEdBQUcsS0FBSyxDQUFDLHFCQUFxQixDQUFDLENBQUM7UUFDL0Msa0JBQWEsR0FBRyxLQUFLLENBQUMscUNBQXFDLENBQUMsQ0FBQztRQUM3RCx3QkFBbUIsR0FBRyxLQUFLLENBQUMsNEJBQTRCLENBQUMsQ0FBQztRQUVoRCxVQUFLLEdBQUcsSUFBSSxZQUFZLEVBQVEsQ0FBQztRQUNqQyxXQUFNLEdBQUcsSUFBSSxZQUFZLEVBQVEsQ0FBQztRQUNsQyxxQkFBZ0IsR0FBRyxJQUFJLFlBQVksRUFBUSxDQUFDO1FBRXRELDhEQUE4RDtRQUNwRCxnQkFBVyxHQUFzRCxRQUFRLENBQUMsR0FBRyxFQUFFLENBQ3JGLElBQUksQ0FBQyxrQkFBa0IsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxrQkFBa0IsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQ3RGLENBQUM7UUFDUSxTQUFJLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsV0FBVyxFQUFHLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFDckYsMEJBQXFCLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRSxDQUM1QyxJQUFJLENBQUMsV0FBVyxFQUFFO1lBQ2QsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUNFLENBQUMsSUFBSSxDQUFDLGVBQWUsRUFBRTtnQkFDdkIsSUFBSSxDQUFDLFdBQVcsRUFBRyxDQUFDLGlCQUFpQixDQUFDLENBQUMsa0JBQWtCLENBQUMsRUFBRSxDQUFDLE9BQU8sQ0FBQyxFQUFFLFFBQVEsQ0FDM0UsZUFBZSxDQUNsQixDQUNKO1lBQ0gsQ0FBQyxDQUFDLEtBQUssQ0FDZCxDQUFDO1FBQ1EsMkJBQXNCLEdBQUcsS0FBSyxDQUFDO1FBQ2pDLG9CQUFlLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsV0FBVyxFQUFFLEVBQUUsQ0FBQyxjQUFjLENBQUMsQ0FBQyxDQUFDO0tBQ3BGOzhHQTdCWSxnQ0FBZ0M7a0dBQWhDLGdDQUFnQyw2akNDbEI3QyxtMUJBc0JBLDBERFJjLG1CQUFtQixrTEFBRSw0QkFBNEIsNEtBQUUseUJBQXlCOzsyRkFJN0UsZ0NBQWdDO2tCQVA1QyxTQUFTOytCQUNJLCtCQUErQixjQUM3QixJQUFJLFdBQ1AsQ0FBQyxtQkFBbUIsRUFBRSw0QkFBNEIsRUFBRSx5QkFBeUIsQ0FBQzs4QkFZN0UsS0FBSztzQkFBZCxNQUFNO2dCQUNHLE1BQU07c0JBQWYsTUFBTTtnQkFDRyxnQkFBZ0I7c0JBQXpCLE1BQU0iLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21wb25lbnQsIGNvbXB1dGVkLCBFdmVudEVtaXR0ZXIsIGlucHV0LCBPdXRwdXQsIFNpZ25hbCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgSGJsbEhlYWRlckNvbXBvbmVudCB9IGZyb20gJy4uL2hibGwtaGVhZGVyL2hibGwtaGVhZGVyLmNvbXBvbmVudCc7XG5pbXBvcnQgeyBJbXBlcnNvbmF0aW9uQmFubmVyQ29tcG9uZW50IH0gZnJvbSAnLi4vaW1wZXJzb25hdGlvbi1iYW5uZXIvaW1wZXJzb25hdGlvbi1iYW5uZXIuY29tcG9uZW50JztcbmltcG9ydCB7XG4gICAgZGVmYXVsdE9pZGNCYXNlVXJpLFxuICAgIGRlZmF1bHRPaWRjRGVmYXVsdElkcCxcbiAgICBJbXBlcnNvbmF0ZU1vZGFsQ29tcG9uZW50LFxufSBmcm9tICcuLi9pbXBlcnNvbmF0ZS1tb2RhbC9pbXBlcnNvbmF0ZS1tb2RhbC5jb21wb25lbnQnO1xuaW1wb3J0IHsgVG9rZW5QYXlsb2FkIH0gZnJvbSAnLi4vbW9kZWxzL3Rva2VuLXBheWxvYWQnO1xuaW1wb3J0IHsgand0RGVjb2RlLCBKd3RQYXlsb2FkIH0gZnJvbSAnand0LWRlY29kZSc7XG5cbkBDb21wb25lbnQoe1xuICAgIHNlbGVjdG9yOiAnbGliLWhlYWRlci13aXRoLWltcGVyc29uYXRpb24nLFxuICAgIHN0YW5kYWxvbmU6IHRydWUsXG4gICAgaW1wb3J0czogW0hibGxIZWFkZXJDb21wb25lbnQsIEltcGVyc29uYXRpb25CYW5uZXJDb21wb25lbnQsIEltcGVyc29uYXRlTW9kYWxDb21wb25lbnRdLFxuICAgIHRlbXBsYXRlVXJsOiAnLi9oZWFkZXItd2l0aC1pbXBlcnNvbmF0aW9uLmNvbXBvbmVudC5odG1sJyxcbiAgICBzdHlsZVVybDogJy4vaGVhZGVyLXdpdGgtaW1wZXJzb25hdGlvbi5jb21wb25lbnQuc2NzcycsXG59KVxuZXhwb3J0IGNsYXNzIEhlYWRlcldpdGhJbXBlcnNvbmF0aW9uQ29tcG9uZW50IHtcbiAgICBhY2Nlc3NUb2tlblBheWxvYWQgPSBpbnB1dC5yZXF1aXJlZDxUb2tlblBheWxvYWQ+KCk7XG4gICAgb2lkY0Jhc2VVcmkgPSBpbnB1dChkZWZhdWx0T2lkY0Jhc2VVcmkpO1xuICAgIG9pZGNEZWZhdWx0SWRwID0gaW5wdXQoZGVmYXVsdE9pZGNEZWZhdWx0SWRwKTtcbiAgICBtYWluU2l0ZUJhc2VVcmwgPSBpbnB1dCgnaHR0cHM6Ly9saWIuYnl1LmVkdScpO1xuICAgIHBlcnNvbkJhc2VVcmkgPSBpbnB1dCgnaHR0cHM6Ly9hcHBzLmxpYi5ieXUuZWR1L3BlcnNvbi92Mi8nKTtcbiAgICBteUFjY291bnRBcGlCYXNlVXJpID0gaW5wdXQoJ2h0dHBzOi8vYXBpLmxpYi5ieXUuZWR1L3YxJyk7XG5cbiAgICBAT3V0cHV0KCkgbG9naW4gPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gICAgQE91dHB1dCgpIGxvZ291dCA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgICBAT3V0cHV0KCkgZW5kSW1wZXJzb25hdGlvbiA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcblxuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tZXhwbGljaXQtYW55XG4gICAgcHJvdGVjdGVkIHBhcnNlZFRva2VuOiBTaWduYWw8KEp3dFBheWxvYWQgJiBSZWNvcmQ8c3RyaW5nLCBhbnk+KSB8IG51bGw+ID0gY29tcHV0ZWQoKCkgPT5cbiAgICAgICAgdGhpcy5hY2Nlc3NUb2tlblBheWxvYWQoKS50b2tlbiA/IGp3dERlY29kZSh0aGlzLmFjY2Vzc1Rva2VuUGF5bG9hZCgpLnRva2VuKSA6IG51bGwsXG4gICAgKTtcbiAgICBwcm90ZWN0ZWQgbmFtZSA9IGNvbXB1dGVkKCgpID0+ICh0aGlzLnBhcnNlZFRva2VuKCkgPyB0aGlzLnBhcnNlZFRva2VuKCkhWydnaXZlbl9uYW1lJ10gOiAnJykpO1xuICAgIHByb3RlY3RlZCBzaG93SW1wZXJzb25hdGVCdXR0b24gPSBjb21wdXRlZCgoKSA9PlxuICAgICAgICB0aGlzLnBhcnNlZFRva2VuKClcbiAgICAgICAgICAgID8gISEoXG4gICAgICAgICAgICAgICAgICAhdGhpcy5pc0ltcGVyc29uYXRpbmcoKSAmJlxuICAgICAgICAgICAgICAgICAgdGhpcy5wYXJzZWRUb2tlbigpIVsncmVzb3VyY2VfYWNjZXNzJ11bJ3JlYWxtLW1hbmFnZW1lbnQnXT8uWydyb2xlcyddPy5pbmNsdWRlcyhcbiAgICAgICAgICAgICAgICAgICAgICAnaW1wZXJzb25hdGlvbicsXG4gICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgIClcbiAgICAgICAgICAgIDogZmFsc2UsXG4gICAgKTtcbiAgICBwcm90ZWN0ZWQgc2hvd0ltcGVyc29uYXRpb25Nb2RhbCA9IGZhbHNlO1xuICAgIHByaXZhdGUgaXNJbXBlcnNvbmF0aW5nID0gY29tcHV0ZWQoKCkgPT4gISF0aGlzLnBhcnNlZFRva2VuKCk/LlsnaW1wZXJzb25hdG9yJ10pO1xufVxuIiwiPGxpYi1pbXBlcnNvbmF0aW9uLWJhbm5lclxuICAgIFthY2Nlc3NUb2tlblBheWxvYWRdPVwiYWNjZXNzVG9rZW5QYXlsb2FkKClcIlxuICAgIChlbmRJbXBlcnNvbmF0aW9uKT1cImVuZEltcGVyc29uYXRpb24uZW1pdCgpXCJcbiAgICBbcGVyc29uQmFzZVVyaV09XCJwZXJzb25CYXNlVXJpKClcIlxuICAgIFtteUFjY291bnRBcGlCYXNlVXJpXT1cIm15QWNjb3VudEFwaUJhc2VVcmkoKVwiXG4+PC9saWItaW1wZXJzb25hdGlvbi1iYW5uZXI+XG48bGliLWhibGwtaGVhZGVyXG4gICAgW25hbWVdPVwibmFtZSgpXCJcbiAgICBbc2hvd0ltcGVyc29uYXRlQnV0dG9uXT1cInNob3dJbXBlcnNvbmF0ZUJ1dHRvbigpXCJcbiAgICAob3BlbkltcGVyc29uYXRpb25Nb2RhbCk9XCJzaG93SW1wZXJzb25hdGlvbk1vZGFsID0gdHJ1ZVwiXG4gICAgKGxvZ2luKT1cImxvZ2luLmVtaXQoKVwiXG4gICAgKGxvZ291dCk9XCJsb2dvdXQuZW1pdCgpXCJcbiAgICBbbWFpbnNpdGViYXNldXJsXT1cIm1haW5TaXRlQmFzZVVybCgpXCJcbi8+XG48bGliLWltcGVyc29uYXRlLW1vZGFsXG4gICAgW3Nob3dNb2RhbF09XCJzaG93SW1wZXJzb25hdGlvbk1vZGFsXCJcbiAgICBbb2lkY0Jhc2VVcmldPVwib2lkY0Jhc2VVcmkoKVwiXG4gICAgW29pZGNEZWZhdWx0SWRwXT1cIm9pZGNEZWZhdWx0SWRwKClcIlxuICAgIFthY2Nlc3NUb2tlblBheWxvYWRdPVwiYWNjZXNzVG9rZW5QYXlsb2FkKClcIlxuICAgIChkaXNtaXNzKT1cInNob3dJbXBlcnNvbmF0aW9uTW9kYWwgPSBmYWxzZVwiXG4gICAgKGluaXQpPVwic2hvd0ltcGVyc29uYXRpb25Nb2RhbCA9IHRydWVcIlxuPjwvbGliLWltcGVyc29uYXRlLW1vZGFsPlxuIl19
120
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVhZGVyLXdpdGgtaW1wZXJzb25hdGlvbi5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9jb21wb25lbnRzL3NyYy9saWIvaGVhZGVyLXdpdGgtaW1wZXJzb25hdGlvbi9oZWFkZXItd2l0aC1pbXBlcnNvbmF0aW9uLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL2NvbXBvbmVudHMvc3JjL2xpYi9oZWFkZXItd2l0aC1pbXBlcnNvbmF0aW9uL2hlYWRlci13aXRoLWltcGVyc29uYXRpb24uY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUNILFNBQVMsRUFDVCxRQUFRLEVBQ1IsWUFBWSxFQUNaLEtBQUssRUFDTCxNQUFNLEVBSU4sTUFBTSxFQUNOLHFCQUFxQixFQUNyQixNQUFNLEVBQ04sUUFBUSxHQUNYLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLHNDQUFzQyxDQUFDO0FBQzNFLE9BQU8sRUFBRSw0QkFBNEIsRUFBRSxNQUFNLHdEQUF3RCxDQUFDO0FBQ3RHLE9BQU8sRUFDSCxrQkFBa0IsRUFDbEIscUJBQXFCLEVBQ3JCLHlCQUF5QixHQUM1QixNQUFNLGtEQUFrRCxDQUFDO0FBRTFELE9BQU8sRUFBRSxTQUFTLEVBQWMsTUFBTSxZQUFZLENBQUM7O0FBU25ELE1BQU0sT0FBTyxnQ0FBZ0M7SUFQN0M7UUFRSSx1QkFBa0IsR0FBRyxLQUFLLENBQUMsUUFBUSxFQUFnQixDQUFDO1FBQ3BELGdCQUFXLEdBQUcsS0FBSyxDQUFDLGtCQUFrQixDQUFDLENBQUM7UUFDeEMsbUJBQWMsR0FBRyxLQUFLLENBQUMscUJBQXFCLENBQUMsQ0FBQztRQUM5QyxvQkFBZSxHQUFHLEtBQUssQ0FBQyxxQkFBcUIsQ0FBQyxDQUFDO1FBQy9DLGtCQUFhLEdBQUcsS0FBSyxDQUFDLHFDQUFxQyxD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O0lBQ2hDLHNCQUFzQjtRQUMxQixJQUFJLENBQUMsY0FBYyxDQUFDLE9BQU8sQ0FBQyxDQUFDLEtBQUssRUFBRSxFQUFFO1lBQ2xDLE1BQU0sTUFBTSxHQUFzQixLQUFLLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQztZQUN6RSxNQUFNLENBQUMsbUJBQW1CLENBQUMsS0FBSyxFQUFFLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDO1FBQ2hFLENBQUMsQ0FBQyxDQUFDO1FBRUgsSUFBSSxJQUFJLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztZQUN6QixZQUFZLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLENBQUM7WUFDckMsSUFBSSxDQUFDLGlCQUFpQixHQUFHLElBQUksQ0FBQztRQUNsQyxDQUFDO1FBQ0QsSUFBSSxJQUFJLENBQUMsZUFBZSxFQUFFLENBQUM7WUFDdkIsWUFBWSxDQUFDLElBQUksQ0FBQyxlQUFlLENBQUMsQ0FBQztZQUNuQyxJQUFJLENBQUMsZUFBZSxHQUFHLElBQUksQ0FBQztRQUNoQyxDQUFDO1FBRUQsSUFBSSxDQUFDLGNBQWMsR0FBRyxLQUFLLENBQUM7SUFDaEMsQ0FBQzs4R0FsR1EsZ0NBQWdDO2tHQUFoQyxnQ0FBZ0MsNmpDQy9CN0MsbTFCQXNCQSwwRERLYyxtQkFBbUIsa0xBQUUsNEJBQTRCLDRLQUFFLHlCQUF5Qjs7MkZBSTdFLGdDQUFnQztrQkFQNUMsU0FBUzsrQkFDSSwrQkFBK0IsY0FDN0IsSUFBSSxXQUNQLENBQUMsbUJBQW1CLEVBQUUsNEJBQTRCLEVBQUUseUJBQXlCLENBQUM7OEJBWTdFLEtBQUs7c0JBQWQsTUFBTTtnQkFDRyxNQUFNO3NCQUFmLE1BQU07Z0JBQ0csZ0JBQWdCO3NCQUF6QixNQUFNIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgICBDb21wb25lbnQsXG4gICAgY29tcHV0ZWQsXG4gICAgRXZlbnRFbWl0dGVyLFxuICAgIGlucHV0LFxuICAgIE91dHB1dCxcbiAgICBTaWduYWwsXG4gICAgT25Jbml0LFxuICAgIE9uRGVzdHJveSxcbiAgICBlZmZlY3QsXG4gICAgcnVuSW5JbmplY3Rpb25Db250ZXh0LFxuICAgIGluamVjdCxcbiAgICBJbmplY3Rvcixcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBIYmxsSGVhZGVyQ29tcG9uZW50IH0gZnJvbSAnLi4vaGJsbC1oZWFkZXIvaGJsbC1oZWFkZXIuY29tcG9uZW50JztcbmltcG9ydCB7IEltcGVyc29uYXRpb25CYW5uZXJDb21wb25lbnQgfSBmcm9tICcuLi9pbXBlcnNvbmF0aW9uLWJhbm5lci9pbXBlcnNvbmF0aW9uLWJhbm5lci5jb21wb25lbnQnO1xuaW1wb3J0IHtcbiAgICBkZWZhdWx0T2lkY0Jhc2VVcmksXG4gICAgZGVmYXVsdE9pZGNEZWZhdWx0SWRwLFxuICAgIEltcGVyc29uYXRlTW9kYWxDb21wb25lbnQsXG59IGZyb20gJy4uL2ltcGVyc29uYXRlLW1vZGFsL2ltcGVyc29uYXRlLW1vZGFsLmNvbXBvbmVudCc7XG5pbXBvcnQgeyBUb2tlblBheWxvYWQgfSBmcm9tICcuLi9tb2RlbHMvdG9rZW4tcGF5bG9hZCc7XG5pbXBvcnQgeyBqd3REZWNvZGUsIEp3dFBheWxvYWQgfSBmcm9tICdqd3QtZGVjb2RlJztcblxuQENvbXBvbmVudCh7XG4gICAgc2VsZWN0b3I6ICdsaWItaGVhZGVyLXdpdGgtaW1wZXJzb25hdGlvbicsXG4gICAgc3RhbmRhbG9uZTogdHJ1ZSxcbiAgICBpbXBvcnRzOiBbSGJsbEhlYWRlckNvbXBvbmVudCwgSW1wZXJzb25hdGlvbkJhbm5lckNvbXBvbmVudCwgSW1wZXJzb25hdGVNb2RhbENvbXBvbmVudF0sXG4gICAgdGVtcGxhdGVVcmw6ICcuL2hlYWRlci13aXRoLWltcGVyc29uYXRpb24uY29tcG9uZW50Lmh0bWwnLFxuICAgIHN0eWxlVXJsOiAnLi9oZWFkZXItd2l0aC1pbXBlcnNvbmF0aW9uLmNvbXBvbmVudC5zY3NzJyxcbn0pXG5leHBvcnQgY2xhc3MgSGVhZGVyV2l0aEltcGVyc29uYXRpb25Db21wb25lbnQgaW1wbGVtZW50cyBPbkluaXQsIE9uRGVzdHJveSB7XG4gICAgYWNjZXNzVG9rZW5QYXlsb2FkID0gaW5wdXQucmVxdWlyZWQ8VG9rZW5QYXlsb2FkPigpO1xuICAgIG9pZGNCYXNlVXJpID0gaW5wdXQoZGVmYXVsdE9pZGNCYXNlVXJpKTtcbiAgICBvaWRjRGVmYXVsdElkcCA9IGlucHV0KGRlZmF1bHRPaWRjRGVmYXVsdElkcCk7XG4gICAgbWFpblNpdGVCYXNlVXJsID0gaW5wdXQoJ2h0dHBzOi8vbGliLmJ5dS5lZHUnKTtcbiAgICBwZXJzb25CYXNlVXJpID0gaW5wdXQoJ2h0dHBzOi8vYXBwcy5saWIuYnl1LmVkdS9wZXJzb24vdjIvJyk7XG4gICAgbXlBY2NvdW50QXBpQmFzZVVyaSA9IGlucHV0KCdodHRwczovL2FwaS5saWIuYnl1LmVkdS92MScpO1xuXG4gICAgQE91dHB1dCgpIGxvZ2luID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICAgIEBPdXRwdXQoKSBsb2dvdXQgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gICAgQE91dHB1dCgpIGVuZEltcGVyc29uYXRpb24gPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG5cbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLWV4cGxpY2l0LWFueVxuICAgIHByb3RlY3RlZCBwYXJzZWRUb2tlbjogU2lnbmFsPChKd3RQYXlsb2FkICYgUmVjb3JkPHN0cmluZywgYW55PikgfCBudWxsPiA9IGNvbXB1dGVkKCgpID0+XG4gICAgICAgIHRoaXMuYWNjZXNzVG9rZW5QYXlsb2FkKCkudG9rZW4gPyBqd3REZWNvZGUodGhpcy5hY2Nlc3NUb2tlblBheWxvYWQoKS50b2tlbikgOiBudWxsLFxuICAgICk7XG4gICAgcHJvdGVjdGVkIG5hbWUgPSBjb21wdXRlZCgoKSA9PiAodGhpcy5wYXJzZWRUb2tlbigpID8gdGhpcy5wYXJzZWRUb2tlbigpIVsnZ2l2ZW5fbmFtZSddIDogJycpKTtcbiAgICBwcm90ZWN0ZWQgc2hvd0ltcGVyc29uYXRlQnV0dG9uID0gY29tcHV0ZWQoKCkgPT5cbiAgICAgICAgdGhpcy5wYXJzZWRUb2tlbigpXG4gICAgICAgICAgICA/ICEhKFxuICAgICAgICAgICAgICAgICAgIXRoaXMuaXNJbXBlcnNvbmF0aW5nKCkgJiZcbiAgICAgICAgICAgICAgICAgIHRoaXMucGFyc2VkVG9rZW4oKSFbJ3Jlc291cmNlX2FjY2VzcyddWydyZWFsbS1tYW5hZ2VtZW50J10/Llsncm9sZXMnXT8uaW5jbHVkZXMoXG4gICAgICAgICAgICAgICAgICAgICAgJ2ltcGVyc29uYXRpb24nLFxuICAgICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICApXG4gICAgICAgICAgICA6IGZhbHNlLFxuICAgICk7XG5cbiAgICBwcm90ZWN0ZWQgc2hvd0ltcGVyc29uYXRpb25Nb2RhbCA9IGZhbHNlO1xuICAgIHByaXZhdGUgaXNJbXBlcnNvbmF0aW5nID0gY29tcHV0ZWQoKCkgPT4gISF0aGlzLnBhcnNlZFRva2VuKCk/LlsnaW1wZXJzb25hdG9yJ10pO1xuXG4gICAgLy8gSW5hY3Rpdml0eSB0aW1lb3V0IGZvciBpbXBlcnNvbmF0aW9uXG4gICAgcHJpdmF0ZSBhY3Rpdml0eUV2ZW50czogQXJyYXk8J2tleWRvd24nIHwgJ3BvaW50ZXJkb3duJyB8ICd3aGVlbCcgfCAnc2Nyb2xsJz4gPSBbXG4gICAgICAgICdrZXlkb3duJyxcbiAgICAgICAgJ3BvaW50ZXJkb3duJyxcbiAgICAgICAgJ3doZWVsJyxcbiAgICAgICAgJ3Njcm9sbCcsXG4gICAgXTtcbiAgICBwcml2YXRlIGluYWN0aXZpdHlUaW1lcklkOiBudW1iZXIgfCBudWxsID0gbnVsbDtcbiAgICBwcml2YXRlIGluYWN0aXZpdHlUaW1lb3V0TXMgPSA1ICogNjAgKiAxMDAwOyAvLyA1IG1pbnV0ZXNcbiAgICBwcml2YXRlIGRlYm91bmNlVGltZXJJZDogbnVtYmVyIHwgbnVsbCA9IG51bGw7XG4gICAgcHJpdmF0ZSBkZWJvdW5jZURlbGF5TXMgPSAyNTA7XG5cbiAgICBwcml2YXRlIHRyYWNraW5nQWN0aXZlID0gZmFsc2U7XG5cbiAgICBwcml2YXRlIGluamVjdG9yID0gaW5qZWN0KEluamVjdG9yKTtcblxuICAgIG5nT25Jbml0KCkge1xuICAgICAgICAvLyBlZmZlY3QgY2FuIG9ubHkgYmUgdXNlZCB3aXRoaW4gYW4gaW5qZWN0aW9uIGNvbnRleHQgKGV4OiBjb25zdHJ1Y3RvcilcbiAgICAgICAgcnVuSW5JbmplY3Rpb25Db250ZXh0KHRoaXMuaW5qZWN0b3IsICgpID0+IHtcbiAgICAgICAgICAgIGVmZmVjdCgoKSA9PiB7XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuaXNJbXBlcnNvbmF0aW5nKCkpIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKCF0aGlzLnRyYWNraW5nQWN0aXZlKSB0aGlzLnN0YXJ0SW5hY3Rpdml0eVRyYWNraW5nKCk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMudHJhY2tpbmdBY3RpdmUpIHRoaXMuc3RvcEluYWN0aXZpdHlUcmFja2luZygpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBuZ09uRGVzdHJveSgpIHtcbiAgICAgICAgdGhpcy5zdG9wSW5hY3Rpdml0eVRyYWNraW5nKCk7XG4gICAgfVxuXG4gICAgLyoqIEJlZ2luIGxpc3RlbmluZyBhbmQgc3RhcnQgY291bnRkb3duICovXG4gICAgcHJpdmF0ZSBzdGFydEluYWN0aXZpdHlUcmFja2luZygpIHtcbiAgICAgICAgdGhpcy5hY3Rpdml0eUV2ZW50cy5mb3JFYWNoKChldmVudCkgPT4ge1xuICAgICAgICAgICAgLy8gJ3Njcm9sbCcgb24gZG9jdW1lbnQgZG9lc24ndCBidWJibGU7IHVzZSB3aW5kb3cgKyBjYXB0dXJlIHRvIGNhdGNoIG5lc3RlZCBzY3JvbGxzXG4gICAgICAgICAgICBjb25zdCB0YXJnZXQ6IFdpbmRvdyB8IERvY3VtZW50ID0gZXZlbnQgPT09ICdzY3JvbGwnID8gd2luZG93IDogZG9jdW1lbnQ7XG4gICAgICAgICAgICBjb25zdCBvcHRpb25zID1cbiAgICAgICAgICAgICAgICBldmVudCA9PT0gJ3Njcm9sbCdcbiAgICAgICAgICAgICAgICAgICAgPyAoeyBwYXNzaXZlOiB0cnVlLCBjYXB0dXJlOiB0cnVlIH0gYXMgQWRkRXZlbnRMaXN0ZW5lck9wdGlvbnMpXG4gICAgICAgICAgICAgICAgICAgIDogKHsgcGFzc2l2ZTogdHJ1ZSB9IGFzIEFkZEV2ZW50TGlzdGVuZXJPcHRpb25zKTtcblxuICAgICAgICAgICAgdGFyZ2V0LmFkZEV2ZW50TGlzdGVuZXIoZXZlbnQsIHRoaXMuZGVib3VuY2VkUmVzZXRUaW1lciwgb3B0aW9ucyk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHRoaXMudHJhY2tpbmdBY3RpdmUgPSB0cnVlO1xuICAgICAgICB0aGlzLnJlc2V0SW5hY3Rpdml0eVRpbWVyKCk7XG4gICAgfVxuXG4gICAgLyoqIFJlbW92ZSBsaXN0ZW5lcnMgYW5kIGNsZWFyIHRpbWVycyAqL1xuICAgIHByaXZhdGUgc3RvcEluYWN0aXZpdHlUcmFja2luZygpIHtcbiAgICAgICAgdGhpcy5hY3Rpdml0eUV2ZW50cy5mb3JFYWNoKChldmVudCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgdGFyZ2V0OiBXaW5kb3cgfCBEb2N1bWVudCA9IGV2ZW50ID09PSAnc2Nyb2xsJyA/IHdpbmRvdyA6IGRvY3VtZW50O1xuICAgICAgICAgICAgdGFyZ2V0LnJlbW92ZUV2ZW50TGlzdGVuZXIoZXZlbnQsIHRoaXMuZGVib3VuY2VkUmVzZXRUaW1lcik7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIGlmICh0aGlzLmluYWN0aXZpdHlUaW1lcklkKSB7XG4gICAgICAgICAgICBjbGVhclRpbWVvdXQodGhpcy5pbmFjdGl2aXR5VGltZXJJZCk7XG4gICAgICAgICAgICB0aGlzLmluYWN0aXZpdHlUaW1lcklkID0gbnVsbDtcbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy5kZWJvdW5jZVRpbWVySWQpIHtcbiAgICAgICAgICAgIGNsZWFyVGltZW91dCh0aGlzLmRlYm91bmNlVGltZXJJZCk7XG4gICAgICAgICAgICB0aGlzLmRlYm91bmNlVGltZXJJZCA9IG51bGw7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLnRyYWNraW5nQWN0aXZlID0gZmFsc2U7XG4gICAgfVxuXG4gICAgLyoqIFJlc2V0IHRoZSBpbmFjdGl2aXR5IGNvdW50ZG93biAobm8tb3AgaWYgbm90IGltcGVyc29uYXRpbmcpICovXG4gICAgcHJpdmF0ZSByZXNldEluYWN0aXZpdHlUaW1lciA9ICgpID0+IHtcbiAgICAgICAgaWYgKCF0aGlzLmlzSW1wZXJzb25hdGluZygpKSByZXR1cm47XG5cbiAgICAgICAgaWYgKHRoaXMuaW5hY3Rpdml0eVRpbWVySWQpIGNsZWFyVGltZW91dCh0aGlzLmluYWN0aXZpdHlUaW1lcklkKTtcblxuICAgICAgICB0aGlzLmluYWN0aXZpdHlUaW1lcklkID0gd2luZG93LnNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICAgICAgdGhpcy5lbmRJbXBlcnNvbmF0aW9uLmVtaXQoKTtcbiAgICAgICAgICAgIHRoaXMuc3RvcEluYWN0aXZpdHlUcmFja2luZygpO1xuICAgICAgICB9LCB0aGlzLmluYWN0aXZpdHlUaW1lb3V0TXMpO1xuICAgIH07XG5cbiAgICAvKiogRGVib3VuY2UgYWN0aXZpdHkgdG8gYXZvaWQgaGFtbWVyaW5nIHJlc2V0cyBkdXJpbmcgZXZlbnQgc3Rvcm1zICovXG4gICAgcHJpdmF0ZSBkZWJvdW5jZWRSZXNldFRpbWVyID0gKCkgPT4ge1xuICAgICAgICBpZiAodGhpcy5kZWJvdW5jZVRpbWVySWQpIGNsZWFyVGltZW91dCh0aGlzLmRlYm91bmNlVGltZXJJZCk7XG5cbiAgICAgICAgdGhpcy5kZWJvdW5jZVRpbWVySWQgPSB3aW5kb3cuc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgICB0aGlzLnJlc2V0SW5hY3Rpdml0eVRpbWVyKCk7XG4gICAgICAgIH0sIHRoaXMuZGVib3VuY2VEZWxheU1zKTtcbiAgICB9O1xufVxuIiwiPGxpYi1pbXBlcnNvbmF0aW9uLWJhbm5lclxuICAgIFthY2Nlc3NUb2tlblBheWxvYWRdPVwiYWNjZXNzVG9rZW5QYXlsb2FkKClcIlxuICAgIChlbmRJbXBlcnNvbmF0aW9uKT1cImVuZEltcGVyc29uYXRpb24uZW1pdCgpXCJcbiAgICBbcGVyc29uQmFzZVVyaV09XCJwZXJzb25CYXNlVXJpKClcIlxuICAgIFtteUFjY291bnRBcGlCYXNlVXJpXT1cIm15QWNjb3VudEFwaUJhc2VVcmkoKVwiXG4+PC9saWItaW1wZXJzb25hdGlvbi1iYW5uZXI+XG48bGliLWhibGwtaGVhZGVyXG4gICAgW25hbWVdPVwibmFtZSgpXCJcbiAgICBbc2hvd0ltcGVyc29uYXRlQnV0dG9uXT1cInNob3dJbXBlcnNvbmF0ZUJ1dHRvbigpXCJcbiAgICAob3BlbkltcGVyc29uYXRpb25Nb2RhbCk9XCJzaG93SW1wZXJzb25hdGlvbk1vZGFsID0gdHJ1ZVwiXG4gICAgKGxvZ2luKT1cImxvZ2luLmVtaXQoKVwiXG4gICAgKGxvZ291dCk9XCJsb2dvdXQuZW1pdCgpXCJcbiAgICBbbWFpbnNpdGViYXNldXJsXT1cIm1haW5TaXRlQmFzZVVybCgpXCJcbi8+XG48bGliLWltcGVyc29uYXRlLW1vZGFsXG4gICAgW3Nob3dNb2RhbF09XCJzaG93SW1wZXJzb25hdGlvbk1vZGFsXCJcbiAgICBbb2lkY0Jhc2VVcmldPVwib2lkY0Jhc2VVcmkoKVwiXG4gICAgW29pZGNEZWZhdWx0SWRwXT1cIm9pZGNEZWZhdWx0SWRwKClcIlxuICAgIFthY2Nlc3NUb2tlblBheWxvYWRdPVwiYWNjZXNzVG9rZW5QYXlsb2FkKClcIlxuICAgIChkaXNtaXNzKT1cInNob3dJbXBlcnNvbmF0aW9uTW9kYWwgPSBmYWxzZVwiXG4gICAgKGluaXQpPVwic2hvd0ltcGVyc29uYXRpb25Nb2RhbCA9IHRydWVcIlxuPjwvbGliLWltcGVyc29uYXRlLW1vZGFsPlxuIl19
@@ -3,7 +3,7 @@ import { CommonModule, DatePipe, DOCUMENT, LowerCasePipe, NgIf, NgClass } from '
3
3
  import { toSignal, toObservable } from '@angular/core/rxjs-interop';
4
4
  import { HttpClient } from '@angular/common/http';
5
5
  import * as i0 from '@angular/core';
6
- import { Component, ChangeDetectionStrategy, ViewChild, Input, input, EventEmitter, Output, inject, computed, ViewChildren, Pipe, Renderer2, viewChild, HostListener, ElementRef, ViewEncapsulation, booleanAttribute, createComponent, Injectable } from '@angular/core';
6
+ import { Component, ChangeDetectionStrategy, ViewChild, Input, input, EventEmitter, Output, inject, computed, ViewChildren, Pipe, Renderer2, viewChild, HostListener, ElementRef, Injector, runInInjectionContext, effect, ViewEncapsulation, booleanAttribute, createComponent, Injectable } from '@angular/core';
7
7
  import { trigger, transition, group, style, query, animateChild, animate } from '@angular/animations';
8
8
  import { map, of, switchMap, shareReplay, combineLatest, Subject, Subscription } from 'rxjs';
9
9
  import { BreakpointObserver } from '@angular/cdk/layout';
@@ -1302,6 +1302,85 @@ class HeaderWithImpersonationComponent {
1302
1302
  : false);
1303
1303
  this.showImpersonationModal = false;
1304
1304
  this.isImpersonating = computed(() => !!this.parsedToken()?.['impersonator']);
1305
+ // Inactivity timeout for impersonation
1306
+ this.activityEvents = [
1307
+ 'keydown',
1308
+ 'pointerdown',
1309
+ 'wheel',
1310
+ 'scroll',
1311
+ ];
1312
+ this.inactivityTimerId = null;
1313
+ this.inactivityTimeoutMs = 5 * 60 * 1000; // 5 minutes
1314
+ this.debounceTimerId = null;
1315
+ this.debounceDelayMs = 250;
1316
+ this.trackingActive = false;
1317
+ this.injector = inject(Injector);
1318
+ /** Reset the inactivity countdown (no-op if not impersonating) */
1319
+ this.resetInactivityTimer = () => {
1320
+ if (!this.isImpersonating())
1321
+ return;
1322
+ if (this.inactivityTimerId)
1323
+ clearTimeout(this.inactivityTimerId);
1324
+ this.inactivityTimerId = window.setTimeout(() => {
1325
+ this.endImpersonation.emit();
1326
+ this.stopInactivityTracking();
1327
+ }, this.inactivityTimeoutMs);
1328
+ };
1329
+ /** Debounce activity to avoid hammering resets during event storms */
1330
+ this.debouncedResetTimer = () => {
1331
+ if (this.debounceTimerId)
1332
+ clearTimeout(this.debounceTimerId);
1333
+ this.debounceTimerId = window.setTimeout(() => {
1334
+ this.resetInactivityTimer();
1335
+ }, this.debounceDelayMs);
1336
+ };
1337
+ }
1338
+ ngOnInit() {
1339
+ // effect can only be used within an injection context (ex: constructor)
1340
+ runInInjectionContext(this.injector, () => {
1341
+ effect(() => {
1342
+ if (this.isImpersonating()) {
1343
+ if (!this.trackingActive)
1344
+ this.startInactivityTracking();
1345
+ }
1346
+ else {
1347
+ if (this.trackingActive)
1348
+ this.stopInactivityTracking();
1349
+ }
1350
+ });
1351
+ });
1352
+ }
1353
+ ngOnDestroy() {
1354
+ this.stopInactivityTracking();
1355
+ }
1356
+ /** Begin listening and start countdown */
1357
+ startInactivityTracking() {
1358
+ this.activityEvents.forEach((event) => {
1359
+ // 'scroll' on document doesn't bubble; use window + capture to catch nested scrolls
1360
+ const target = event === 'scroll' ? window : document;
1361
+ const options = event === 'scroll'
1362
+ ? { passive: true, capture: true }
1363
+ : { passive: true };
1364
+ target.addEventListener(event, this.debouncedResetTimer, options);
1365
+ });
1366
+ this.trackingActive = true;
1367
+ this.resetInactivityTimer();
1368
+ }
1369
+ /** Remove listeners and clear timers */
1370
+ stopInactivityTracking() {
1371
+ this.activityEvents.forEach((event) => {
1372
+ const target = event === 'scroll' ? window : document;
1373
+ target.removeEventListener(event, this.debouncedResetTimer);
1374
+ });
1375
+ if (this.inactivityTimerId) {
1376
+ clearTimeout(this.inactivityTimerId);
1377
+ this.inactivityTimerId = null;
1378
+ }
1379
+ if (this.debounceTimerId) {
1380
+ clearTimeout(this.debounceTimerId);
1381
+ this.debounceTimerId = null;
1382
+ }
1383
+ this.trackingActive = false;
1305
1384
  }
1306
1385
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: HeaderWithImpersonationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1307
1386
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.1.0", type: HeaderWithImpersonationComponent, isStandalone: true, selector: "lib-header-with-impersonation", inputs: { accessTokenPayload: { classPropertyName: "accessTokenPayload", publicName: "accessTokenPayload", isSignal: true, isRequired: true, transformFunction: null }, oidcBaseUri: { classPropertyName: "oidcBaseUri", publicName: "oidcBaseUri", isSignal: true, isRequired: false, transformFunction: null }, oidcDefaultIdp: { classPropertyName: "oidcDefaultIdp", publicName: "oidcDefaultIdp", isSignal: true, isRequired: false, transformFunction: null }, mainSiteBaseUrl: { classPropertyName: "mainSiteBaseUrl", publicName: "mainSiteBaseUrl", isSignal: true, isRequired: false, transformFunction: null }, personBaseUri: { classPropertyName: "personBaseUri", publicName: "personBaseUri", isSignal: true, isRequired: false, transformFunction: null }, myAccountApiBaseUri: { classPropertyName: "myAccountApiBaseUri", publicName: "myAccountApiBaseUri", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { login: "login", logout: "logout", endImpersonation: "endImpersonation" }, ngImport: i0, template: "<lib-impersonation-banner\n [accessTokenPayload]=\"accessTokenPayload()\"\n (endImpersonation)=\"endImpersonation.emit()\"\n [personBaseUri]=\"personBaseUri()\"\n [myAccountApiBaseUri]=\"myAccountApiBaseUri()\"\n></lib-impersonation-banner>\n<lib-hbll-header\n [name]=\"name()\"\n [showImpersonateButton]=\"showImpersonateButton()\"\n (openImpersonationModal)=\"showImpersonationModal = true\"\n (login)=\"login.emit()\"\n (logout)=\"logout.emit()\"\n [mainsitebaseurl]=\"mainSiteBaseUrl()\"\n/>\n<lib-impersonate-modal\n [showModal]=\"showImpersonationModal\"\n [oidcBaseUri]=\"oidcBaseUri()\"\n [oidcDefaultIdp]=\"oidcDefaultIdp()\"\n [accessTokenPayload]=\"accessTokenPayload()\"\n (dismiss)=\"showImpersonationModal = false\"\n (init)=\"showImpersonationModal = true\"\n></lib-impersonate-modal>\n", styles: [""], dependencies: [{ kind: "component", type: HbllHeaderComponent, selector: "lib-hbll-header", inputs: ["name", "mainsitebaseurl", "showImpersonateButton"], outputs: ["openImpersonationModal", "login", "logout"] }, { kind: "component", type: ImpersonationBannerComponent, selector: "lib-impersonation-banner", inputs: ["accessTokenPayload", "personBaseUri", "myAccountApiBaseUri"], outputs: ["endImpersonation"] }, { kind: "component", type: ImpersonateModalComponent, selector: "lib-impersonate-modal", inputs: ["showModal", "oidcBaseUri", "oidcDefaultIdp", "accessTokenPayload"], outputs: ["dismiss", "init"] }] }); }