From 1c2e5bdf89f7ae4e7efcfba360062087e4f0f1d1 Mon Sep 17 00:00:00 2001 From: nsemets Date: Mon, 20 Oct 2025 11:51:03 +0300 Subject: [PATCH 01/25] fix(share): updated share social button --- .../share-and-download.component.html | 98 ++++------------- .../share-and-download.component.scss | 22 ---- .../share-and-download.component.spec.ts | 101 +----------------- .../share-and-download.component.ts | 67 +++--------- .../overview-toolbar.component.html | 25 +---- .../overview-toolbar.component.scss | 14 --- .../overview-toolbar.component.ts | 80 +------------- .../features/project/overview/models/index.ts | 1 - .../overview/project-overview.component.html | 18 ++-- .../registry-overview.component.html | 2 +- src/app/shared/components/index.ts | 1 + .../socials-share-button.component.html | 19 ++++ .../socials-share-button.component.scss | 13 +++ .../socials-share-button.component.spec.ts | 30 ++++++ .../socials-share-button.component.ts | 44 ++++++++ src/app/shared/constants/index.ts | 1 + .../constants/social-platforms.const.ts | 34 ++++++ src/app/shared/models/index.ts | 3 +- src/app/shared/models/social-share.model.ts | 15 --- src/app/shared/models/socials/index.ts | 5 + .../models/{ => socials}/social-icon.model.ts | 0 .../socials/social-platform-config.model.ts | 7 ++ .../socials/social-share-content.model.ts | 5 + .../socials/social-share-links.model.ts | 8 ++ .../socials-share-action-item.model.ts | 0 .../shared/services/social-share.service.ts | 76 +++++++------ src/testing/mocks/social-share-links.mock.ts | 8 +- 27 files changed, 276 insertions(+), 421 deletions(-) create mode 100644 src/app/shared/components/socials-share-button/socials-share-button.component.html create mode 100644 src/app/shared/components/socials-share-button/socials-share-button.component.scss create mode 100644 src/app/shared/components/socials-share-button/socials-share-button.component.spec.ts create mode 100644 src/app/shared/components/socials-share-button/socials-share-button.component.ts create mode 100644 src/app/shared/constants/social-platforms.const.ts delete mode 100644 src/app/shared/models/social-share.model.ts create mode 100644 src/app/shared/models/socials/index.ts rename src/app/shared/models/{ => socials}/social-icon.model.ts (100%) create mode 100644 src/app/shared/models/socials/social-platform-config.model.ts create mode 100644 src/app/shared/models/socials/social-share-content.model.ts create mode 100644 src/app/shared/models/socials/social-share-links.model.ts rename src/app/{features/project/overview/models => shared/models/socials}/socials-share-action-item.model.ts (100%) diff --git a/src/app/features/preprints/components/preprint-details/share-and-download/share-and-download.component.html b/src/app/features/preprints/components/preprint-details/share-and-download/share-and-download.component.html index 0c5b0319b..b29783b2b 100644 --- a/src/app/features/preprints/components/preprint-details/share-and-download/share-and-download.component.html +++ b/src/app/features/preprints/components/preprint-details/share-and-download/share-and-download.component.html @@ -1,77 +1,23 @@ - -
-
- @if (preprint() && preprintProvider()) { - - {{ - 'preprints.details.share.downloadPreprint' | translate: { documentType: preprintProvider()?.preprintWord } - }} - - } +
+ - @if (metrics()) { -
- {{ 'preprints.details.share.views' | translate }}: {{ metrics()!.views }} - | - {{ 'preprints.details.share.downloads' | translate }}: {{ metrics()!.downloads }} -
- } - - @if (isPreprintLoading()) { - - - } -
- - -
- + @if (preprint() && preprintProvider()) { + + } +
diff --git a/src/app/features/preprints/components/preprint-details/share-and-download/share-and-download.component.scss b/src/app/features/preprints/components/preprint-details/share-and-download/share-and-download.component.scss index e4223887b..e69de29bb 100644 --- a/src/app/features/preprints/components/preprint-details/share-and-download/share-and-download.component.scss +++ b/src/app/features/preprints/components/preprint-details/share-and-download/share-and-download.component.scss @@ -1,22 +0,0 @@ -@use "styles/mixins" as mix; -@use "styles/variables" as var; - -.social-link { - background-color: var(--pr-blue-1); - border-radius: mix.rem(8px); - color: var(--white); - padding: mix.rem(7px); - width: mix.rem(36px); - height: mix.rem(36px); - - &:hover { - background-color: var(--pr-blue-3); - text-decoration: none; - } -} - -.card { - @media (max-width: var.$breakpoint-sm) { - --p-card-body-padding: 0.75rem; - } -} diff --git a/src/app/features/preprints/components/preprint-details/share-and-download/share-and-download.component.spec.ts b/src/app/features/preprints/components/preprint-details/share-and-download/share-and-download.component.spec.ts index 04311ebc6..0e11fe9c3 100644 --- a/src/app/features/preprints/components/preprint-details/share-and-download/share-and-download.component.spec.ts +++ b/src/app/features/preprints/components/preprint-details/share-and-download/share-and-download.component.spec.ts @@ -6,16 +6,13 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { PreprintProviderDetails } from '@osf/features/preprints/models'; import { PreprintSelectors } from '@osf/features/preprints/store/preprint'; -import { IconComponent } from '@shared/components'; -import { SocialShareLinks } from '@shared/models'; -import { SocialShareService } from '@shared/services'; -import { DataciteService } from '@shared/services/datacite/datacite.service'; +import { SocialsShareButtonComponent } from '@osf/shared/components'; +import { DataciteService } from '@osf/shared/services/datacite/datacite.service'; import { ShareAndDownloadComponent } from './share-and-download.component'; import { PREPRINT_MOCK } from '@testing/mocks/preprint.mock'; import { PREPRINT_PROVIDER_DETAILS_MOCK } from '@testing/mocks/preprint-provider-details'; -import { SOCIAL_SHARE_LINKS_MOCK } from '@testing/mocks/social-share-links.mock'; import { TranslationServiceMock } from '@testing/mocks/translation.service.mock'; import { OSFTestingModule } from '@testing/osf.testing.module'; import { provideMockStore } from '@testing/providers/store-provider.mock'; @@ -24,39 +21,26 @@ describe('ShareAndDownloadComponent', () => { let component: ShareAndDownloadComponent; let fixture: ComponentFixture; let dataciteService: jest.Mocked; - let socialShareService: jest.Mocked; const mockPreprint = PREPRINT_MOCK; const mockProvider: PreprintProviderDetails = PREPRINT_PROVIDER_DETAILS_MOCK; - const mockShareLinks: SocialShareLinks = SOCIAL_SHARE_LINKS_MOCK; beforeEach(async () => { dataciteService = { logIdentifiableDownload: jest.fn().mockReturnValue(of(void 0)), } as any; - socialShareService = { - createDownloadUrl: jest.fn().mockReturnValue('https://example.com/download/preprint-1'), - createPreprintUrl: jest.fn().mockReturnValue('https://example.com/preprint/preprint-1'), - generateAllSharingLinks: jest.fn().mockReturnValue(mockShareLinks), - } as any; - await TestBed.configureTestingModule({ - imports: [ShareAndDownloadComponent, OSFTestingModule, MockComponent(IconComponent)], + imports: [ShareAndDownloadComponent, OSFTestingModule, MockComponent(SocialsShareButtonComponent)], providers: [ TranslationServiceMock, MockProvider(DataciteService, dataciteService), - MockProvider(SocialShareService, socialShareService), provideMockStore({ signals: [ { selector: PreprintSelectors.getPreprint, value: mockPreprint, }, - { - selector: PreprintSelectors.isPreprintLoading, - value: false, - }, ], }), ], @@ -77,85 +61,6 @@ describe('ShareAndDownloadComponent', () => { expect(preprint).toBe(mockPreprint); }); - it('should return preprint loading state from store', () => { - const loading = component.isPreprintLoading(); - expect(loading).toBe(false); - }); - - it('should compute metrics from preprint', () => { - const metrics = component.metrics(); - expect(metrics).toBe(mockPreprint.metrics); - }); - - it('should return null metrics when no preprint', () => { - jest.spyOn(component, 'preprint').mockReturnValue(null); - const metrics = component.metrics(); - expect(metrics).toBeNull(); - }); - - it('should compute download link from preprint', () => { - const downloadLink = component.downloadLink(); - expect(downloadLink).toBe('https://example.com/download/preprint-1'); - expect(socialShareService.createDownloadUrl).toHaveBeenCalledWith('preprint-1'); - }); - - it('should return default download link when no preprint', () => { - jest.spyOn(component, 'preprint').mockReturnValue(null); - const downloadLink = component.downloadLink(); - expect(downloadLink).toBe('#'); - }); - - it('should return null shareable content when no preprint or provider', () => { - jest.spyOn(component, 'preprint').mockReturnValue(null); - const shareableContent = (component as any).shareableContent(); - expect(shareableContent).toBeNull(); - }); - - it('should compute share links from shareable content', () => { - const shareLinks = component.shareLinks(); - expect(shareLinks).toBe(mockShareLinks); - expect(socialShareService.generateAllSharingLinks).toHaveBeenCalled(); - }); - - it('should return null share links when no shareable content', () => { - jest.spyOn(component as any, 'shareableContent').mockReturnValue(null); - const shareLinks = component.shareLinks(); - expect(shareLinks).toBeNull(); - }); - - it('should compute email share link', () => { - const emailLink = component.emailShareLink(); - expect(emailLink).toBe(mockShareLinks.email); - }); - - it('should compute twitter share link', () => { - const twitterLink = component.twitterShareLink(); - expect(twitterLink).toBe(mockShareLinks.twitter); - }); - - it('should compute facebook share link', () => { - const facebookLink = component.facebookShareLink(); - expect(facebookLink).toBe(mockShareLinks.facebook); - }); - - it('should compute linkedIn share link', () => { - const linkedInLink = component.linkedInShareLink(); - expect(linkedInLink).toBe(mockShareLinks.linkedIn); - }); - - it('should return empty string for share links when no share links', () => { - jest.spyOn(component, 'shareLinks').mockReturnValue(null); - expect(component.emailShareLink()).toBe(''); - expect(component.twitterShareLink()).toBe(''); - expect(component.facebookShareLink()).toBe(''); - expect(component.linkedInShareLink()).toBe(''); - }); - - it('should call dataciteService.logIdentifiableDownload when logDownload is called', () => { - component.logDownload(); - expect(dataciteService.logIdentifiableDownload).toHaveBeenCalledWith(component.preprint$); - }); - it('should handle preprint provider input', () => { const provider = component.preprintProvider(); expect(provider).toBe(mockProvider); diff --git a/src/app/features/preprints/components/preprint-details/share-and-download/share-and-download.component.ts b/src/app/features/preprints/components/preprint-details/share-and-download/share-and-download.component.ts index cf4c4739d..5d4cd6374 100644 --- a/src/app/features/preprints/components/preprint-details/share-and-download/share-and-download.component.ts +++ b/src/app/features/preprints/components/preprint-details/share-and-download/share-and-download.component.ts @@ -2,23 +2,22 @@ import { select } from '@ngxs/store'; import { TranslatePipe } from '@ngx-translate/core'; -import { ButtonDirective } from 'primeng/button'; -import { Card } from 'primeng/card'; -import { Skeleton } from 'primeng/skeleton'; +import { Button } from 'primeng/button'; +import { Tooltip } from 'primeng/tooltip'; -import { ChangeDetectionStrategy, Component, computed, DestroyRef, inject, input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, DestroyRef, inject, input } from '@angular/core'; import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop'; import { PreprintProviderDetails } from '@osf/features/preprints/models'; import { PreprintSelectors } from '@osf/features/preprints/store/preprint'; -import { IconComponent } from '@shared/components'; -import { ShareableContent } from '@shared/models'; -import { SocialShareService } from '@shared/services'; -import { DataciteService } from '@shared/services/datacite/datacite.service'; +import { SocialsShareButtonComponent } from '@osf/shared/components'; +import { ResourceType } from '@osf/shared/enums'; +import { SocialShareService } from '@osf/shared/services'; +import { DataciteService } from '@osf/shared/services/datacite/datacite.service'; @Component({ selector: 'osf-preprint-share-and-download', - imports: [Card, IconComponent, Skeleton, ButtonDirective, TranslatePipe], + imports: [Button, SocialsShareButtonComponent, TranslatePipe, Tooltip], templateUrl: './share-and-download.component.html', styleUrl: './share-and-download.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, @@ -32,54 +31,18 @@ export class ShareAndDownloadComponent { preprint = select(PreprintSelectors.getPreprint); preprint$ = toObservable(this.preprint); - isPreprintLoading = select(PreprintSelectors.isPreprintLoading); + resourceType = ResourceType.Preprint; - metrics = computed(() => { + download() { const preprint = this.preprint(); - if (!preprint) return null; + if (!preprint) { + return; + } - return preprint.metrics!; - }); + const downloadLink = this.socialShareService.createDownloadUrl(preprint.id); + window.open(downloadLink)?.focus(); - downloadLink = computed(() => { - const preprint = this.preprint(); - - if (!preprint) return '#'; - - return this.socialShareService.createDownloadUrl(preprint.id); - }); - - logDownload() { this.dataciteService.logIdentifiableDownload(this.preprint$).pipe(takeUntilDestroyed(this.destroyRef)).subscribe(); } - - private shareableContent = computed((): ShareableContent | null => { - const preprint = this.preprint(); - const preprintProvider = this.preprintProvider(); - - if (!preprint || !preprintProvider) return null; - - return { - id: preprint.id, - title: preprint.title, - description: preprint.description, - url: this.socialShareService.createPreprintUrl(preprint.id, preprintProvider.id), - }; - }); - - shareLinks = computed(() => { - const content = this.shareableContent(); - - if (!content) return null; - - return this.socialShareService.generateAllSharingLinks(content); - }); - - emailShareLink = computed(() => this.shareLinks()?.email || ''); - twitterShareLink = computed(() => this.shareLinks()?.twitter || ''); - facebookShareLink = computed(() => this.shareLinks()?.facebook || ''); - linkedInShareLink = computed(() => this.shareLinks()?.linkedIn || ''); - mastodonShareLink = computed(() => this.shareLinks()?.mastodon || ''); - blueskyShareLink = computed(() => this.shareLinks()?.bluesky || ''); } diff --git a/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.html b/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.html index f04cb53aa..221bcb1c1 100644 --- a/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.html +++ b/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.html @@ -97,26 +97,11 @@ } @if (resource.isPublic && !hasViewOnly()) { - - {{ socialsActionItems().length }} - - - - - - {{ item.label | translate }} - - - - + } diff --git a/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.scss b/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.scss index c935e55e2..a5c7df749 100644 --- a/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.scss +++ b/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.scss @@ -6,17 +6,3 @@ color: var(--pr-blue-1); font-size: 1.5rem; } - -.social-link { - background-color: var(--pr-blue-1); - border-radius: 0.25rem; - color: var(--white); - padding: 0.125rem; - width: 1.125rem; - height: 1.125rem; - - &:hover { - background-color: var(--pr-blue-3); - text-decoration: none; - } -} diff --git a/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.ts b/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.ts index a9090aa7e..876e1c046 100644 --- a/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.ts +++ b/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.ts @@ -17,11 +17,11 @@ import { ActivatedRoute, Router, RouterLink } from '@angular/router'; import { UserSelectors } from '@core/store/user'; import { ClearDuplicatedProject, ProjectOverviewSelectors } from '@osf/features/project/overview/store'; -import { IconComponent } from '@osf/shared/components'; +import { SocialsShareButtonComponent } from '@osf/shared/components'; import { ResourceType } from '@osf/shared/enums'; -import { ShareableContent, ToolbarResource } from '@osf/shared/models'; +import { ToolbarResource } from '@osf/shared/models'; import { FileSizePipe } from '@osf/shared/pipes'; -import { CustomDialogService, SocialShareService, ToastService } from '@osf/shared/services'; +import { CustomDialogService, ToastService } from '@osf/shared/services'; import { AddResourceToBookmarks, BookmarksSelectors, @@ -31,7 +31,6 @@ import { } from '@osf/shared/stores'; import { hasViewOnlyParam } from '@shared/helpers'; -import { SocialsShareActionItem } from '../../models'; import { DuplicateDialogComponent } from '../duplicate-dialog/duplicate-dialog.component'; import { ForkDialogComponent } from '../fork-dialog/fork-dialog.component'; import { TogglePublicityDialogComponent } from '../toggle-publicity-dialog/toggle-publicity-dialog.component'; @@ -48,7 +47,7 @@ import { TogglePublicityDialogComponent } from '../toggle-publicity-dialog/toggl NgClass, RouterLink, FileSizePipe, - IconComponent, + SocialsShareButtonComponent, ], templateUrl: './overview-toolbar.component.html', styleUrl: './overview-toolbar.component.scss', @@ -58,7 +57,6 @@ export class OverviewToolbarComponent { private store = inject(Store); private customDialogService = inject(CustomDialogService); private toastService = inject(ToastService); - private socialShareService = inject(SocialShareService); private readonly router = inject(Router); private readonly route = inject(ActivatedRoute); @@ -69,7 +67,7 @@ export class OverviewToolbarComponent { isCollectionsRoute = input(false); canEdit = input.required(); - currentResource = input.required(); + currentResource = input.required(); projectDescription = input(''); showViewOnlyLinks = input(true); @@ -79,10 +77,6 @@ export class OverviewToolbarComponent { bookmarkedProjects = select(MyResourcesSelectors.getBookmarks); duplicatedProject = select(ProjectOverviewSelectors.getDuplicatedProject); isAuthenticated = select(UserSelectors.isAuthenticated); - socialsActionItems = computed(() => { - const shareableContent = this.createShareableContent(); - return shareableContent ? this.buildSocialActionItems(shareableContent) : []; - }); hasViewOnly = computed(() => hasViewOnlyParam(this.router)); @@ -229,68 +223,4 @@ export class OverviewToolbarComponent { complete: () => this.actions.clearDuplicatedProject(), }); } - - private createShareableContent(): ShareableContent | null { - const resource = this.currentResource(); - const description = this.projectDescription(); - - if (!resource?.isPublic) { - return null; - } - - return { - id: resource.id, - title: resource.title, - description, - url: this.buildResourceUrl(resource), - }; - } - - private buildResourceUrl(resource: ToolbarResource): string { - switch (resource.resourceType) { - case ResourceType.Project: - return this.socialShareService.createProjectUrl(resource.id); - case ResourceType.Registration: - return this.socialShareService.createRegistrationUrl(resource.id); - default: - return `${window.location.origin}/${resource.id}`; - } - } - - private buildSocialActionItems(shareableContent: ShareableContent): SocialsShareActionItem[] { - const shareLinks = this.socialShareService.generateAllSharingLinks(shareableContent); - - return [ - { - label: 'project.overview.actions.socials.email', - icon: 'fas fa-envelope', - url: shareLinks.email, - }, - { - label: 'project.overview.actions.socials.x', - icon: 'fab fa-x-twitter', - url: shareLinks.twitter, - }, - { - label: 'project.overview.actions.socials.linkedIn', - icon: 'fab fa-linkedin', - url: shareLinks.linkedIn, - }, - { - label: 'project.overview.actions.socials.facebook', - icon: 'fab fa-facebook-f', - url: shareLinks.facebook, - }, - { - label: 'project.overview.actions.socials.mastodon', - icon: 'fab fa-mastodon', - url: shareLinks.mastodon, - }, - { - label: 'project.overview.actions.socials.bluesky', - icon: 'fab fa-bluesky', - url: shareLinks.bluesky, - }, - ]; - } } diff --git a/src/app/features/project/overview/models/index.ts b/src/app/features/project/overview/models/index.ts index 7a6789398..ffa3996aa 100644 --- a/src/app/features/project/overview/models/index.ts +++ b/src/app/features/project/overview/models/index.ts @@ -2,4 +2,3 @@ export * from './addon-tree-item.model'; export * from './formatted-citation-item.model'; export * from './privacy-status.model'; export * from './project-overview.models'; -export * from './socials-share-action-item.model'; diff --git a/src/app/features/project/overview/project-overview.component.html b/src/app/features/project/overview/project-overview.component.html index a31557c6b..f9d5a7850 100644 --- a/src/app/features/project/overview/project-overview.component.html +++ b/src/app/features/project/overview/project-overview.component.html @@ -12,14 +12,16 @@ /> -
- -
+ @if (currentProject()) { +
+ +
+ }
@if (isCollectionsRoute()) { diff --git a/src/app/features/registry/pages/registry-overview/registry-overview.component.html b/src/app/features/registry/pages/registry-overview/registry-overview.component.html index 8280fd1cb..387f1f2a6 100644 --- a/src/app/features/registry/pages/registry-overview/registry-overview.component.html +++ b/src/app/features/registry/pages/registry-overview/registry-overview.component.html @@ -15,7 +15,7 @@ @if (showToolbar()) {
diff --git a/src/app/shared/components/index.ts b/src/app/shared/components/index.ts index 9857ff339..a67e812cc 100644 --- a/src/app/shared/components/index.ts +++ b/src/app/shared/components/index.ts @@ -46,6 +46,7 @@ export { SearchHelpTutorialComponent } from './search-help-tutorial/search-help- export { SearchInputComponent } from './search-input/search-input.component'; export { SearchResultsContainerComponent } from './search-results-container/search-results-container.component'; export { SelectComponent } from './select/select.component'; +export { SocialsShareButtonComponent } from './socials-share-button/socials-share-button.component'; export { StatisticCardComponent } from './statistic-card/statistic-card.component'; export { StepperComponent } from './stepper/stepper.component'; export { SubHeaderComponent } from './sub-header/sub-header.component'; diff --git a/src/app/shared/components/socials-share-button/socials-share-button.component.html b/src/app/shared/components/socials-share-button/socials-share-button.component.html new file mode 100644 index 000000000..fa9b194de --- /dev/null +++ b/src/app/shared/components/socials-share-button/socials-share-button.component.html @@ -0,0 +1,19 @@ + + + + + + + {{ item.label | translate }} + + + + diff --git a/src/app/shared/components/socials-share-button/socials-share-button.component.scss b/src/app/shared/components/socials-share-button/socials-share-button.component.scss new file mode 100644 index 000000000..5a4fc67ca --- /dev/null +++ b/src/app/shared/components/socials-share-button/socials-share-button.component.scss @@ -0,0 +1,13 @@ +.social-link { + background-color: var(--pr-blue-1); + border-radius: 0.25rem; + color: var(--white); + padding: 0.125rem; + width: 1.125rem; + height: 1.125rem; + + &:hover { + background-color: var(--pr-blue-3); + text-decoration: none; + } +} diff --git a/src/app/shared/components/socials-share-button/socials-share-button.component.spec.ts b/src/app/shared/components/socials-share-button/socials-share-button.component.spec.ts new file mode 100644 index 000000000..c30239e09 --- /dev/null +++ b/src/app/shared/components/socials-share-button/socials-share-button.component.spec.ts @@ -0,0 +1,30 @@ +import { TranslatePipe } from '@ngx-translate/core'; +import { MockComponent, MockPipe, MockProvider } from 'ng-mocks'; + +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SocialShareService } from '@osf/shared/services'; + +import { IconComponent } from '../icon/icon.component'; + +import { SocialsShareButtonComponent } from './socials-share-button.component'; + +describe.skip('SocialsShareButtonComponent', () => { + let component: SocialsShareButtonComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [SocialsShareButtonComponent, MockComponent(IconComponent), MockPipe(TranslatePipe)], + providers: [MockProvider(SocialShareService)], + }).compileComponents(); + + fixture = TestBed.createComponent(SocialsShareButtonComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/components/socials-share-button/socials-share-button.component.ts b/src/app/shared/components/socials-share-button/socials-share-button.component.ts new file mode 100644 index 000000000..5906560d6 --- /dev/null +++ b/src/app/shared/components/socials-share-button/socials-share-button.component.ts @@ -0,0 +1,44 @@ +import { TranslatePipe } from '@ngx-translate/core'; + +import { Button } from 'primeng/button'; +import { Menu } from 'primeng/menu'; +import { Tooltip } from 'primeng/tooltip'; + +import { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core'; + +import { ResourceType } from '@osf/shared/enums'; +import { SocialShareContentModel } from '@osf/shared/models'; +import { SocialShareService } from '@osf/shared/services'; + +import { IconComponent } from '../icon/icon.component'; + +@Component({ + selector: 'osf-socials-share-button', + imports: [Menu, Button, Tooltip, IconComponent, TranslatePipe], + templateUrl: './socials-share-button.component.html', + styleUrl: './socials-share-button.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class SocialsShareButtonComponent { + private readonly socialShareService = inject(SocialShareService); + + resourceId = input.required(); + resourceTitle = input.required(); + resourceType = input.required(); + resourceProvider = input(''); + + socialsActionItems = computed(() => { + const resourceUrl = + this.resourceType() === ResourceType.Preprint + ? this.socialShareService.createPreprintUrl(this.resourceId(), this.resourceProvider()) + : this.socialShareService.createGuidUrl(this.resourceId()); + + const shareableContent: SocialShareContentModel = { + id: this.resourceId(), + title: this.resourceTitle(), + url: resourceUrl, + }; + + return this.socialShareService.generateSocialActionItems(shareableContent); + }); +} diff --git a/src/app/shared/constants/index.ts b/src/app/shared/constants/index.ts index ea425dd27..0fb592994 100644 --- a/src/app/shared/constants/index.ts +++ b/src/app/shared/constants/index.ts @@ -21,5 +21,6 @@ export * from './search-tab-options.const'; export * from './search-tutorial-steps.const'; export * from './social-links.const'; export * from './social-links.const'; +export * from './social-platforms.const'; export * from './social-share.config'; export * from './sort-options.const'; diff --git a/src/app/shared/constants/social-platforms.const.ts b/src/app/shared/constants/social-platforms.const.ts new file mode 100644 index 000000000..a1689fad3 --- /dev/null +++ b/src/app/shared/constants/social-platforms.const.ts @@ -0,0 +1,34 @@ +import { SocialPlatformConfig } from '../models'; + +export const SOCIAL_PLATFORMS: SocialPlatformConfig[] = [ + { + label: 'project.overview.actions.socials.email', + icon: 'fas fa-envelope', + urlKey: 'email', + }, + { + label: 'project.overview.actions.socials.x', + icon: 'fab fa-x-twitter', + urlKey: 'twitter', + }, + { + label: 'project.overview.actions.socials.linkedIn', + icon: 'fab fa-linkedin', + urlKey: 'linkedIn', + }, + { + label: 'project.overview.actions.socials.facebook', + icon: 'fab fa-facebook-f', + urlKey: 'facebook', + }, + { + label: 'project.overview.actions.socials.mastodon', + icon: 'fab fa-mastodon', + urlKey: 'mastodon', + }, + { + label: 'project.overview.actions.socials.bluesky', + icon: 'fab fa-bluesky', + urlKey: 'bluesky', + }, +]; diff --git a/src/app/shared/models/index.ts b/src/app/shared/models/index.ts index 84ff8ba7e..789b99b07 100644 --- a/src/app/shared/models/index.ts +++ b/src/app/shared/models/index.ts @@ -45,8 +45,7 @@ export * from './search'; export * from './search-filters.model'; export * from './select-option.model'; export * from './severity.type'; -export * from './social-icon.model'; -export * from './social-share.model'; +export * from './socials'; export * from './status-info.model'; export * from './step-option.model'; export * from './store'; diff --git a/src/app/shared/models/social-share.model.ts b/src/app/shared/models/social-share.model.ts deleted file mode 100644 index b44798fef..000000000 --- a/src/app/shared/models/social-share.model.ts +++ /dev/null @@ -1,15 +0,0 @@ -export interface ShareableContent { - id: string; - title: string; - description?: string; - url: string; -} - -export interface SocialShareLinks { - email: string; - twitter: string; - facebook: string; - linkedIn: string; - mastodon: string; - bluesky: string; -} diff --git a/src/app/shared/models/socials/index.ts b/src/app/shared/models/socials/index.ts new file mode 100644 index 000000000..39949c806 --- /dev/null +++ b/src/app/shared/models/socials/index.ts @@ -0,0 +1,5 @@ +export * from './social-icon.model'; +export * from './social-platform-config.model'; +export * from './social-share-content.model'; +export * from './social-share-links.model'; +export * from './socials-share-action-item.model'; diff --git a/src/app/shared/models/social-icon.model.ts b/src/app/shared/models/socials/social-icon.model.ts similarity index 100% rename from src/app/shared/models/social-icon.model.ts rename to src/app/shared/models/socials/social-icon.model.ts diff --git a/src/app/shared/models/socials/social-platform-config.model.ts b/src/app/shared/models/socials/social-platform-config.model.ts new file mode 100644 index 000000000..9e3428cc8 --- /dev/null +++ b/src/app/shared/models/socials/social-platform-config.model.ts @@ -0,0 +1,7 @@ +import { SocialShareLinksModel } from './social-share-links.model'; + +export interface SocialPlatformConfig { + label: string; + icon: string; + urlKey: keyof SocialShareLinksModel; +} diff --git a/src/app/shared/models/socials/social-share-content.model.ts b/src/app/shared/models/socials/social-share-content.model.ts new file mode 100644 index 000000000..fc5b94031 --- /dev/null +++ b/src/app/shared/models/socials/social-share-content.model.ts @@ -0,0 +1,5 @@ +export interface SocialShareContentModel { + id: string; + title: string; + url: string; +} diff --git a/src/app/shared/models/socials/social-share-links.model.ts b/src/app/shared/models/socials/social-share-links.model.ts new file mode 100644 index 000000000..13dde8268 --- /dev/null +++ b/src/app/shared/models/socials/social-share-links.model.ts @@ -0,0 +1,8 @@ +export interface SocialShareLinksModel { + email: string; + twitter: string; + facebook: string; + linkedIn: string; + mastodon: string; + bluesky: string; +} diff --git a/src/app/features/project/overview/models/socials-share-action-item.model.ts b/src/app/shared/models/socials/socials-share-action-item.model.ts similarity index 100% rename from src/app/features/project/overview/models/socials-share-action-item.model.ts rename to src/app/shared/models/socials/socials-share-action-item.model.ts diff --git a/src/app/shared/services/social-share.service.ts b/src/app/shared/services/social-share.service.ts index 20977c169..262684cb0 100644 --- a/src/app/shared/services/social-share.service.ts +++ b/src/app/shared/services/social-share.service.ts @@ -2,8 +2,8 @@ import { inject, Injectable } from '@angular/core'; import { ENVIRONMENT } from '@core/provider/environment.provider'; -import { SOCIAL_SHARE_URLS } from '../constants'; -import { ShareableContent, SocialShareLinks } from '../models'; +import { SOCIAL_PLATFORMS, SOCIAL_SHARE_URLS } from '../constants'; +import { SocialShareContentModel, SocialShareLinksModel, SocialsShareActionItem } from '../models'; @Injectable({ providedIn: 'root', @@ -15,69 +15,75 @@ export class SocialShareService { return this.environment.webUrl; } - generateEmailLink(content: ShareableContent): string { + generateAllSharingLinks(content: SocialShareContentModel): SocialShareLinksModel { + return { + email: this.generateEmailLink(content), + twitter: this.generateTwitterLink(content), + facebook: this.generateFacebookLink(content), + linkedIn: this.generateLinkedInLink(content), + mastodon: this.generateMastodonLink(content), + bluesky: this.generateBlueskyLink(content), + }; + } + + createPreprintUrl(preprintId: string, providerId: string): string { + return `${this.webUrl}/preprints/${providerId}/${preprintId}`; + } + + createGuidUrl(guid: string): string { + return `${this.webUrl}/${guid}`; + } + + createDownloadUrl(resourceId: string): string { + return `${this.webUrl}/download/${resourceId}`; + } + + generateSocialActionItems(content: SocialShareContentModel): SocialsShareActionItem[] { + const shareLinks = this.generateAllSharingLinks(content); + + return SOCIAL_PLATFORMS.map((platform) => ({ + label: platform.label, + icon: platform.icon, + url: shareLinks[platform.urlKey], + })); + } + + private generateEmailLink(content: SocialShareContentModel): string { const subject = encodeURIComponent(content.title); const body = encodeURIComponent(content.url); return `${SOCIAL_SHARE_URLS.email}?subject=${subject}&body=${body}`; } - generateTwitterLink(content: ShareableContent): string { + private generateTwitterLink(content: SocialShareContentModel): string { const url = encodeURIComponent(content.url); const text = encodeURIComponent(content.title); return `${SOCIAL_SHARE_URLS.twitter.preview_url}?url=${url}&text=${text}&via=${SOCIAL_SHARE_URLS.twitter.viaHandle}`; } - generateFacebookLink(content: ShareableContent): string { + private generateFacebookLink(content: SocialShareContentModel): string { const href = encodeURIComponent(content.url); return `${SOCIAL_SHARE_URLS.facebook}?u=${href}`; } - generateLinkedInLink(content: ShareableContent): string { + private generateLinkedInLink(content: SocialShareContentModel): string { const url = encodeURIComponent(content.url); return `${SOCIAL_SHARE_URLS.linkedIn}?url=${url}`; } - generateMastodonLink(content: ShareableContent): string { + private generateMastodonLink(content: SocialShareContentModel): string { const url = encodeURIComponent(content.url); const text = encodeURIComponent(content.title); return `${SOCIAL_SHARE_URLS.mastodon}?url=${url}&text=${text}`; } - generateBlueskyLink(content: ShareableContent): string { + private generateBlueskyLink(content: SocialShareContentModel): string { const text = encodeURIComponent(`${content.title} ${content.url}`); return `${SOCIAL_SHARE_URLS.bluesky}?text=${text}`; } - - generateAllSharingLinks(content: ShareableContent): SocialShareLinks { - return { - email: this.generateEmailLink(content), - twitter: this.generateTwitterLink(content), - facebook: this.generateFacebookLink(content), - linkedIn: this.generateLinkedInLink(content), - mastodon: this.generateMastodonLink(content), - bluesky: this.generateBlueskyLink(content), - }; - } - - createPreprintUrl(preprintId: string, providerId: string): string { - return `${this.webUrl}/preprints/${providerId}/${preprintId}`; - } - - createProjectUrl(projectId: string): string { - return `${this.webUrl}/${projectId}`; - } - - createRegistrationUrl(registrationId: string, providerId = 'osf'): string { - return `${this.webUrl}/registries/${providerId}/${registrationId}`; - } - - createDownloadUrl(resourceId: string): string { - return `${this.webUrl}/download/${resourceId}`; - } } diff --git a/src/testing/mocks/social-share-links.mock.ts b/src/testing/mocks/social-share-links.mock.ts index 86bafcc8d..c7f9beb6f 100644 --- a/src/testing/mocks/social-share-links.mock.ts +++ b/src/testing/mocks/social-share-links.mock.ts @@ -1,10 +1,14 @@ -import { SocialShareLinks } from '@shared/models'; +import { SocialShareLinksModel } from '@shared/models'; -export const SOCIAL_SHARE_LINKS_MOCK: SocialShareLinks = { +export const SOCIAL_SHARE_LINKS_MOCK: SocialShareLinksModel = { email: 'mailto:?subject=Sample%20Preprint%20Title&body=Check%20out%20this%20preprint%3A%20https%3A//example.com/preprint/preprint-1', twitter: 'https://twitter.com/intent/tweet?text=Sample%20Preprint%20Title&url=https%3A//example.com/preprint/preprint-1', facebook: 'https://www.facebook.com/sharer/sharer.php?u=https%3A//example.com/preprint/preprint-1', linkedIn: 'https://www.linkedin.com/sharing/share-offsite/?url=https%3A//example.com/preprint/preprint-1', + mastodon: + 'https://mastodonshare.com/?url=https%3A%2F%2Fstaging4.osf.io%2Fpreprints%2Flawarchive%2Fm5sy9_v1&text=test', + bluesky: + 'https://bsky.app/intent/compose?text=test%20https%3A%2F%2Fstaging4.osf.io%2Fpreprints%2Flawarchive%2Fm5sy9_v1', }; From a2c7f0e3b0075355bbdbbeaf15cf4260a72ff482 Mon Sep 17 00:00:00 2001 From: nsemets Date: Mon, 20 Oct 2025 11:52:28 +0300 Subject: [PATCH 02/25] fix(preprint-details): updated views and downloads --- .../features/preprints/components/index.ts | 1 + .../general-information.component.html | 25 ++++++++++--------- .../preprint-file-section.component.html | 9 ++----- .../preprint-metrics-info.component.html | 9 +++++++ .../preprint-metrics-info.component.scss | 0 .../preprint-metrics-info.component.spec.ts | 22 ++++++++++++++++ .../preprint-metrics-info.component.ts | 18 +++++++++++++ .../preprint-details.component.html | 18 ++++++++----- .../preprint-details.component.ts | 2 ++ src/assets/i18n/en.json | 1 + 10 files changed, 80 insertions(+), 25 deletions(-) create mode 100644 src/app/features/preprints/components/preprint-details/preprint-metrics-info/preprint-metrics-info.component.html create mode 100644 src/app/features/preprints/components/preprint-details/preprint-metrics-info/preprint-metrics-info.component.scss create mode 100644 src/app/features/preprints/components/preprint-details/preprint-metrics-info/preprint-metrics-info.component.spec.ts create mode 100644 src/app/features/preprints/components/preprint-details/preprint-metrics-info/preprint-metrics-info.component.ts diff --git a/src/app/features/preprints/components/index.ts b/src/app/features/preprints/components/index.ts index c22b6221a..9d0394975 100644 --- a/src/app/features/preprints/components/index.ts +++ b/src/app/features/preprints/components/index.ts @@ -5,6 +5,7 @@ export { GeneralInformationComponent } from './preprint-details/general-informat export { ModerationStatusBannerComponent } from './preprint-details/moderation-status-banner/moderation-status-banner.component'; export { PreprintFileSectionComponent } from './preprint-details/preprint-file-section/preprint-file-section.component'; export { PreprintMakeDecisionComponent } from './preprint-details/preprint-make-decision/preprint-make-decision.component'; +export { PreprintMetricsInfoComponent } from './preprint-details/preprint-metrics-info/preprint-metrics-info.component'; export { PreprintTombstoneComponent } from './preprint-details/preprint-tombstone/preprint-tombstone.component'; export { PreprintWarningBannerComponent } from './preprint-details/preprint-warning-banner/preprint-warning-banner.component'; export { ShareAndDownloadComponent } from './preprint-details/share-and-download/share-and-download.component'; diff --git a/src/app/features/preprints/components/preprint-details/general-information/general-information.component.html b/src/app/features/preprints/components/preprint-details/general-information/general-information.component.html index b0df7c2b1..9bb687216 100644 --- a/src/app/features/preprints/components/preprint-details/general-information/general-information.component.html +++ b/src/app/features/preprints/components/preprint-details/general-information/general-information.component.html @@ -1,7 +1,20 @@ @if (preprint()) { @let preprintValue = preprint()!; +
+
+

{{ 'preprints.preprintStepper.review.sections.metadata.authors' | translate }}

+ +
+ + + @if (areContributorsLoading()) { + + } +
+
+

{{ 'preprints.preprintStepper.common.labels.abstract' | translate }}

@@ -21,18 +34,6 @@

{{ 'preprints.details.supplementalMaterials' | translate }}

} -
-

{{ 'preprints.preprintStepper.review.sections.metadata.authors' | translate }}

- -
- - - @if (areContributorsLoading()) { - - } -
-
- @if (preprintProvider()?.assertionsEnabled) {

{{ 'preprints.preprintStepper.review.sections.authorAssertions.conflictOfInterest' | translate }}

diff --git a/src/app/features/preprints/components/preprint-details/preprint-file-section/preprint-file-section.component.html b/src/app/features/preprints/components/preprint-details/preprint-file-section/preprint-file-section.component.html index 5ee1164b7..5cbf8f18f 100644 --- a/src/app/features/preprints/components/preprint-details/preprint-file-section/preprint-file-section.component.html +++ b/src/app/features/preprints/components/preprint-details/preprint-file-section/preprint-file-section.component.html @@ -1,15 +1,10 @@ -
+
@if (safeLink()) { } @if (isIframeLoading || isFileLoading()) { diff --git a/src/app/features/preprints/components/preprint-details/preprint-make-decision/preprint-make-decision.component.spec.ts b/src/app/features/preprints/components/preprint-details/preprint-make-decision/preprint-make-decision.component.spec.ts index 1e76617aa..35b404aed 100644 --- a/src/app/features/preprints/components/preprint-details/preprint-make-decision/preprint-make-decision.component.spec.ts +++ b/src/app/features/preprints/components/preprint-details/preprint-make-decision/preprint-make-decision.component.spec.ts @@ -47,10 +47,6 @@ describe('PreprintMakeDecisionComponent', () => { fixture.componentRef.setInput('isPendingWithdrawal', false); }); - it('should create', () => { - expect(component).toBeTruthy(); - }); - it('should return preprint from store', () => { const preprint = component.preprint(); expect(preprint).toBe(mockPreprint); diff --git a/src/app/features/preprints/components/preprint-details/preprint-metrics-info/preprint-metrics-info.component.html b/src/app/features/preprints/components/preprint-details/preprint-metrics-info/preprint-metrics-info.component.html index 9f44a3f6b..6ee03c0f4 100644 --- a/src/app/features/preprints/components/preprint-details/preprint-metrics-info/preprint-metrics-info.component.html +++ b/src/app/features/preprints/components/preprint-details/preprint-metrics-info/preprint-metrics-info.component.html @@ -1,5 +1,11 @@ - @if (metrics()) { + @if (isLoading()) { +
+ + + +
+ } @else if (metrics()) {
{{ 'preprints.details.share.views' | translate }}: {{ metrics()!.views }} | diff --git a/src/app/features/preprints/components/preprint-details/preprint-metrics-info/preprint-metrics-info.component.ts b/src/app/features/preprints/components/preprint-details/preprint-metrics-info/preprint-metrics-info.component.ts index c9e8219b9..ce1ee2b84 100644 --- a/src/app/features/preprints/components/preprint-details/preprint-metrics-info/preprint-metrics-info.component.ts +++ b/src/app/features/preprints/components/preprint-details/preprint-metrics-info/preprint-metrics-info.component.ts @@ -1,6 +1,7 @@ import { TranslatePipe } from '@ngx-translate/core'; import { Card } from 'primeng/card'; +import { Skeleton } from 'primeng/skeleton'; import { ChangeDetectionStrategy, Component, input } from '@angular/core'; @@ -8,11 +9,12 @@ import { PreprintMetrics } from '@osf/features/preprints/models'; @Component({ selector: 'osf-preprint-metrics-info', - imports: [Card, TranslatePipe], + imports: [Card, TranslatePipe, Skeleton], templateUrl: './preprint-metrics-info.component.html', styleUrl: './preprint-metrics-info.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, }) export class PreprintMetricsInfoComponent { - metrics = input(); + metrics = input(); + isLoading = input(false); } diff --git a/src/app/features/preprints/components/preprint-details/preprint-tombstone/preprint-tombstone.component.html b/src/app/features/preprints/components/preprint-details/preprint-tombstone/preprint-tombstone.component.html index 633711405..a9423933d 100644 --- a/src/app/features/preprints/components/preprint-details/preprint-tombstone/preprint-tombstone.component.html +++ b/src/app/features/preprints/components/preprint-details/preprint-tombstone/preprint-tombstone.component.html @@ -33,18 +33,9 @@

{{ 'preprints.preprintStepper.common.labels.abstract' | translate }}

/>
-

{{ 'preprints.preprintStepper.review.sections.metadata.license' | translate }}

+

{{ 'common.labels.license' | translate }}

- - - -

{{ license()!.name }}

-
- -

{{ license()!.text | interpolate: licenseOptionsRecord() }}

-
-
-
+
@@ -52,6 +43,7 @@

{{ 'preprints.preprintStepper.review.sections.metadata.license' | translate

{{ 'common.labels.dateCreated' | translate }}

{{ preprintValue.dateCreated | date: 'MMM d, y, hh:mm a' }}

+

{{ 'common.labels.dateUpdated' | translate }}

{{ preprintValue.dateModified | date: 'MMM d, y, hh:mm a' }}

diff --git a/src/app/features/preprints/components/preprint-details/preprint-tombstone/preprint-tombstone.component.ts b/src/app/features/preprints/components/preprint-details/preprint-tombstone/preprint-tombstone.component.ts index aaa407bc7..34f587648 100644 --- a/src/app/features/preprints/components/preprint-details/preprint-tombstone/preprint-tombstone.component.ts +++ b/src/app/features/preprints/components/preprint-details/preprint-tombstone/preprint-tombstone.component.ts @@ -2,7 +2,6 @@ import { createDispatchMap, select } from '@ngxs/store'; import { TranslatePipe } from '@ngx-translate/core'; -import { Accordion, AccordionContent, AccordionHeader, AccordionPanel } from 'primeng/accordion'; import { Card } from 'primeng/card'; import { Skeleton } from 'primeng/skeleton'; import { Tag } from 'primeng/tag'; @@ -14,9 +13,8 @@ import { Router } from '@angular/router'; import { ApplicabilityStatus, PreregLinkInfo } from '@osf/features/preprints/enums'; import { PreprintProviderDetails } from '@osf/features/preprints/models'; import { FetchPreprintById, PreprintSelectors } from '@osf/features/preprints/store/preprint'; -import { ContributorsListComponent, TruncatedTextComponent } from '@osf/shared/components'; +import { ContributorsListComponent, LicenseDisplayComponent, TruncatedTextComponent } from '@osf/shared/components'; import { ResourceType } from '@osf/shared/enums'; -import { InterpolatePipe } from '@osf/shared/pipes'; import { ContributorsSelectors, GetBibliographicContributors, @@ -35,14 +33,10 @@ import { PreprintDoiSectionComponent } from '../preprint-doi-section/preprint-do Skeleton, TranslatePipe, TruncatedTextComponent, - Accordion, - AccordionContent, Tag, - AccordionPanel, - AccordionHeader, - InterpolatePipe, DatePipe, ContributorsListComponent, + LicenseDisplayComponent, ], templateUrl: './preprint-tombstone.component.html', styleUrl: './preprint-tombstone.component.scss', diff --git a/src/app/features/preprints/components/preprint-details/status-banner/status-banner.component.spec.ts b/src/app/features/preprints/components/preprint-details/status-banner/status-banner.component.spec.ts index ba1771f3c..449cd76e7 100644 --- a/src/app/features/preprints/components/preprint-details/status-banner/status-banner.component.spec.ts +++ b/src/app/features/preprints/components/preprint-details/status-banner/status-banner.component.spec.ts @@ -4,7 +4,7 @@ import { TitleCasePipe } from '@angular/common'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ReviewAction } from '@osf/features/moderation/models'; -import { Preprint, PreprintProviderDetails } from '@osf/features/preprints/models'; +import { PreprintModel, PreprintProviderDetails } from '@osf/features/preprints/models'; import { PreprintSelectors } from '@osf/features/preprints/store/preprint'; import { IconComponent } from '@shared/components'; @@ -20,7 +20,7 @@ describe('StatusBannerComponent', () => { let component: StatusBannerComponent; let fixture: ComponentFixture; - const mockPreprint: Preprint = PREPRINT_MOCK; + const mockPreprint: PreprintModel = PREPRINT_MOCK; const mockProvider: PreprintProviderDetails = PREPRINT_PROVIDER_DETAILS_MOCK; const mockReviewAction: ReviewAction = REVIEW_ACTION_MOCK; const mockRequestAction = REVIEW_ACTION_MOCK; diff --git a/src/app/features/preprints/components/preprint-details/status-banner/status-banner.component.ts b/src/app/features/preprints/components/preprint-details/status-banner/status-banner.component.ts index b691f77d8..693a527ab 100644 --- a/src/app/features/preprints/components/preprint-details/status-banner/status-banner.component.ts +++ b/src/app/features/preprints/components/preprint-details/status-banner/status-banner.component.ts @@ -22,7 +22,7 @@ import { import { ProviderReviewsWorkflow, ReviewsState } from '@osf/features/preprints/enums'; import { PreprintProviderDetails, PreprintRequestAction } from '@osf/features/preprints/models'; import { PreprintSelectors } from '@osf/features/preprints/store/preprint'; -import { IconComponent } from '@shared/components'; +import { IconComponent } from '@osf/shared/components'; @Component({ selector: 'osf-preprint-status-banner', diff --git a/src/app/features/preprints/components/preprint-details/withdraw-dialog/withdraw-dialog.component.spec.ts b/src/app/features/preprints/components/preprint-details/withdraw-dialog/withdraw-dialog.component.spec.ts index 57d0e0094..bfd79e403 100644 --- a/src/app/features/preprints/components/preprint-details/withdraw-dialog/withdraw-dialog.component.spec.ts +++ b/src/app/features/preprints/components/preprint-details/withdraw-dialog/withdraw-dialog.component.spec.ts @@ -7,7 +7,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { formInputLimits } from '@osf/features/preprints/constants'; import { ProviderReviewsWorkflow, ReviewsState } from '@osf/features/preprints/enums'; -import { Preprint, PreprintProviderDetails } from '@osf/features/preprints/models'; +import { PreprintModel, PreprintProviderDetails } from '@osf/features/preprints/models'; import { WithdrawDialogComponent } from './withdraw-dialog.component'; @@ -23,7 +23,7 @@ describe('WithdrawDialogComponent', () => { let dialogConfigMock: any; const mockProvider: PreprintProviderDetails = PREPRINT_PROVIDER_DETAILS_MOCK; - const mockPreprint: Preprint = PREPRINT_MOCK; + const mockPreprint: PreprintModel = PREPRINT_MOCK; beforeEach(async () => { dialogRefMock = { diff --git a/src/app/features/preprints/components/preprint-details/withdraw-dialog/withdraw-dialog.component.ts b/src/app/features/preprints/components/preprint-details/withdraw-dialog/withdraw-dialog.component.ts index 14a50aee9..53a683cb4 100644 --- a/src/app/features/preprints/components/preprint-details/withdraw-dialog/withdraw-dialog.component.ts +++ b/src/app/features/preprints/components/preprint-details/withdraw-dialog/withdraw-dialog.component.ts @@ -15,7 +15,7 @@ import { ENVIRONMENT } from '@core/provider/environment.provider'; import { formInputLimits } from '@osf/features/preprints/constants'; import { ProviderReviewsWorkflow, ReviewsState } from '@osf/features/preprints/enums'; import { getPreprintDocumentType } from '@osf/features/preprints/helpers'; -import { Preprint, PreprintProviderDetails, PreprintWordGrammar } from '@osf/features/preprints/models'; +import { PreprintModel, PreprintProviderDetails, PreprintWordGrammar } from '@osf/features/preprints/models'; import { WithdrawPreprint } from '@osf/features/preprints/store/preprint'; import { CustomValidators } from '@osf/shared/helpers'; import { INPUT_VALIDATION_MESSAGES } from '@shared/constants'; @@ -37,7 +37,7 @@ export class WithdrawDialogComponent implements OnInit { readonly supportEmail = this.environment.supportEmail; private provider!: PreprintProviderDetails; - private preprint!: Preprint; + private preprint!: PreprintModel; private actions = createDispatchMap({ withdrawPreprint: WithdrawPreprint, diff --git a/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.spec.ts b/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.spec.ts index 22fdaa745..64abf505c 100644 --- a/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.spec.ts +++ b/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.spec.ts @@ -4,7 +4,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ArrayInputComponent } from '@osf/features/preprints/components/stepper/author-assertion-step/array-input/array-input.component'; import { ApplicabilityStatus } from '@osf/features/preprints/enums'; -import { Preprint } from '@osf/features/preprints/models'; +import { PreprintModel } from '@osf/features/preprints/models'; import { PreprintStepperSelectors } from '@osf/features/preprints/store/preprint-stepper'; import { FormSelectComponent } from '@shared/components'; import { CustomConfirmationService, ToastService } from '@shared/services'; @@ -24,7 +24,7 @@ describe('AuthorAssertionsStepComponent', () => { let toastServiceMock: ReturnType; let customConfirmationServiceMock: ReturnType; - const mockPreprint: Preprint = PREPRINT_MOCK; + const mockPreprint: PreprintModel = PREPRINT_MOCK; beforeEach(async () => { toastServiceMock = ToastServiceMockBuilder.create().build(); diff --git a/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.ts b/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.ts index 71346a1f3..5a16cbd9e 100644 --- a/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.ts +++ b/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.ts @@ -26,7 +26,7 @@ import { import { ArrayInputComponent } from '@osf/features/preprints/components/stepper/author-assertion-step/array-input/array-input.component'; import { formInputLimits, preregLinksOptions } from '@osf/features/preprints/constants'; import { ApplicabilityStatus, PreregLinkInfo } from '@osf/features/preprints/enums'; -import { Preprint } from '@osf/features/preprints/models'; +import { PreprintModel } from '@osf/features/preprints/models'; import { PreprintStepperSelectors, UpdatePreprint } from '@osf/features/preprints/store/preprint-stepper'; import { CustomValidators, findChangedFields } from '@osf/shared/helpers'; import { FormSelectComponent } from '@shared/components'; @@ -228,7 +228,7 @@ export class AuthorAssertionsStepComponent { backButtonClicked() { const formValue = this.authorAssertionsForm.getRawValue(); - const changedFields = findChangedFields(formValue, this.createdPreprint()!); + const changedFields = findChangedFields(formValue, this.createdPreprint()!); if (!Object.keys(changedFields).length) { this.backClicked.emit(); diff --git a/src/app/features/preprints/components/stepper/file-step/file-step.component.spec.ts b/src/app/features/preprints/components/stepper/file-step/file-step.component.spec.ts index b7f4de625..0163a1125 100644 --- a/src/app/features/preprints/components/stepper/file-step/file-step.component.spec.ts +++ b/src/app/features/preprints/components/stepper/file-step/file-step.component.spec.ts @@ -3,7 +3,7 @@ import { MockComponents, MockProvider } from 'ng-mocks'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { PreprintFileSource } from '@osf/features/preprints/enums'; -import { Preprint, PreprintProviderDetails } from '@osf/features/preprints/models'; +import { PreprintModel, PreprintProviderDetails } from '@osf/features/preprints/models'; import { PreprintStepperSelectors } from '@osf/features/preprints/store/preprint-stepper'; import { FilesTreeComponent, IconComponent } from '@shared/components'; import { FileFolderModel } from '@shared/models'; @@ -26,7 +26,7 @@ describe('FileStepComponent', () => { let confirmationServiceMock: ReturnType; const mockProvider: PreprintProviderDetails = PREPRINT_PROVIDER_DETAILS_MOCK; - const mockPreprint: Preprint = PREPRINT_MOCK; + const mockPreprint: PreprintModel = PREPRINT_MOCK; const mockProjectFiles: FileFolderModel[] = [OSF_FILE_MOCK]; const mockPreprintFile: FileFolderModel = OSF_FILE_MOCK; diff --git a/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-affiliated-institutions/preprints-affiliated-institutions.component.ts b/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-affiliated-institutions/preprints-affiliated-institutions.component.ts index 4d57e1de4..06bab1f5b 100644 --- a/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-affiliated-institutions/preprints-affiliated-institutions.component.ts +++ b/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-affiliated-institutions/preprints-affiliated-institutions.component.ts @@ -7,7 +7,7 @@ import { Card } from 'primeng/card'; import { ChangeDetectionStrategy, Component, effect, input, OnInit, signal } from '@angular/core'; import { ReviewsState } from '@osf/features/preprints/enums'; -import { Preprint, PreprintProviderDetails } from '@osf/features/preprints/models'; +import { PreprintModel, PreprintProviderDetails } from '@osf/features/preprints/models'; import { PreprintStepperSelectors, SetInstitutionsChanged } from '@osf/features/preprints/store/preprint-stepper'; import { AffiliatedInstitutionSelectComponent } from '@osf/shared/components'; import { ResourceType } from '@osf/shared/enums'; @@ -28,7 +28,7 @@ import { }) export class PreprintsAffiliatedInstitutionsComponent implements OnInit { provider = input.required(); - preprint = input.required(); + preprint = input.required(); selectedInstitutions = signal([]); diff --git a/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-metadata-step.component.ts b/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-metadata-step.component.ts index 786b4aaa0..b9ce03bba 100644 --- a/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-metadata-step.component.ts +++ b/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-metadata-step.component.ts @@ -13,7 +13,7 @@ import { ChangeDetectionStrategy, Component, inject, input, OnInit, output } fro import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; import { formInputLimits } from '@osf/features/preprints/constants'; -import { MetadataForm, Preprint, PreprintProviderDetails } from '@osf/features/preprints/models'; +import { MetadataForm, PreprintModel, PreprintProviderDetails } from '@osf/features/preprints/models'; import { CreatePreprint, FetchLicenses, @@ -115,7 +115,7 @@ export class PreprintsMetadataStepComponent implements OnInit { const model = this.metadataForm.value; - const changedFields = findChangedFields(model, this.createdPreprint()!); + const changedFields = findChangedFields(model, this.createdPreprint()!); this.actions.updatePreprint(this.createdPreprint()!.id, changedFields).subscribe({ complete: () => { @@ -145,7 +145,7 @@ export class PreprintsMetadataStepComponent implements OnInit { backButtonClicked() { const formValue = this.metadataForm.value; delete formValue.subjects; - const changedFields = findChangedFields(formValue, this.createdPreprint()!); + const changedFields = findChangedFields(formValue, this.createdPreprint()!); if (!Object.keys(changedFields).length) { this.backClicked.emit(); diff --git a/src/app/features/preprints/components/stepper/review-step/review-step.component.html b/src/app/features/preprints/components/stepper/review-step/review-step.component.html index e0e092c86..2ae83eb00 100644 --- a/src/app/features/preprints/components/stepper/review-step/review-step.component.html +++ b/src/app/features/preprints/components/stepper/review-step/review-step.component.html @@ -76,18 +76,9 @@

{{ 'common.labels.contributors' | translate }}

@if (license()) {
-

{{ 'preprints.preprintStepper.review.sections.metadata.license' | translate }}

- - - - -

{{ license()?.name }}

-
- -

{{ license()!.text | interpolate: licenseOptionsRecord() }}

-
-
-
+

{{ 'common.labels.license' | translate }}

+ +
} diff --git a/src/app/features/preprints/components/stepper/review-step/review-step.component.ts b/src/app/features/preprints/components/stepper/review-step/review-step.component.ts index 4dc31ccf1..3a5d77919 100644 --- a/src/app/features/preprints/components/stepper/review-step/review-step.component.ts +++ b/src/app/features/preprints/components/stepper/review-step/review-step.component.ts @@ -2,7 +2,6 @@ import { createDispatchMap, select } from '@ngxs/store'; import { TranslatePipe } from '@ngx-translate/core'; -import { Accordion, AccordionContent, AccordionHeader, AccordionPanel } from 'primeng/accordion'; import { Button } from 'primeng/button'; import { Card } from 'primeng/card'; import { Tag } from 'primeng/tag'; @@ -26,10 +25,10 @@ import { import { AffiliatedInstitutionsViewComponent, ContributorsListComponent, + LicenseDisplayComponent, TruncatedTextComponent, } from '@shared/components'; import { ResourceType } from '@shared/enums'; -import { InterpolatePipe } from '@shared/pipes'; import { ToastService } from '@shared/services'; import { ContributorsSelectors, @@ -49,13 +48,9 @@ import { FetchSelectedSubjects, SubjectsSelectors } from '@shared/stores/subject Button, TitleCasePipe, TranslatePipe, - Accordion, - AccordionContent, - AccordionHeader, - AccordionPanel, - InterpolatePipe, AffiliatedInstitutionsViewComponent, ContributorsListComponent, + LicenseDisplayComponent, ], templateUrl: './review-step.component.html', styleUrl: './review-step.component.scss', diff --git a/src/app/features/preprints/mappers/preprints.mapper.ts b/src/app/features/preprints/mappers/preprints.mapper.ts index 4c8a6e630..0c7c4dfdf 100644 --- a/src/app/features/preprints/mappers/preprints.mapper.ts +++ b/src/app/features/preprints/mappers/preprints.mapper.ts @@ -1,13 +1,13 @@ -import { LicensesMapper } from '@osf/shared/mappers'; +import { IdentifiersMapper, LicensesMapper } from '@osf/shared/mappers'; import { ApiData, JsonApiResponseWithMeta, ResponseJsonApi } from '@osf/shared/models'; import { StringOrNull } from '@shared/helpers'; import { - Preprint, PreprintAttributesJsonApi, PreprintEmbedsJsonApi, PreprintLinksJsonApi, PreprintMetaJsonApi, + PreprintModel, PreprintRelationshipsJsonApi, PreprintShortInfoWithTotalCount, } from '../models'; @@ -35,7 +35,7 @@ export class PreprintsMapper { static fromPreprintJsonApi( response: ApiData - ): Preprint { + ): PreprintModel { return { id: response.id, dateCreated: response.attributes.date_created, @@ -78,6 +78,7 @@ export class PreprintsMapper { preregLinkInfo: response.attributes.prereg_link_info, preprintDoiLink: response.links.preprint_doi, articleDoiLink: response.links.doi, + embeddedLicense: null, }; } @@ -87,10 +88,10 @@ export class PreprintsMapper { PreprintMetaJsonApi, null > - ): Preprint { + ): PreprintModel { const data = response.data; - const meta = response.meta; const links = response.data.links; + return { id: data.id, dateCreated: data.attributes.date_created, @@ -131,17 +132,8 @@ export class PreprintsMapper { whyNoPrereg: data.attributes.why_no_prereg, preregLinks: data.attributes.prereg_links, preregLinkInfo: data.attributes.prereg_link_info, - metrics: { - downloads: meta.metrics.downloads, - views: meta.metrics.views, - }, - embeddedLicense: LicensesMapper.fromLicenseDataJsonApi(data.embeds.license.data), - identifiers: data.embeds.identifiers?.data.map((identifier) => ({ - id: identifier.id, - type: identifier.type, - value: identifier.attributes.value, - category: identifier.attributes.category, - })), + embeddedLicense: LicensesMapper.fromLicenseDataJsonApi(data.embeds?.license?.data), + identifiers: IdentifiersMapper.fromJsonApi(data.embeds?.identifiers), preprintDoiLink: links.preprint_doi, articleDoiLink: links.doi, }; diff --git a/src/app/features/preprints/models/preprint-json-api.models.ts b/src/app/features/preprints/models/preprint-json-api.models.ts index ca1456f4b..8dcc27bff 100644 --- a/src/app/features/preprints/models/preprint-json-api.models.ts +++ b/src/app/features/preprints/models/preprint-json-api.models.ts @@ -1,6 +1,11 @@ import { UserPermissions } from '@osf/shared/enums'; import { BooleanOrNull, StringOrNull } from '@osf/shared/helpers'; -import { ContributorDataJsonApi, LicenseRecordJsonApi, LicenseResponseJsonApi } from '@osf/shared/models'; +import { + ContributorDataJsonApi, + IdentifiersResponseJsonApi, + LicenseRecordJsonApi, + LicenseResponseJsonApi, +} from '@osf/shared/models'; import { ApplicabilityStatus, PreregLinkInfo, ReviewsState } from '../enums'; @@ -69,16 +74,7 @@ export interface PreprintEmbedsJsonApi { data: ContributorDataJsonApi[]; }; license: LicenseResponseJsonApi; - identifiers: { - data: { - id: string; - type: string; - attributes: { - category: string; - value: string; - }; - }[]; - }; + identifiers: IdentifiersResponseJsonApi; } export interface PreprintMetaJsonApi { diff --git a/src/app/features/preprints/models/preprint.models.ts b/src/app/features/preprints/models/preprint.models.ts index d425d17ff..22ceab8cc 100644 --- a/src/app/features/preprints/models/preprint.models.ts +++ b/src/app/features/preprints/models/preprint.models.ts @@ -4,7 +4,7 @@ import { Identifier, IdName, LicenseModel, LicenseOptions } from '@osf/shared/mo import { ApplicabilityStatus, PreregLinkInfo, ReviewsState } from '../enums'; -export interface Preprint { +export interface PreprintModel { id: string; dateCreated: string; dateModified: string; @@ -40,7 +40,7 @@ export interface Preprint { preregLinks: string[]; preregLinkInfo: PreregLinkInfo | null; metrics?: PreprintMetrics; - embeddedLicense?: LicenseModel; + embeddedLicense: LicenseModel | null; preprintDoiLink?: string; articleDoiLink?: string; identifiers?: Identifier[]; diff --git a/src/app/features/preprints/pages/my-preprints/my-preprints.component.spec.ts b/src/app/features/preprints/pages/my-preprints/my-preprints.component.spec.ts index cd271d446..e1bc36de0 100644 --- a/src/app/features/preprints/pages/my-preprints/my-preprints.component.spec.ts +++ b/src/app/features/preprints/pages/my-preprints/my-preprints.component.spec.ts @@ -7,11 +7,12 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ActivatedRoute, Router } from '@angular/router'; import { PreprintShortInfo } from '@osf/features/preprints/models'; -import { PreprintSelectors } from '@osf/features/preprints/store/preprint'; import { ListInfoShortenerComponent, SearchInputComponent, SubHeaderComponent } from '@shared/components'; import { DEFAULT_TABLE_PARAMS } from '@shared/constants'; import { SortOrder } from '@shared/enums'; +import { MyPreprintsSelectors } from '../../store/my-preprints'; + import { MyPreprintsComponent } from './my-preprints.component'; import { PREPRINT_SHORT_INFO_ARRAY_MOCK } from '@testing/mocks/preprint-short-info.mock'; @@ -61,15 +62,15 @@ describe('MyPreprintsComponent', () => { provideMockStore({ signals: [ { - selector: PreprintSelectors.getMyPreprints, + selector: MyPreprintsSelectors.getMyPreprints, value: mockPreprints, }, { - selector: PreprintSelectors.getMyPreprintsTotalCount, + selector: MyPreprintsSelectors.getMyPreprintsTotalCount, value: 5, }, { - selector: PreprintSelectors.areMyPreprintsLoading, + selector: MyPreprintsSelectors.areMyPreprintsLoading, value: false, }, ], @@ -82,10 +83,6 @@ describe('MyPreprintsComponent', () => { fixture.detectChanges(); }); - it('should create', () => { - expect(component).toBeTruthy(); - }); - it('should initialize with correct default values', () => { expect(component.searchControl.value).toBe(''); expect(component.sortColumn()).toBe(''); diff --git a/src/app/features/preprints/pages/my-preprints/my-preprints.component.ts b/src/app/features/preprints/pages/my-preprints/my-preprints.component.ts index 76178fb3e..1e769f811 100644 --- a/src/app/features/preprints/pages/my-preprints/my-preprints.component.ts +++ b/src/app/features/preprints/pages/my-preprints/my-preprints.component.ts @@ -30,7 +30,7 @@ import { parseQueryFilterParams } from '@osf/shared/helpers'; import { QueryParams, SearchFilters, TableParameters } from '@osf/shared/models'; import { PreprintShortInfo } from '../../models'; -import { FetchMyPreprints, PreprintSelectors } from '../../store/preprint'; +import { FetchMyPreprints, MyPreprintsSelectors } from '../../store/my-preprints'; @Component({ selector: 'osf-my-preprints', @@ -65,9 +65,9 @@ export class MyPreprintsComponent { currentPageSize = signal(DEFAULT_TABLE_PARAMS.rows); tableParams = signal({ ...DEFAULT_TABLE_PARAMS, firstRowIndex: 0 }); - preprints = select(PreprintSelectors.getMyPreprints); - preprintsTotalCount = select(PreprintSelectors.getMyPreprintsTotalCount); - areMyPreprintsLoading = select(PreprintSelectors.areMyPreprintsLoading); + preprints = select(MyPreprintsSelectors.getMyPreprints); + preprintsTotalCount = select(MyPreprintsSelectors.getMyPreprintsTotalCount); + areMyPreprintsLoading = select(MyPreprintsSelectors.areMyPreprintsLoading); skeletonData: PreprintShortInfo[] = Array.from({ length: 10 }, () => ({}) as PreprintShortInfo); constructor() { diff --git a/src/app/features/preprints/pages/preprint-details/preprint-details.component.html b/src/app/features/preprints/pages/preprint-details/preprint-details.component.html index 38aaef76b..f987fa783 100644 --- a/src/app/features/preprints/pages/preprint-details/preprint-details.component.html +++ b/src/app/features/preprints/pages/preprint-details/preprint-details.component.html @@ -41,7 +41,7 @@

{{ preprint()?.title }}

- @if (isPreprintLoading() || isPreprintProviderLoading() || areContributorsLoading()) { + @if (isPreprintLoading() || isPreprintProviderLoading()) { @@ -83,6 +83,7 @@

{{ preprint()?.title }}

@if (isOsfPreprint()) { } + @if (moderationStatusBannerVisible()) { {{ preprint()?.title }} [latestWithdrawalRequest]="latestWithdrawalRequest()" /> } + @if (statusBannerVisible()) { {{ preprint()?.title }} (preprintVersionSelected)="fetchPreprint($event)" /> - +
diff --git a/src/app/features/preprints/pages/preprint-details/preprint-details.component.spec.ts b/src/app/features/preprints/pages/preprint-details/preprint-details.component.spec.ts index 2a57fd3c3..78bd01e18 100644 --- a/src/app/features/preprints/pages/preprint-details/preprint-details.component.spec.ts +++ b/src/app/features/preprints/pages/preprint-details/preprint-details.component.spec.ts @@ -5,7 +5,6 @@ import { of } from 'rxjs'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ActivatedRoute, Router } from '@angular/router'; -import { UserSelectors } from '@core/store/user'; import { UserPermissions } from '@osf/shared/enums'; import { CustomDialogService, MetaTagsService } from '@osf/shared/services'; import { DataciteService } from '@osf/shared/services/datacite/datacite.service'; @@ -28,7 +27,7 @@ import { PreprintProvidersSelectors } from '../../store/preprint-providers'; import { PreprintDetailsComponent } from './preprint-details.component'; -import { MOCK_CONTRIBUTOR, MOCK_USER } from '@testing/mocks'; +import { MOCK_CONTRIBUTOR } from '@testing/mocks'; import { PREPRINT_MOCK } from '@testing/mocks/preprint.mock'; import { PREPRINT_PROVIDER_DETAILS_MOCK } from '@testing/mocks/preprint-provider-details'; import { PREPRINT_REQUEST_MOCK } from '@testing/mocks/preprint-request.mock'; @@ -55,7 +54,6 @@ describe('PreprintDetailsComponent', () => { const mockWithdrawalRequests = [PREPRINT_REQUEST_MOCK]; const mockRequestActions = [REVIEW_ACTION_MOCK]; const mockContributors = [MOCK_CONTRIBUTOR]; - const mockCurrentUser = MOCK_USER; beforeEach(async () => { routerMock = RouterMockBuilder.create() @@ -104,10 +102,6 @@ describe('PreprintDetailsComponent', () => { MockProvider(CustomDialogService, mockCustomDialogService), provideMockStore({ signals: [ - { - selector: UserSelectors.getCurrentUser, - value: mockCurrentUser, - }, { selector: PreprintProvidersSelectors.getPreprintProviderDetails('osf'), value: mockProvider, @@ -156,6 +150,14 @@ describe('PreprintDetailsComponent', () => { selector: PreprintSelectors.arePreprintRequestActionsLoading, value: false, }, + { + selector: PreprintSelectors.hasAdminAccess, + value: false, + }, + { + selector: PreprintSelectors.hasWriteAccess, + value: false, + }, ], }), ], @@ -212,11 +214,6 @@ describe('PreprintDetailsComponent', () => { expect(contributors).toBe(mockContributors); }); - it('should return current user from store', () => { - const currentUser = component.currentUser(); - expect(currentUser).toBe(mockCurrentUser); - }); - it('should return contributors loading state from store', () => { const loading = component.areContributorsLoading(); expect(loading).toBe(false); @@ -338,21 +335,10 @@ describe('PreprintDetailsComponent', () => { }); it('should handle hasReadWriteAccess correctly', () => { - const hasAccess = component['hasReadWriteAccess'](); + const hasAccess = component['hasWriteAccess'](); expect(typeof hasAccess).toBe('boolean'); }); - it('should handle preprint with write permissions', () => { - const preprintWithWrite = { - ...mockPreprint, - currentUserPermissions: [UserPermissions.Write], - }; - jest.spyOn(component, 'preprint').mockReturnValue(preprintWithWrite); - - const hasAccess = component['hasReadWriteAccess'](); - expect(hasAccess).toBe(true); - }); - it('should handle preprint without write permissions', () => { const preprintWithoutWrite = { ...mockPreprint, @@ -360,7 +346,7 @@ describe('PreprintDetailsComponent', () => { }; jest.spyOn(component, 'preprint').mockReturnValue(preprintWithoutWrite); - const hasAccess = component['hasReadWriteAccess'](); + const hasAccess = component['hasWriteAccess'](); expect(hasAccess).toBe(false); }); }); diff --git a/src/app/features/preprints/pages/preprint-details/preprint-details.component.ts b/src/app/features/preprints/pages/preprint-details/preprint-details.component.ts index ff01240b4..bbb0626fc 100644 --- a/src/app/features/preprints/pages/preprint-details/preprint-details.component.ts +++ b/src/app/features/preprints/pages/preprint-details/preprint-details.component.ts @@ -27,19 +27,6 @@ import { ENVIRONMENT } from '@core/provider/environment.provider'; import { HelpScoutService } from '@core/services/help-scout.service'; import { ClearCurrentProvider } from '@core/store/provider'; import { UserSelectors } from '@core/store/user'; -import { - AdditionalInfoComponent, - GeneralInformationComponent, - ModerationStatusBannerComponent, - PreprintFileSectionComponent, - PreprintMakeDecisionComponent, - PreprintMetricsInfoComponent, - PreprintTombstoneComponent, - ShareAndDownloadComponent, - StatusBannerComponent, - WithdrawDialogComponent, -} from '@osf/features/preprints/components'; -import { PreprintRequestMachineState, ProviderReviewsWorkflow, ReviewsState } from '@osf/features/preprints/enums'; import { FetchPreprintById, FetchPreprintRequestActions, @@ -51,13 +38,26 @@ import { import { GetPreprintProviderById, PreprintProvidersSelectors } from '@osf/features/preprints/store/preprint-providers'; import { CreateNewVersion, PreprintStepperSelectors } from '@osf/features/preprints/store/preprint-stepper'; import { pathJoin } from '@osf/shared/helpers'; -import { ReviewPermissions, UserPermissions } from '@shared/enums'; +import { ReviewPermissions } from '@shared/enums'; import { CustomDialogService, MetaTagsService, ToastService } from '@shared/services'; import { AnalyticsService } from '@shared/services/analytics.service'; import { DataciteService } from '@shared/services/datacite/datacite.service'; import { ContributorsSelectors } from '@shared/stores/contributors'; +import { + AdditionalInfoComponent, + GeneralInformationComponent, + ModerationStatusBannerComponent, + PreprintFileSectionComponent, + PreprintMakeDecisionComponent, + PreprintMetricsInfoComponent, + PreprintTombstoneComponent, + ShareAndDownloadComponent, + StatusBannerComponent, + WithdrawDialogComponent, +} from '../../components'; import { PreprintWarningBannerComponent } from '../../components/preprint-details/preprint-warning-banner/preprint-warning-banner.component'; +import { PreprintRequestMachineState, ProviderReviewsWorkflow, ReviewsState } from '../../enums'; @Component({ selector: 'osf-preprint-details', @@ -96,6 +96,8 @@ export class PreprintDetailsComponent implements OnInit, OnDestroy { private readonly metaTags = inject(MetaTagsService); private readonly datePipe = inject(DatePipe); private readonly dataciteService = inject(DataciteService); + private readonly analyticsService = inject(AnalyticsService); + private readonly environment = inject(ENVIRONMENT); private preprintId = toSignal(this.route.params.pipe(map((params) => params['id'])) ?? of(undefined)); @@ -110,6 +112,7 @@ export class PreprintDetailsComponent implements OnInit, OnDestroy { fetchPreprintRequestActions: FetchPreprintRequestActions, clearCurrentProvider: ClearCurrentProvider, }); + providerId = toSignal(this.route.params.pipe(map((params) => params['providerId'])) ?? of(undefined)); currentUser = select(UserSelectors.getCurrentUser); preprintProvider = select(PreprintProvidersSelectors.getPreprintProviderDetails(this.providerId())); @@ -125,8 +128,13 @@ export class PreprintDetailsComponent implements OnInit, OnDestroy { areWithdrawalRequestsLoading = select(PreprintSelectors.arePreprintRequestsLoading); requestActions = select(PreprintSelectors.getPreprintRequestActions); areRequestActionsLoading = select(PreprintSelectors.arePreprintRequestActionsLoading); + hasAdminAccess = select(PreprintSelectors.hasAdminAccess); + hasWriteAccess = select(PreprintSelectors.hasWriteAccess); + metrics = select(PreprintSelectors.getPreprintMetrics); + areMetricsLoading = select(PreprintSelectors.arePreprintMetricsLoading); isPresentModeratorQueryParam = toSignal(this.route.queryParams.pipe(map((params) => params['mode'] === 'moderator'))); + moderationMode = computed(() => { const provider = this.preprintProvider(); return this.isPresentModeratorQueryParam() && provider?.permissions.includes(ReviewPermissions.ViewSubmissions); @@ -156,37 +164,18 @@ export class PreprintDetailsComponent implements OnInit, OnDestroy { return actions[0]; }); - private readonly analyticsService = inject(AnalyticsService); - constructor() { this.helpScoutService.setResourceType('preprint'); + effect(() => { const currentPreprint = this.preprint(); + if (currentPreprint && currentPreprint.isPublic) { this.analyticsService.sendCountedUsage(currentPreprint.id, 'preprint.detail').subscribe(); } }); } - private currentUserIsAdmin = computed(() => { - return this.preprint()?.currentUserPermissions.includes(UserPermissions.Admin) || false; - }); - - private currentUserIsContributor = computed(() => { - const contributors = this.contributors(); - const currentUser = this.currentUser(); - - if (this.currentUserIsAdmin()) { - return true; - } else if (contributors.length) { - const authorIds = contributors.map((author) => author.id); - return currentUser?.id - ? authorIds.some((id) => id.endsWith(currentUser!.id)) && this.hasReadWriteAccess() - : false; - } - return false; - }); - private preprintWithdrawableState = computed(() => { const preprint = this.preprint(); if (!preprint) return false; @@ -197,7 +186,7 @@ export class PreprintDetailsComponent implements OnInit, OnDestroy { const preprint = this.preprint(); if (!preprint) return false; - return this.currentUserIsAdmin() && preprint.datePublished && preprint.isLatestVersion; + return this.hasAdminAccess() && preprint.datePublished && preprint.isLatestVersion; }); editButtonVisible = computed(() => { @@ -208,7 +197,7 @@ export class PreprintDetailsComponent implements OnInit, OnDestroy { const providerIsPremod = provider.reviewsWorkflow === ProviderReviewsWorkflow.PreModeration; const preprintIsRejected = preprint.reviewsState === ReviewsState.Rejected; - if (!this.currentUserIsContributor()) { + if (!this.hasWriteAccess()) { return false; } @@ -224,7 +213,7 @@ export class PreprintDetailsComponent implements OnInit, OnDestroy { return true; } - if (preprintIsRejected && this.currentUserIsAdmin()) { + if (preprintIsRejected && this.hasAdminAccess()) { return true; } } @@ -235,7 +224,7 @@ export class PreprintDetailsComponent implements OnInit, OnDestroy { const providerIsPremod = this.preprintProvider()?.reviewsWorkflow === ProviderReviewsWorkflow.PreModeration; const preprintIsRejected = this.preprint()?.reviewsState === ReviewsState.Rejected; - return providerIsPremod && preprintIsRejected && this.currentUserIsAdmin() + return providerIsPremod && preprintIsRejected && this.hasAdminAccess() ? 'common.buttons.editAndResubmit' : 'common.buttons.edit'; }); @@ -255,8 +244,9 @@ export class PreprintDetailsComponent implements OnInit, OnDestroy { withdrawalButtonVisible = computed(() => { if (this.areWithdrawalRequestsLoading() || this.areRequestActionsLoading()) return false; + return ( - this.currentUserIsAdmin() && + this.hasAdminAccess() && this.preprintWithdrawableState() && !this.isWithdrawalRejected() && !this.isPendingWithdrawal() @@ -293,18 +283,16 @@ export class PreprintDetailsComponent implements OnInit, OnDestroy { return ( provider.reviewsWorkflow && preprint.isPublic && - this.currentUserIsContributor() && + this.hasWriteAccess() && preprint.reviewsState !== ReviewsState.Initial && !preprint.isPreprintOrphan ); }); ngOnInit() { - this.actions.getPreprintProviderById(this.providerId()).subscribe({ - next: () => { - this.fetchPreprint(this.preprintId()); - }, - }); + this.actions.getPreprintProviderById(this.providerId()); + this.fetchPreprint(this.preprintId()); + this.dataciteService.logIdentifiableView(this.preprint$).pipe(takeUntilDestroyed(this.destroyRef)).subscribe(); } @@ -362,12 +350,15 @@ export class PreprintDetailsComponent implements OnInit, OnDestroy { this.actions.fetchPreprintById(preprintId).subscribe({ next: () => { this.checkAndSetVersionToTheUrl(); + if (this.preprint()!.currentUserPermissions.length > 0 || this.moderationMode()) { this.actions.fetchPreprintReviewActions(); - if (this.preprintWithdrawableState() && (this.currentUserIsAdmin() || this.moderationMode())) { + + if (this.preprintWithdrawableState() && (this.hasAdminAccess() || this.moderationMode())) { this.actions.fetchPreprintRequests().subscribe({ next: () => { const latestWithdrawalRequest = this.latestWithdrawalRequest(); + if (latestWithdrawalRequest) { this.actions.fetchPreprintRequestActions(latestWithdrawalRequest.id); } @@ -404,10 +395,6 @@ export class PreprintDetailsComponent implements OnInit, OnDestroy { ); } - private hasReadWriteAccess(): boolean { - return this.preprint()?.currentUserPermissions.includes(UserPermissions.Write) || false; - } - private checkAndSetVersionToTheUrl() { const currentUrl = this.router.url; const newPreprintId = this.preprint()!.id; diff --git a/src/app/features/preprints/services/preprint-files.service.ts b/src/app/features/preprints/services/preprint-files.service.ts index 0191a7919..ef640e202 100644 --- a/src/app/features/preprints/services/preprint-files.service.ts +++ b/src/app/features/preprints/services/preprint-files.service.ts @@ -6,10 +6,10 @@ import { inject, Injectable } from '@angular/core'; import { ENVIRONMENT } from '@core/provider/environment.provider'; import { PreprintsMapper } from '@osf/features/preprints/mappers'; import { - Preprint, PreprintAttributesJsonApi, PreprintFilesLinks, PreprintLinksJsonApi, + PreprintModel, PreprintRelationshipsJsonApi, } from '@osf/features/preprints/models'; import { ApiData, FileFolderModel, FileFolderResponseJsonApi, FileFoldersResponseJsonApi } from '@osf/shared/models'; @@ -28,7 +28,7 @@ export class PreprintFilesService { return `${this.environment.apiDomainUrl}/v2`; } - updateFileRelationship(preprintId: string, fileId: string): Observable { + updateFileRelationship(preprintId: string, fileId: string): Observable { return this.jsonApiService .patch>( `${this.apiUrl}/preprints/${preprintId}/`, diff --git a/src/app/features/preprints/services/preprints-projects.service.ts b/src/app/features/preprints/services/preprints-projects.service.ts index da369b71e..e3cd53fad 100644 --- a/src/app/features/preprints/services/preprints-projects.service.ts +++ b/src/app/features/preprints/services/preprints-projects.service.ts @@ -14,7 +14,12 @@ import { import { JsonApiService } from '@osf/shared/services'; import { PreprintsMapper } from '../mappers'; -import { Preprint, PreprintAttributesJsonApi, PreprintLinksJsonApi, PreprintRelationshipsJsonApi } from '../models'; +import { + PreprintAttributesJsonApi, + PreprintLinksJsonApi, + PreprintModel, + PreprintRelationshipsJsonApi, +} from '../models'; @Injectable({ providedIn: 'root', @@ -62,7 +67,7 @@ export class PreprintsProjectsService { }); } - updatePreprintProjectRelationship(preprintId: string, projectId: string): Observable { + updatePreprintProjectRelationship(preprintId: string, projectId: string): Observable { return this.jsonApiService .patch>( `${this.apiUrl}/preprints/${preprintId}/`, diff --git a/src/app/features/preprints/services/preprints.service.ts b/src/app/features/preprints/services/preprints.service.ts index bbb327aed..ff55460ba 100644 --- a/src/app/features/preprints/services/preprints.service.ts +++ b/src/app/features/preprints/services/preprints.service.ts @@ -14,11 +14,11 @@ import { JsonApiService } from '@osf/shared/services'; import { preprintSortFieldMap } from '../constants'; import { PreprintRequestMapper, PreprintsMapper } from '../mappers'; import { - Preprint, PreprintAttributesJsonApi, PreprintEmbedsJsonApi, PreprintLinksJsonApi, PreprintMetaJsonApi, + PreprintModel, PreprintRelationshipsJsonApi, PreprintRequest, PreprintRequestActionsJsonApiResponse, @@ -79,11 +79,7 @@ export class PreprintsService { } getByIdWithEmbeds(id: string) { - const params = { - 'metrics[views]': 'total', - 'metrics[downloads]': 'total', - 'embed[]': ['license', 'identifiers'], - }; + const params = { 'embed[]': ['license', 'identifiers'] }; return this.jsonApiService .get< JsonApiResponseWithMeta< @@ -95,11 +91,26 @@ export class PreprintsService { .pipe(map((response) => PreprintsMapper.fromPreprintWithEmbedsJsonApi(response))); } + getPreprintMetrics(id: string) { + const params = { 'metrics[views]': 'total', 'metrics[downloads]': 'total' }; + + return this.jsonApiService + .get< + JsonApiResponseWithMeta, PreprintMetaJsonApi, null> + >(`${this.apiUrl}/preprints/${id}/`, params) + .pipe( + map((response) => ({ + downloads: response.meta.metrics.downloads, + views: response.meta.metrics.views, + })) + ); + } + deletePreprint(id: string) { return this.jsonApiService.delete(`${this.apiUrl}/preprints/${id}/`); } - updatePreprint(id: string, payload: Partial): Observable { + updatePreprint(id: string, payload: Partial): Observable { const apiPayload = this.mapPreprintDomainToApiPayload(payload); return this.jsonApiService @@ -132,7 +143,7 @@ export class PreprintsService { .pipe(map((response) => PreprintsMapper.fromPreprintJsonApi(response.data))); } - private mapPreprintDomainToApiPayload(domainPayload: Partial): Partial { + private mapPreprintDomainToApiPayload(domainPayload: Partial): Partial { const apiPayload: Record = {}; Object.entries(domainPayload).forEach(([key, value]) => { if (value !== undefined && this.domainToApiFieldMap[key]) { diff --git a/src/app/features/preprints/store/my-preprints/index.ts b/src/app/features/preprints/store/my-preprints/index.ts new file mode 100644 index 000000000..a83accbb1 --- /dev/null +++ b/src/app/features/preprints/store/my-preprints/index.ts @@ -0,0 +1,4 @@ +export * from './my-preprints.actions'; +export * from './my-preprints.model'; +export * from './my-preprints.selectors'; +export * from './my-preprints.state'; diff --git a/src/app/features/preprints/store/my-preprints/my-preprints.actions.ts b/src/app/features/preprints/store/my-preprints/my-preprints.actions.ts new file mode 100644 index 000000000..6718a06e7 --- /dev/null +++ b/src/app/features/preprints/store/my-preprints/my-preprints.actions.ts @@ -0,0 +1,11 @@ +import { SearchFilters } from '@shared/models'; + +export class FetchMyPreprints { + static readonly type = '[My Preprints] Fetch My Preprints'; + + constructor( + public pageNumber: number, + public pageSize: number, + public filters: SearchFilters + ) {} +} diff --git a/src/app/features/preprints/store/my-preprints/my-preprints.model.ts b/src/app/features/preprints/store/my-preprints/my-preprints.model.ts new file mode 100644 index 000000000..d2343a4ec --- /dev/null +++ b/src/app/features/preprints/store/my-preprints/my-preprints.model.ts @@ -0,0 +1,16 @@ +import { AsyncStateWithTotalCount } from '@shared/models'; + +import { PreprintShortInfo } from '../../models'; + +export interface MyPreprintsStateModel { + myPreprints: AsyncStateWithTotalCount; +} + +export const DEFAULT_MY_PREPRINTS_STATE: MyPreprintsStateModel = { + myPreprints: { + data: [], + isLoading: false, + error: null, + totalCount: 0, + }, +}; diff --git a/src/app/features/preprints/store/my-preprints/my-preprints.selectors.ts b/src/app/features/preprints/store/my-preprints/my-preprints.selectors.ts new file mode 100644 index 000000000..6a13f37ee --- /dev/null +++ b/src/app/features/preprints/store/my-preprints/my-preprints.selectors.ts @@ -0,0 +1,21 @@ +import { Selector } from '@ngxs/store'; + +import { MyPreprintsStateModel } from './my-preprints.model'; +import { MyPreprintsState } from './my-preprints.state'; + +export class MyPreprintsSelectors { + @Selector([MyPreprintsState]) + static getMyPreprints(state: MyPreprintsStateModel) { + return state.myPreprints.data; + } + + @Selector([MyPreprintsState]) + static getMyPreprintsTotalCount(state: MyPreprintsStateModel) { + return state.myPreprints.totalCount; + } + + @Selector([MyPreprintsState]) + static areMyPreprintsLoading(state: MyPreprintsStateModel) { + return state.myPreprints.isLoading; + } +} diff --git a/src/app/features/preprints/store/my-preprints/my-preprints.state.ts b/src/app/features/preprints/store/my-preprints/my-preprints.state.ts new file mode 100644 index 000000000..dc6bc45ce --- /dev/null +++ b/src/app/features/preprints/store/my-preprints/my-preprints.state.ts @@ -0,0 +1,43 @@ +import { Action, State, StateContext } from '@ngxs/store'; +import { patch } from '@ngxs/store/operators'; + +import { tap } from 'rxjs'; +import { catchError } from 'rxjs/operators'; + +import { inject, Injectable } from '@angular/core'; + +import { handleSectionError } from '@shared/helpers'; + +import { PreprintsService } from '../../services'; + +import { FetchMyPreprints } from './my-preprints.actions'; +import { DEFAULT_MY_PREPRINTS_STATE, MyPreprintsStateModel } from './my-preprints.model'; + +@State({ + name: 'myPreprints', + defaults: { ...DEFAULT_MY_PREPRINTS_STATE }, +}) +@Injectable() +export class MyPreprintsState { + private preprintsService = inject(PreprintsService); + + @Action(FetchMyPreprints) + fetchMyPreprints(ctx: StateContext, action: FetchMyPreprints) { + ctx.setState(patch({ myPreprints: patch({ isLoading: true }) })); + + return this.preprintsService.getMyPreprints(action.pageNumber, action.pageSize, action.filters).pipe( + tap((preprints) => { + ctx.setState( + patch({ + myPreprints: patch({ + isLoading: false, + data: preprints.data, + totalCount: preprints.totalCount, + }), + }) + ); + }), + catchError((error) => handleSectionError(ctx, 'myPreprints', error)) + ); + } +} diff --git a/src/app/features/preprints/store/preprint-stepper/preprint-stepper.actions.ts b/src/app/features/preprints/store/preprint-stepper/preprint-stepper.actions.ts index 62ee6f9b7..288638c23 100644 --- a/src/app/features/preprints/store/preprint-stepper/preprint-stepper.actions.ts +++ b/src/app/features/preprints/store/preprint-stepper/preprint-stepper.actions.ts @@ -1,5 +1,5 @@ import { PreprintFileSource } from '@osf/features/preprints/enums'; -import { Preprint } from '@osf/features/preprints/models'; +import { PreprintModel } from '@osf/features/preprints/models'; import { StringOrNull } from '@shared/helpers'; import { FileFolderModel, FileModel, LicenseOptions } from '@shared/models'; @@ -24,7 +24,7 @@ export class UpdatePreprint { constructor( public id: string, - public payload: Partial + public payload: Partial ) {} } diff --git a/src/app/features/preprints/store/preprint-stepper/preprint-stepper.model.ts b/src/app/features/preprints/store/preprint-stepper/preprint-stepper.model.ts index 4f6b7eb55..42261f965 100644 --- a/src/app/features/preprints/store/preprint-stepper/preprint-stepper.model.ts +++ b/src/app/features/preprints/store/preprint-stepper/preprint-stepper.model.ts @@ -1,12 +1,12 @@ import { PreprintFileSource } from '@osf/features/preprints/enums'; -import { Preprint, PreprintFilesLinks } from '@osf/features/preprints/models'; +import { PreprintFilesLinks, PreprintModel } from '@osf/features/preprints/models'; import { StringOrNull } from '@shared/helpers'; import { AsyncStateModel, FileFolderModel, FileModel, IdName } from '@shared/models'; import { LicenseModel } from '@shared/models/license.model'; export interface PreprintStepperStateModel { selectedProviderId: StringOrNull; - preprint: AsyncStateModel; + preprint: AsyncStateModel; fileSource: PreprintFileSource; preprintFilesLinks: AsyncStateModel; preprintFile: AsyncStateModel; diff --git a/src/app/features/preprints/store/preprint-stepper/preprint-stepper.state.ts b/src/app/features/preprints/store/preprint-stepper/preprint-stepper.state.ts index 35cb73caf..78962f57d 100644 --- a/src/app/features/preprints/store/preprint-stepper/preprint-stepper.state.ts +++ b/src/app/features/preprints/store/preprint-stepper/preprint-stepper.state.ts @@ -8,7 +8,7 @@ import { HttpEventType } from '@angular/common/http'; import { inject, Injectable } from '@angular/core'; import { PreprintFileSource } from '@osf/features/preprints/enums'; -import { Preprint } from '@osf/features/preprints/models'; +import { PreprintModel } from '@osf/features/preprints/models'; import { PreprintFilesService, PreprintLicensesService, @@ -198,7 +198,7 @@ export class PreprintStepperState { ctx.setState(patch({ preprint: patch({ isSubmitting: true }) })); return this.preprintFilesService.updateFileRelationship(state.preprint.data!.id, action.fileId).pipe( - tap((preprint: Preprint) => { + tap((preprint: PreprintModel) => { ctx.patchState({ preprint: { ...ctx.getState().preprint, @@ -344,7 +344,7 @@ export class PreprintStepperState { const fileIdAfterCopy = file.id.split('/')[1]; return this.preprintFilesService.updateFileRelationship(createdPreprintId, fileIdAfterCopy).pipe( - tap((preprint: Preprint) => { + tap((preprint: PreprintModel) => { ctx.patchState({ preprint: { ...ctx.getState().preprint, diff --git a/src/app/features/preprints/store/preprint/preprint.actions.ts b/src/app/features/preprints/store/preprint/preprint.actions.ts index 5446d1966..e13179b78 100644 --- a/src/app/features/preprints/store/preprint/preprint.actions.ts +++ b/src/app/features/preprints/store/preprint/preprint.actions.ts @@ -1,15 +1,4 @@ import { StringOrNull } from '@shared/helpers'; -import { SearchFilters } from '@shared/models'; - -export class FetchMyPreprints { - static readonly type = '[Preprint] Fetch My Preprints'; - - constructor( - public pageNumber: number, - public pageSize: number, - public filters: SearchFilters - ) {} -} export class FetchPreprintById { static readonly type = '[Preprint] Fetch Preprint By Id'; @@ -43,6 +32,10 @@ export class FetchPreprintRequestActions { constructor(public requestId: string) {} } +export class FetchPreprintMetrics { + static readonly type = '[Preprint] Fetch Preprint Metrics'; +} + export class WithdrawPreprint { static readonly type = '[Preprint] Withdraw Preprint'; diff --git a/src/app/features/preprints/store/preprint/preprint.model.ts b/src/app/features/preprints/store/preprint/preprint.model.ts index 0f18743c5..6876edc7e 100644 --- a/src/app/features/preprints/store/preprint/preprint.model.ts +++ b/src/app/features/preprints/store/preprint/preprint.model.ts @@ -1,17 +1,17 @@ import { ReviewAction } from '@osf/features/moderation/models'; -import { Preprint, PreprintRequestAction, PreprintShortInfo } from '@osf/features/preprints/models'; -import { PreprintRequest } from '@osf/features/preprints/models/preprint-request.models'; -import { AsyncStateModel, AsyncStateWithTotalCount, FileModel, FileVersionModel } from '@shared/models'; +import { AsyncStateModel, FileModel, FileVersionModel } from '@shared/models'; + +import { PreprintMetrics, PreprintModel, PreprintRequest, PreprintRequestAction } from '../../models'; export interface PreprintStateModel { - myPreprints: AsyncStateWithTotalCount; - preprint: AsyncStateModel; + preprint: AsyncStateModel; preprintFile: AsyncStateModel; fileVersions: AsyncStateModel; preprintVersionIds: AsyncStateModel; preprintReviewActions: AsyncStateModel; preprintRequests: AsyncStateModel; preprintRequestsActions: AsyncStateModel; + metrics: AsyncStateModel; } export const DefaultState: PreprintStateModel = { @@ -21,11 +21,11 @@ export const DefaultState: PreprintStateModel = { error: null, isSubmitting: false, }, - myPreprints: { - data: [], + metrics: { + data: null, isLoading: false, error: null, - totalCount: 0, + isSubmitting: false, }, preprintFile: { data: null, diff --git a/src/app/features/preprints/store/preprint/preprint.selectors.ts b/src/app/features/preprints/store/preprint/preprint.selectors.ts index fa413fd7a..66cdf22fe 100644 --- a/src/app/features/preprints/store/preprint/preprint.selectors.ts +++ b/src/app/features/preprints/store/preprint/preprint.selectors.ts @@ -1,24 +1,11 @@ import { Selector } from '@ngxs/store'; +import { UserPermissions } from '@osf/shared/enums'; + import { PreprintStateModel } from './preprint.model'; import { PreprintState } from './preprint.state'; export class PreprintSelectors { - @Selector([PreprintState]) - static getMyPreprints(state: PreprintStateModel) { - return state.myPreprints.data; - } - - @Selector([PreprintState]) - static getMyPreprintsTotalCount(state: PreprintStateModel) { - return state.myPreprints.totalCount; - } - - @Selector([PreprintState]) - static areMyPreprintsLoading(state: PreprintStateModel) { - return state.myPreprints.isLoading; - } - @Selector([PreprintState]) static getPreprint(state: PreprintStateModel) { return state.preprint.data; @@ -93,4 +80,24 @@ export class PreprintSelectors { static arePreprintRequestActionsLoading(state: PreprintStateModel) { return state.preprintRequestsActions.isLoading; } + + @Selector([PreprintState]) + static hasAdminAccess(state: PreprintStateModel) { + return state.preprint.data?.currentUserPermissions.includes(UserPermissions.Admin) || false; + } + + @Selector([PreprintState]) + static hasWriteAccess(state: PreprintStateModel) { + return state.preprint.data?.currentUserPermissions.includes(UserPermissions.Write) || false; + } + + @Selector([PreprintState]) + static getPreprintMetrics(state: PreprintStateModel) { + return state.metrics.data; + } + + @Selector([PreprintState]) + static arePreprintMetricsLoading(state: PreprintStateModel) { + return state.metrics.isLoading; + } } diff --git a/src/app/features/preprints/store/preprint/preprint.state.ts b/src/app/features/preprints/store/preprint/preprint.state.ts index 4e7a27651..ecda51613 100644 --- a/src/app/features/preprints/store/preprint/preprint.state.ts +++ b/src/app/features/preprints/store/preprint/preprint.state.ts @@ -6,15 +6,16 @@ import { catchError } from 'rxjs/operators'; import { inject, Injectable } from '@angular/core'; -import { PreprintsService } from '@osf/features/preprints/services'; -import { handleSectionError } from '@shared/helpers'; -import { FilesService } from '@shared/services'; +import { handleSectionError } from '@osf/shared/helpers'; +import { FilesService } from '@osf/shared/services'; + +import { PreprintsService } from '../../services'; import { - FetchMyPreprints, FetchPreprintById, FetchPreprintFile, FetchPreprintFileVersions, + FetchPreprintMetrics, FetchPreprintRequestActions, FetchPreprintRequests, FetchPreprintReviewActions, @@ -35,26 +36,6 @@ export class PreprintState { private preprintsService = inject(PreprintsService); private fileService = inject(FilesService); - @Action(FetchMyPreprints) - fetchMyPreprints(ctx: StateContext, action: FetchMyPreprints) { - ctx.setState(patch({ myPreprints: patch({ isLoading: true }) })); - - return this.preprintsService.getMyPreprints(action.pageNumber, action.pageSize, action.filters).pipe( - tap((preprints) => { - ctx.setState( - patch({ - myPreprints: patch({ - isLoading: false, - data: preprints.data, - totalCount: preprints.totalCount, - }), - }) - ); - }), - catchError((error) => handleSectionError(ctx, 'myPreprints', error)) - ); - } - @Action(FetchPreprintById) fetchPreprintById(ctx: StateContext, action: FetchPreprintById) { ctx.setState( @@ -65,16 +46,20 @@ export class PreprintState { preprintReviewActions: patch({ isLoading: false, data: [] }), preprintRequests: patch({ isLoading: false, data: [] }), preprintRequestsActions: patch({ isLoading: false, data: [] }), + metrics: patch({ isLoading: false, data: null }), }) ); return this.preprintsService.getByIdWithEmbeds(action.id).pipe( tap((preprint) => { ctx.setState(patch({ preprint: patch({ isLoading: false, data: preprint }) })); + if (!preprint.dateWithdrawn) { ctx.dispatch(new FetchPreprintFile()); } + ctx.dispatch(new FetchPreprintVersionIds()); + ctx.dispatch(new FetchPreprintMetrics()); }), catchError((error) => handleSectionError(ctx, 'preprint', error)) ); @@ -125,6 +110,22 @@ export class PreprintState { ); } + @Action(FetchPreprintMetrics) + fetchPreprintMetrics(ctx: StateContext) { + const preprintId = ctx.getState().preprint.data?.id; + + if (!preprintId) return; + + ctx.setState(patch({ metrics: patch({ isLoading: true }) })); + + return this.preprintsService.getPreprintMetrics(preprintId).pipe( + tap((metrics) => { + ctx.setState(patch({ metrics: patch({ isLoading: false, data: metrics }) })); + }), + catchError((error) => handleSectionError(ctx, 'metrics', error)) + ); + } + @Action(FetchPreprintReviewActions) fetchPreprintReviewActions(ctx: StateContext) { const preprintId = ctx.getState().preprint.data?.id; diff --git a/src/app/features/registries/components/review/review.component.html b/src/app/features/registries/components/review/review.component.html index de6695ca7..46cfb88b6 100644 --- a/src/app/features/registries/components/review/review.component.html +++ b/src/app/features/registries/components/review/review.component.html @@ -37,18 +37,10 @@

{{ 'common.labels.contributors' | translate }}

-

{{ 'shared.license.title' | translate }}

+

{{ 'common.labels.license' | translate }}

+ @if (draftRegistration()?.license && license()) { - - - -
{{ license()?.name }}
-
- -
{{ license()!.text | interpolate: licenseOptionsRecord() }}
-
-
-
+ } @else {

{{ 'common.labels.noData' | translate }}

diff --git a/src/app/features/registries/components/review/review.component.ts b/src/app/features/registries/components/review/review.component.ts index 3df64f67e..3a58230e7 100644 --- a/src/app/features/registries/components/review/review.component.ts +++ b/src/app/features/registries/components/review/review.component.ts @@ -2,7 +2,6 @@ import { createDispatchMap, select } from '@ngxs/store'; import { TranslatePipe } from '@ngx-translate/core'; -import { Accordion, AccordionContent, AccordionHeader, AccordionPanel } from 'primeng/accordion'; import { Button } from 'primeng/button'; import { Card } from 'primeng/card'; import { Message } from 'primeng/message'; @@ -15,10 +14,13 @@ import { toSignal } from '@angular/core/rxjs-interop'; import { ActivatedRoute, Router } from '@angular/router'; import { ENVIRONMENT } from '@core/provider/environment.provider'; -import { ContributorsListComponent, RegistrationBlocksDataComponent } from '@osf/shared/components'; +import { + ContributorsListComponent, + LicenseDisplayComponent, + RegistrationBlocksDataComponent, +} from '@osf/shared/components'; import { INPUT_VALIDATION_MESSAGES } from '@osf/shared/constants'; import { FieldType, ResourceType, UserPermissions } from '@osf/shared/enums'; -import { InterpolatePipe } from '@osf/shared/pipes'; import { CustomConfirmationService, CustomDialogService, ToastService } from '@osf/shared/services'; import { ContributorsSelectors, @@ -47,13 +49,9 @@ import { SelectComponentsDialogComponent } from '../select-components-dialog/sel Message, Tag, Button, - Accordion, - AccordionContent, - AccordionHeader, - AccordionPanel, - InterpolatePipe, RegistrationBlocksDataComponent, ContributorsListComponent, + LicenseDisplayComponent, ], templateUrl: './review.component.html', styleUrl: './review.component.scss', diff --git a/src/app/shared/components/contributors/add-contributor-item/add-contributor-item.component.html b/src/app/shared/components/contributors/add-contributor-item/add-contributor-item.component.html index 239a1b4c2..76fd7d072 100644 --- a/src/app/shared/components/contributors/add-contributor-item/add-contributor-item.component.html +++ b/src/app/shared/components/contributors/add-contributor-item/add-contributor-item.component.html @@ -25,7 +25,7 @@
-
-

{{ 'project.overview.metadata.license' | translate }}

+

{{ 'common.labels.license' | translate }}

{{ resource.license?.name ?? ('project.overview.metadata.noLicense' | translate) }}
diff --git a/src/app/shared/mappers/identifiers.mapper.ts b/src/app/shared/mappers/identifiers.mapper.ts index 9db03b1dc..04d5cdaca 100644 --- a/src/app/shared/mappers/identifiers.mapper.ts +++ b/src/app/shared/mappers/identifiers.mapper.ts @@ -1,14 +1,16 @@ import { Identifier, IdentifiersResponseJsonApi } from '@shared/models'; export class IdentifiersMapper { - static fromJsonApi(response: IdentifiersResponseJsonApi): Identifier[] { - return response?.data.map((rawIdentifier) => { - return { - category: rawIdentifier.attributes.category, - value: rawIdentifier.attributes.value, - id: rawIdentifier.id, - type: rawIdentifier.type, - }; - }); + static fromJsonApi(response: IdentifiersResponseJsonApi | undefined): Identifier[] { + if (!response || !response.data) { + return []; + } + + return response?.data.map((rawIdentifier) => ({ + id: rawIdentifier.id, + type: rawIdentifier.type, + category: rawIdentifier.attributes.category, + value: rawIdentifier.attributes.value, + })); } } diff --git a/src/app/shared/mappers/licenses.mapper.ts b/src/app/shared/mappers/licenses.mapper.ts index aafaf8d67..f4726cfb3 100644 --- a/src/app/shared/mappers/licenses.mapper.ts +++ b/src/app/shared/mappers/licenses.mapper.ts @@ -2,10 +2,18 @@ import { LicenseDataJsonApi, LicenseModel, LicensesResponseJsonApi } from '../mo export class LicensesMapper { static fromLicensesResponse(response: LicensesResponseJsonApi): LicenseModel[] { - return response.data.map((item) => LicensesMapper.fromLicenseDataJsonApi(item)); + if (!response.data) { + return []; + } + + return response.data.map((item) => LicensesMapper.fromLicenseDataJsonApi(item)).filter((item) => !!item); } - static fromLicenseDataJsonApi(data: LicenseDataJsonApi): LicenseModel { + static fromLicenseDataJsonApi(data: LicenseDataJsonApi): LicenseModel | null { + if (!data) { + return null; + } + return { id: data?.id, name: data?.attributes?.name, diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 01f68a556..11e832513 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -136,7 +136,8 @@ "contributors": "Contributors", "updated": "Updated", "dateUpdated": "Date Updated", - "dateCreated": "Date Created" + "dateCreated": "Date Created", + "license": "License" }, "deleteConfirmation": { "header": "Delete", @@ -720,7 +721,6 @@ "supplementsText2": "on OSF Preprints", "dateCreated": "Date Created", "dateUpdated": "Date Updated", - "license": "License", "projectDOI": "Project DOI", "publicationDOI": "Publication DOI", "registrationDOI": "Registration DOI", @@ -1386,7 +1386,6 @@ "title": "Title", "description": "Description", "chooseLicense": "Choose License", - "license": "License", "licensePlaceholder": "Select license", "tags": "Tags", "tagsPlaceholder": "Add tags here", @@ -2220,7 +2219,6 @@ "contributors": "Contributors", "authors": "Authors", "affiliatedInstitutions": "Affiliated Institutions", - "license": "License", "publicationDoi": "Publication DOI", "subjects": "Subjects", "tags": "Tags", @@ -2741,7 +2739,6 @@ "description": "Description:", "descriptionBold": "Description: ", "provider": "Provider:", - "license": "License:", "context": "Context", "registrationTemplate": "Registration Template:", "conflictOfInterestResponse": "Conflict of Interest response:", diff --git a/src/main.ts b/src/main.ts index 62b5a3c94..e005d74a2 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,9 +3,6 @@ import { bootstrapApplication } from '@angular/platform-browser'; import { AppComponent } from '@osf/app.component'; import { appConfig } from '@osf/app.config'; -// Import CEDAR Embeddable Editor web component -import 'cedar-embeddable-editor'; - bootstrapApplication(AppComponent, { providers: [...appConfig.providers], }).catch((err) => diff --git a/src/styles/_fonts.scss b/src/styles/_fonts.scss index 1dda8264e..45559d13f 100644 --- a/src/styles/_fonts.scss +++ b/src/styles/_fonts.scss @@ -45,3 +45,7 @@ font-style: normal; font-display: swap; } + +// Override default value in font awesome library + +$fa-font-display: swap; diff --git a/src/styles/overrides/accordion.scss b/src/styles/overrides/accordion.scss index 221e87564..c2121cb4d 100644 --- a/src/styles/overrides/accordion.scss +++ b/src/styles/overrides/accordion.scss @@ -46,7 +46,7 @@ row-gap: 1.5rem; p { - font-weight: 300; + font-weight: 400; } } } diff --git a/src/testing/mocks/environment.token.mock.ts b/src/testing/mocks/environment.token.mock.ts index 1c8c64216..70f4d3ce1 100644 --- a/src/testing/mocks/environment.token.mock.ts +++ b/src/testing/mocks/environment.token.mock.ts @@ -32,5 +32,20 @@ export const EnvironmentTokenMock = { webUrl: 'http://localhost:4200', supportEmail: 'support@test.com', defaultProvider: 'osf', + newRelicEnabled: false, + newRelicInitDistributedTracingEnabled: false, + newRelicInitPerformanceCaptureMeasures: false, + newRelicInitPrivacyCookiesEnabled: false, + newRelicInitAjaxDenyList: [], + newRelicInfoBeacon: '', + newRelicInfoErrorBeacon: '', + newRelicInfoLicenseKey: '', + newRelicInfoApplicationID: '', + newRelicInfoSa: 1, + newRelicLoaderConfigAccountID: '', + newRelicLoaderConfigTrustKey: '', + newRelicLoaderConfigAgengID: '', + newRelicLoaderConfigLicenseKey: '', + newRelicLoaderConfigApplicationID: '', }, }; diff --git a/src/testing/mocks/new-relic.mock.ts b/src/testing/mocks/new-relic.mock.ts new file mode 100644 index 000000000..6afb0ce1a --- /dev/null +++ b/src/testing/mocks/new-relic.mock.ts @@ -0,0 +1,17 @@ +export const NEW_RELIC_CONFIG_MOCK = { + newRelicEnabled: true, + newRelicInitDistributedTracingEnabled: true, + newRelicInitPerformanceCaptureMeasures: true, + newRelicInitPrivacyCookiesEnabled: true, + newRelicInitAjaxDenyList: ['test-url'], + newRelicInfoBeacon: 'test-beacon', + newRelicInfoErrorBeacon: 'test-error-beacon', + newRelicInfoLicenseKey: 'test-license-key', + newRelicInfoApplicationID: 'test-app-id', + newRelicInfoSa: 1, + newRelicLoaderConfigAccountID: 'test-account-id', + newRelicLoaderConfigTrustKey: 'test-trust-key', + newRelicLoaderConfigAgengID: 'test-agent-id', + newRelicLoaderConfigLicenseKey: 'test-loader-license-key', + newRelicLoaderConfigApplicationID: 'test-loader-app-id', +}; From 25ae015e8a9fdde59f0169c65ce087e8e5509509 Mon Sep 17 00:00:00 2001 From: nsemets Date: Mon, 27 Oct 2025 18:34:49 +0200 Subject: [PATCH 25/25] [ENG-9643] Get New Relic fully installed - Ticket: [ENG-9643] - Feature flag: n/a ## Summary of Changes 1. Added snippet for new relic. --- .../application.initialization.provider.ts | 2 +- src/app/shared/models/environment.model.ts | 2 +- src/assets/config/template.json | 25 +- src/assets/js/newrelic/newrelic.snippet.js | 3319 +++++++++++++++++ src/environments/environment.development.ts | 2 +- src/environments/environment.docker.ts | 2 +- src/environments/environment.staging.ts | 2 +- src/environments/environment.test-osf.ts | 2 +- src/environments/environment.test.ts | 2 +- src/environments/environment.ts | 2 +- src/index.html | 1 + src/testing/mocks/environment.token.mock.ts | 2 +- src/testing/mocks/new-relic.mock.ts | 2 +- 13 files changed, 3331 insertions(+), 34 deletions(-) create mode 100644 src/assets/js/newrelic/newrelic.snippet.js diff --git a/src/app/core/provider/application.initialization.provider.ts b/src/app/core/provider/application.initialization.provider.ts index 137ab79e7..b022860e9 100644 --- a/src/app/core/provider/application.initialization.provider.ts +++ b/src/app/core/provider/application.initialization.provider.ts @@ -63,7 +63,7 @@ export function initializeApplication() { loader_config: { accountID: environment.newRelicLoaderConfigAccountID, trustKey: environment.newRelicLoaderConfigTrustKey, - agentID: environment.newRelicLoaderConfigAgengID, + agentID: environment.newRelicLoaderConfigAgentID, licenseKey: environment.newRelicLoaderConfigLicenseKey, applicationID: environment.newRelicLoaderConfigApplicationID, }, diff --git a/src/app/shared/models/environment.model.ts b/src/app/shared/models/environment.model.ts index 03ee09262..9b399bbed 100644 --- a/src/app/shared/models/environment.model.ts +++ b/src/app/shared/models/environment.model.ts @@ -25,7 +25,7 @@ export interface EnvironmentModel { newRelicInfoSa: number; newRelicLoaderConfigAccountID: string; newRelicLoaderConfigTrustKey: string; - newRelicLoaderConfigAgengID: string; + newRelicLoaderConfigAgentID: string; newRelicLoaderConfigLicenseKey: string; newRelicLoaderConfigApplicationID: string; activityLogs?: { diff --git a/src/assets/config/template.json b/src/assets/config/template.json index 9849e4300..55891f4a1 100644 --- a/src/assets/config/template.json +++ b/src/assets/config/template.json @@ -12,29 +12,6 @@ "googleTagManagerId": "", "googleFilePickerApiKey": "", "googleFilePickerAppId": 0, - "newRelic": { - "enabled": false, - "init": { - "distributed_tracing": { "enabled": false }, - "performance": { "capture_measures": false }, - "privacy": { "cookies_enabled": true }, - "ajax": { "deny_list": [""] } - }, - "info": { - "beacon": "", - "errorBeacon": "", - "licenseKey": "", - "applicationID": "", - "sa": 1 - }, - "loader_config": { - "accountID": "", - "trustKey": "", - "agentID": "", - "licenseKey": "", - "applicationID": "" - } - }, "newRelicEnabled": true, "newRelicInitDistributedTracingEnabled": false, "newRelicInitPerformanceCaptureMeasures": false, @@ -47,7 +24,7 @@ "newRelicInfoSa": 1, "newRelicLoaderConfigAccountID": "", "newRelicLoaderConfigTrustKey": "", - "newRelicLoaderConfigAgengID": "", + "newRelicLoaderConfigAgentID": "", "newRelicLoaderConfigLicenseKey": "", "newRelicLoaderConfigApplicationID": "" } diff --git a/src/assets/js/newrelic/newrelic.snippet.js b/src/assets/js/newrelic/newrelic.snippet.js new file mode 100644 index 000000000..e63e4fc1d --- /dev/null +++ b/src/assets/js/newrelic/newrelic.snippet.js @@ -0,0 +1,3319 @@ +window.NREUM || (NREUM = {}); +NREUM.init = { + distributed_tracing: { enabled: true }, + performance: { capture_measures: true }, + privacy: { cookies_enabled: true }, + ajax: { deny_list: ['bam.nr-data.net'] }, +}; + +NREUM.loader_config = { + accountID: '772413', + trustKey: '772413', + agentID: '1835137194', + licenseKey: '704513e63b', + applicationID: '1835137194', +}; +NREUM.info = { + beacon: 'bam.nr-data.net', + errorBeacon: 'bam.nr-data.net', + licenseKey: '704513e63b', + applicationID: '1835137194', + sa: 1, +}; /*! For license information please see nr-loader-spa-1.301.0.min.js.LICENSE.txt */ +(() => { + var e, + t, + r = { + 384: (e, t, r) => { + 'use strict'; + r.d(t, { NT: () => a, US: () => d, Zm: () => s, bQ: () => u, dV: () => c, pV: () => l }); + var n = r(6154), + i = r(1863), + o = r(1910); + const a = { beacon: 'bam.nr-data.net', errorBeacon: 'bam.nr-data.net' }; + function s() { + return n.gm.NREUM || (n.gm.NREUM = {}), void 0 === n.gm.newrelic && (n.gm.newrelic = n.gm.NREUM), n.gm.NREUM; + } + function c() { + let e = s(); + return ( + e.o || + ((e.o = { + ST: n.gm.setTimeout, + SI: n.gm.setImmediate || n.gm.setInterval, + CT: n.gm.clearTimeout, + XHR: n.gm.XMLHttpRequest, + REQ: n.gm.Request, + EV: n.gm.Event, + PR: n.gm.Promise, + MO: n.gm.MutationObserver, + FETCH: n.gm.fetch, + WS: n.gm.WebSocket, + }), + (0, o.i)(...Object.values(e.o))), + e + ); + } + function u(e, t) { + let r = s(); + (r.initializedAgents ??= {}), + (t.initializedAt = { ms: (0, i.t)(), date: new Date() }), + (r.initializedAgents[e] = t); + } + function d(e, t) { + s()[e] = t; + } + function l() { + return ( + (function () { + let e = s(); + const t = e.info || {}; + e.info = { beacon: a.beacon, errorBeacon: a.errorBeacon, ...t }; + })(), + (function () { + let e = s(); + const t = e.init || {}; + e.init = { ...t }; + })(), + c(), + (function () { + let e = s(); + const t = e.loader_config || {}; + e.loader_config = { ...t }; + })(), + s() + ); + } + }, + 782: (e, t, r) => { + 'use strict'; + r.d(t, { T: () => n }); + const n = r(860).K7.pageViewTiming; + }, + 860: (e, t, r) => { + 'use strict'; + r.d(t, { + $J: () => d, + K7: () => c, + P3: () => u, + XX: () => i, + Yy: () => s, + df: () => o, + qY: () => n, + v4: () => a, + }); + const n = 'events', + i = 'jserrors', + o = 'browser/blobs', + a = 'rum', + s = 'browser/logs', + c = { + ajax: 'ajax', + genericEvents: 'generic_events', + jserrors: i, + logging: 'logging', + metrics: 'metrics', + pageAction: 'page_action', + pageViewEvent: 'page_view_event', + pageViewTiming: 'page_view_timing', + sessionReplay: 'session_replay', + sessionTrace: 'session_trace', + softNav: 'soft_navigations', + spa: 'spa', + }, + u = { + [c.pageViewEvent]: 1, + [c.pageViewTiming]: 2, + [c.metrics]: 3, + [c.jserrors]: 4, + [c.spa]: 5, + [c.ajax]: 6, + [c.sessionTrace]: 7, + [c.softNav]: 8, + [c.sessionReplay]: 9, + [c.logging]: 10, + [c.genericEvents]: 11, + }, + d = { + [c.pageViewEvent]: a, + [c.pageViewTiming]: n, + [c.ajax]: n, + [c.spa]: n, + [c.softNav]: n, + [c.metrics]: i, + [c.jserrors]: i, + [c.sessionTrace]: o, + [c.sessionReplay]: o, + [c.logging]: s, + [c.genericEvents]: 'ins', + }; + }, + 944: (e, t, r) => { + 'use strict'; + r.d(t, { R: () => i }); + var n = r(3241); + function i(e, t) { + 'function' == typeof console.debug && + (console.debug( + 'New Relic Warning: https://github.com/newrelic/newrelic-browser-agent/blob/main/docs/warning-codes.md#'.concat( + e + ), + t + ), + (0, n.W)({ + agentIdentifier: null, + drained: null, + type: 'data', + name: 'warn', + feature: 'warn', + data: { code: e, secondary: t }, + })); + } + }, + 993: (e, t, r) => { + 'use strict'; + r.d(t, { A$: () => o, ET: () => a, TZ: () => s, p_: () => i }); + var n = r(860); + const i = { ERROR: 'ERROR', WARN: 'WARN', INFO: 'INFO', DEBUG: 'DEBUG', TRACE: 'TRACE' }, + o = { OFF: 0, ERROR: 1, WARN: 2, INFO: 3, DEBUG: 4, TRACE: 5 }, + a = 'log', + s = n.K7.logging; + }, + 1687: (e, t, r) => { + 'use strict'; + r.d(t, { Ak: () => u, Ze: () => f, x3: () => d }); + var n = r(3241), + i = r(7836), + o = r(3606), + a = r(860), + s = r(2646); + const c = {}; + function u(e, t) { + const r = { staged: !1, priority: a.P3[t] || 0 }; + l(e), c[e].get(t) || c[e].set(t, r); + } + function d(e, t) { + e && c[e] && (c[e].get(t) && c[e].delete(t), p(e, t, !1), c[e].size && h(e)); + } + function l(e) { + if (!e) throw new Error('agentIdentifier required'); + c[e] || (c[e] = new Map()); + } + function f(e = '', t = 'feature', r = !1) { + if ((l(e), !e || !c[e].get(t) || r)) return p(e, t); + (c[e].get(t).staged = !0), h(e); + } + function h(e) { + const t = Array.from(c[e]); + t.every(([e, t]) => t.staged) && + (t.sort((e, t) => e[1].priority - t[1].priority), + t.forEach(([t]) => { + c[e].delete(t), p(e, t); + })); + } + function p(e, t, r = !0) { + const a = e ? i.ee.get(e) : i.ee, + c = o.i.handlers; + if (!a.aborted && a.backlog && c) { + if (((0, n.W)({ agentIdentifier: e, type: 'lifecycle', name: 'drain', feature: t }), r)) { + const e = a.backlog[t], + r = c[t]; + if (r) { + for (let t = 0; e && t < e.length; ++t) g(e[t], r); + Object.entries(r).forEach(([e, t]) => { + Object.values(t || {}).forEach((t) => { + t[0]?.on && t[0]?.context() instanceof s.y && t[0].on(e, t[1]); + }); + }); + } + } + a.isolatedBacklog || delete c[t], (a.backlog[t] = null), a.emit('drain-' + t, []); + } + } + function g(e, t) { + var r = e[1]; + Object.values(t[r] || {}).forEach((t) => { + var r = e[0]; + if (t[0] === r) { + var n = t[1], + i = e[3], + o = e[2]; + n.apply(i, o); + } + }); + } + }, + 1741: (e, t, r) => { + 'use strict'; + r.d(t, { W: () => o }); + var n = r(944), + i = r(4261); + class o { + #e(e, ...t) { + if (this[e] !== o.prototype[e]) return this[e](...t); + (0, n.R)(35, e); + } + addPageAction(e, t) { + return this.#e(i.hG, e, t); + } + register(e) { + return this.#e(i.eY, e); + } + recordCustomEvent(e, t) { + return this.#e(i.fF, e, t); + } + setPageViewName(e, t) { + return this.#e(i.Fw, e, t); + } + setCustomAttribute(e, t, r) { + return this.#e(i.cD, e, t, r); + } + noticeError(e, t) { + return this.#e(i.o5, e, t); + } + setUserId(e) { + return this.#e(i.Dl, e); + } + setApplicationVersion(e) { + return this.#e(i.nb, e); + } + setErrorHandler(e) { + return this.#e(i.bt, e); + } + addRelease(e, t) { + return this.#e(i.k6, e, t); + } + log(e, t) { + return this.#e(i.$9, e, t); + } + start() { + return this.#e(i.d3); + } + finished(e) { + return this.#e(i.BL, e); + } + recordReplay() { + return this.#e(i.CH); + } + pauseReplay() { + return this.#e(i.Tb); + } + addToTrace(e) { + return this.#e(i.U2, e); + } + setCurrentRouteName(e) { + return this.#e(i.PA, e); + } + interaction(e) { + return this.#e(i.dT, e); + } + wrapLogger(e, t, r) { + return this.#e(i.Wb, e, t, r); + } + measure(e, t) { + return this.#e(i.V1, e, t); + } + } + }, + 1863: (e, t, r) => { + 'use strict'; + function n() { + return Math.floor(performance.now()); + } + r.d(t, { t: () => n }); + }, + 1910: (e, t, r) => { + 'use strict'; + r.d(t, { i: () => o }); + var n = r(944); + const i = new Map(); + function o(...e) { + return e.every((e) => { + if (i.has(e)) return i.get(e); + const t = 'function' == typeof e && e.toString().includes('[native code]'); + return t || (0, n.R)(64, e?.name || e?.toString()), i.set(e, t), t; + }); + } + }, + 2555: (e, t, r) => { + 'use strict'; + r.d(t, { D: () => s, f: () => a }); + var n = r(384), + i = r(8122); + const o = { + beacon: n.NT.beacon, + errorBeacon: n.NT.errorBeacon, + licenseKey: void 0, + applicationID: void 0, + sa: void 0, + queueTime: void 0, + applicationTime: void 0, + ttGuid: void 0, + user: void 0, + account: void 0, + product: void 0, + extra: void 0, + jsAttributes: {}, + userAttributes: void 0, + atts: void 0, + transactionName: void 0, + tNamePlain: void 0, + }; + function a(e) { + try { + return !!e.licenseKey && !!e.errorBeacon && !!e.applicationID; + } catch (e) { + return !1; + } + } + const s = (e) => (0, i.a)(e, o); + }, + 2614: (e, t, r) => { + 'use strict'; + r.d(t, { BB: () => a, H3: () => n, g: () => u, iL: () => c, tS: () => s, uh: () => i, wk: () => o }); + const n = 'NRBA', + i = 'SESSION', + o = 144e5, + a = 18e5, + s = { + STARTED: 'session-started', + PAUSE: 'session-pause', + RESET: 'session-reset', + RESUME: 'session-resume', + UPDATE: 'session-update', + }, + c = { SAME_TAB: 'same-tab', CROSS_TAB: 'cross-tab' }, + u = { OFF: 0, FULL: 1, ERROR: 2 }; + }, + 2646: (e, t, r) => { + 'use strict'; + r.d(t, { y: () => n }); + class n { + constructor(e) { + this.contextId = e; + } + } + }, + 2843: (e, t, r) => { + 'use strict'; + r.d(t, { u: () => i }); + var n = r(3878); + function i(e, t = !1, r, i) { + (0, n.DD)( + 'visibilitychange', + function () { + if (t) return void ('hidden' === document.visibilityState && e()); + e(document.visibilityState); + }, + r, + i + ); + } + }, + 3241: (e, t, r) => { + 'use strict'; + r.d(t, { W: () => o }); + var n = r(6154); + const i = 'newrelic'; + function o(e = {}) { + try { + n.gm.dispatchEvent(new CustomEvent(i, { detail: e })); + } catch (e) {} + } + }, + 3304: (e, t, r) => { + 'use strict'; + r.d(t, { A: () => o }); + var n = r(7836); + const i = () => { + const e = new WeakSet(); + return (t, r) => { + if ('object' == typeof r && null !== r) { + if (e.has(r)) return; + e.add(r); + } + return r; + }; + }; + function o(e) { + try { + return JSON.stringify(e, i()) ?? ''; + } catch (e) { + try { + n.ee.emit('internal-error', [e]); + } catch (e) {} + return ''; + } + } + }, + 3333: (e, t, r) => { + 'use strict'; + r.d(t, { + $v: () => d, + TZ: () => n, + Xh: () => c, + Zp: () => i, + kd: () => u, + mq: () => s, + nf: () => a, + qN: () => o, + }); + const n = r(860).K7.genericEvents, + i = ['auxclick', 'click', 'copy', 'keydown', 'paste', 'scrollend'], + o = ['focus', 'blur'], + a = 4, + s = 1e3, + c = 2e3, + u = ['PageAction', 'UserAction', 'BrowserPerformance'], + d = { RESOURCES: 'experimental.resources' }; + }, + 3434: (e, t, r) => { + 'use strict'; + r.d(t, { Jt: () => o, YM: () => u }); + var n = r(7836), + i = r(5607); + const o = 'nr@original:'.concat(i.W), + a = 50; + var s = Object.prototype.hasOwnProperty, + c = !1; + function u(e, t) { + return ( + e || (e = n.ee), + (r.inPlace = function (e, t, n, i, o) { + n || (n = ''); + const a = '-' === n.charAt(0); + for (let s = 0; s < t.length; s++) { + const c = t[s], + u = e[c]; + l(u) || (e[c] = r(u, a ? c + n : n, i, c, o)); + } + }), + (r.flag = o), + r + ); + function r(t, r, n, c, u) { + return l(t) + ? t + : (r || (r = ''), + (nrWrapper[o] = t), + (function (e, t, r) { + if (Object.defineProperty && Object.keys) + try { + return ( + Object.keys(e).forEach(function (r) { + Object.defineProperty(t, r, { + get: function () { + return e[r]; + }, + set: function (t) { + return (e[r] = t), t; + }, + }); + }), + t + ); + } catch (e) { + d([e], r); + } + for (var n in e) s.call(e, n) && (t[n] = e[n]); + })(t, nrWrapper, e), + nrWrapper); + function nrWrapper() { + var o, s, l, f; + let h; + try { + (s = this), (o = [...arguments]), (l = 'function' == typeof n ? n(o, s) : n || {}); + } catch (t) { + d([t, '', [o, s, c], l], e); + } + i(r + 'start', [o, s, c], l, u); + const p = performance.now(); + let g; + try { + return (f = t.apply(s, o)), (g = performance.now()), f; + } catch (e) { + throw ((g = performance.now()), i(r + 'err', [o, s, e], l, u), (h = e), h); + } finally { + const e = g - p, + t = { start: p, end: g, duration: e, isLongTask: e >= a, methodName: c, thrownError: h }; + t.isLongTask && i('long-task', [t, s], l, u), i(r + 'end', [o, s, f], l, u); + } + } + } + function i(r, n, i, o) { + if (!c || t) { + var a = c; + c = !0; + try { + e.emit(r, n, i, t, o); + } catch (t) { + d([t, r, n, i], e); + } + c = a; + } + } + } + function d(e, t) { + t || (t = n.ee); + try { + t.emit('internal-error', e); + } catch (e) {} + } + function l(e) { + return !(e && 'function' == typeof e && e.apply && !e[o]); + } + }, + 3496: (e, t, r) => { + 'use strict'; + function n(e) { + return !e || !(!e.licenseKey || !e.applicationID); + } + function i(e, t) { + return !e || (e.licenseKey === t.info.licenseKey && e.applicationID === t.info.applicationID); + } + r.d(t, { A: () => i, I: () => n }); + }, + 3606: (e, t, r) => { + 'use strict'; + r.d(t, { i: () => o }); + var n = r(9908); + o.on = a; + var i = (o.handlers = {}); + function o(e, t, r, o) { + a(o || n.d, i, e, t, r); + } + function a(e, t, r, i, o) { + o || (o = 'feature'), e || (e = n.d); + var a = (t[o] = t[o] || {}); + (a[r] = a[r] || []).push([e, i]); + } + }, + 3738: (e, t, r) => { + 'use strict'; + r.d(t, { + He: () => i, + Kp: () => s, + Lc: () => u, + Rz: () => d, + TZ: () => n, + bD: () => o, + d3: () => a, + jx: () => l, + sl: () => f, + uP: () => c, + }); + const n = r(860).K7.sessionTrace, + i = 'bstResource', + o = 'resource', + a = '-start', + s = '-end', + c = 'fn' + a, + u = 'fn' + s, + d = 'pushState', + l = 1e3, + f = 3e4; + }, + 3785: (e, t, r) => { + 'use strict'; + r.d(t, { R: () => c, b: () => u }); + var n = r(9908), + i = r(1863), + o = r(860), + a = r(8154), + s = r(993); + function c(e, t, r = {}, c = s.p_.INFO, u, d = (0, i.t)()) { + (0, n.p)(a.xV, ['API/logging/'.concat(c.toLowerCase(), '/called')], void 0, o.K7.metrics, e), + (0, n.p)(s.ET, [d, t, r, c, u], void 0, o.K7.logging, e); + } + function u(e) { + return 'string' == typeof e && Object.values(s.p_).some((t) => t === e.toUpperCase().trim()); + } + }, + 3878: (e, t, r) => { + 'use strict'; + function n(e, t) { + return { capture: e, passive: !1, signal: t }; + } + function i(e, t, r = !1, i) { + window.addEventListener(e, t, n(r, i)); + } + function o(e, t, r = !1, i) { + document.addEventListener(e, t, n(r, i)); + } + r.d(t, { DD: () => o, jT: () => n, sp: () => i }); + }, + 3962: (e, t, r) => { + 'use strict'; + r.d(t, { + AM: () => a, + O2: () => l, + OV: () => o, + Qu: () => f, + TZ: () => c, + ih: () => h, + pP: () => s, + t1: () => d, + tC: () => i, + wD: () => u, + }); + var n = r(860); + const i = ['click', 'keydown', 'submit'], + o = 'popstate', + a = 'api', + s = 'initialPageLoad', + c = n.K7.softNav, + u = 5e3, + d = 500, + l = { INITIAL_PAGE_LOAD: '', ROUTE_CHANGE: 1, UNSPECIFIED: 2 }, + f = { INTERACTION: 1, AJAX: 2, CUSTOM_END: 3, CUSTOM_TRACER: 4 }, + h = { IP: 'in progress', PF: 'pending finish', FIN: 'finished', CAN: 'cancelled' }; + }, + 4234: (e, t, r) => { + 'use strict'; + r.d(t, { W: () => o }); + var n = r(7836), + i = r(1687); + class o { + constructor(e, t) { + (this.agentIdentifier = e), (this.ee = n.ee.get(e)), (this.featureName = t), (this.blocked = !1); + } + deregisterDrain() { + (0, i.x3)(this.agentIdentifier, this.featureName); + } + } + }, + 4261: (e, t, r) => { + 'use strict'; + r.d(t, { + $9: () => d, + BL: () => c, + CH: () => p, + Dl: () => R, + Fw: () => w, + PA: () => v, + Pl: () => n, + Tb: () => f, + U2: () => a, + V1: () => E, + Wb: () => T, + bt: () => y, + cD: () => b, + d3: () => x, + dT: () => u, + eY: () => g, + fF: () => h, + hG: () => o, + hw: () => i, + k6: () => s, + nb: () => m, + o5: () => l, + }); + const n = 'api-', + i = n + 'ixn-', + o = 'addPageAction', + a = 'addToTrace', + s = 'addRelease', + c = 'finished', + u = 'interaction', + d = 'log', + l = 'noticeError', + f = 'pauseReplay', + h = 'recordCustomEvent', + p = 'recordReplay', + g = 'register', + m = 'setApplicationVersion', + v = 'setCurrentRouteName', + b = 'setCustomAttribute', + y = 'setErrorHandler', + w = 'setPageViewName', + R = 'setUserId', + x = 'start', + T = 'wrapLogger', + E = 'measure'; + }, + 5205: (e, t, r) => { + 'use strict'; + r.d(t, { j: () => O }); + var n = r(384), + i = r(1741); + var o = r(2555), + a = r(3333); + const s = (e) => { + if (!e || 'string' != typeof e) return !1; + try { + document.createDocumentFragment().querySelector(e); + } catch { + return !1; + } + return !0; + }; + var c = r(2614), + u = r(944), + d = r(8122); + const l = '[data-nr-mask]', + f = (e) => + (0, d.a)( + e, + (() => { + const e = { + feature_flags: [], + experimental: { resources: !1 }, + mask_selector: '*', + block_selector: '[data-nr-block]', + mask_input_options: { + color: !1, + date: !1, + 'datetime-local': !1, + email: !1, + month: !1, + number: !1, + range: !1, + search: !1, + tel: !1, + text: !1, + time: !1, + url: !1, + week: !1, + textarea: !1, + select: !1, + password: !0, + }, + }; + return { + ajax: { deny_list: void 0, block_internal: !0, enabled: !0, autoStart: !0 }, + api: { allow_registered_children: !0, duplicate_registered_data: !1 }, + distributed_tracing: { + enabled: void 0, + exclude_newrelic_header: void 0, + cors_use_newrelic_header: void 0, + cors_use_tracecontext_headers: void 0, + allowed_origins: void 0, + }, + get feature_flags() { + return e.feature_flags; + }, + set feature_flags(t) { + e.feature_flags = t; + }, + generic_events: { enabled: !0, autoStart: !0 }, + harvest: { interval: 30 }, + jserrors: { enabled: !0, autoStart: !0 }, + logging: { enabled: !0, autoStart: !0 }, + metrics: { enabled: !0, autoStart: !0 }, + obfuscate: void 0, + page_action: { enabled: !0 }, + page_view_event: { enabled: !0, autoStart: !0 }, + page_view_timing: { enabled: !0, autoStart: !0 }, + performance: { + capture_marks: !1, + capture_measures: !1, + capture_detail: !0, + resources: { + get enabled() { + return e.feature_flags.includes(a.$v.RESOURCES) || e.experimental.resources; + }, + set enabled(t) { + e.experimental.resources = t; + }, + asset_types: [], + first_party_domains: [], + ignore_newrelic: !0, + }, + }, + privacy: { cookies_enabled: !0 }, + proxy: { assets: void 0, beacon: void 0 }, + session: { expiresMs: c.wk, inactiveMs: c.BB }, + session_replay: { + autoStart: !0, + enabled: !1, + preload: !1, + sampling_rate: 10, + error_sampling_rate: 100, + collect_fonts: !1, + inline_images: !1, + fix_stylesheets: !0, + mask_all_inputs: !0, + get mask_text_selector() { + return e.mask_selector; + }, + set mask_text_selector(t) { + s(t) + ? (e.mask_selector = ''.concat(t, ',').concat(l)) + : '' === t || null === t + ? (e.mask_selector = l) + : (0, u.R)(5, t); + }, + get block_class() { + return 'nr-block'; + }, + get ignore_class() { + return 'nr-ignore'; + }, + get mask_text_class() { + return 'nr-mask'; + }, + get block_selector() { + return e.block_selector; + }, + set block_selector(t) { + s(t) ? (e.block_selector += ','.concat(t)) : '' !== t && (0, u.R)(6, t); + }, + get mask_input_options() { + return e.mask_input_options; + }, + set mask_input_options(t) { + t && 'object' == typeof t ? (e.mask_input_options = { ...t, password: !0 }) : (0, u.R)(7, t); + }, + }, + session_trace: { enabled: !0, autoStart: !0 }, + soft_navigations: { enabled: !0, autoStart: !0 }, + spa: { enabled: !0, autoStart: !0 }, + ssl: void 0, + user_actions: { enabled: !0, elementAttributes: ['id', 'className', 'tagName', 'type'] }, + }; + })() + ); + var h = r(6154), + p = r(9324); + let g = 0; + const m = { buildEnv: p.F3, distMethod: p.Xs, version: p.xv, originTime: h.WN }, + v = { + appMetadata: {}, + customTransaction: void 0, + denyList: void 0, + disabled: !1, + entityManager: void 0, + harvester: void 0, + isolatedBacklog: !1, + isRecording: !1, + loaderType: void 0, + maxBytes: 3e4, + obfuscator: void 0, + onerror: void 0, + ptid: void 0, + releaseIds: {}, + session: void 0, + timeKeeper: void 0, + jsAttributesMetadata: { bytes: 0 }, + get harvestCount() { + return ++g; + }, + }, + b = (e) => { + const t = (0, d.a)(e, v), + r = Object.keys(m).reduce( + (e, t) => ((e[t] = { value: m[t], writable: !1, configurable: !0, enumerable: !0 }), e), + {} + ); + return Object.defineProperties(t, r); + }; + var y = r(5701); + const w = (e) => { + const t = e.startsWith('http'); + (e += '/'), (r.p = t ? e : 'https://' + e); + }; + var R = r(7836), + x = r(3241); + const T = { + accountID: void 0, + trustKey: void 0, + agentID: void 0, + licenseKey: void 0, + applicationID: void 0, + xpid: void 0, + }, + E = (e) => (0, d.a)(e, T), + A = new Set(); + function O(e, t = {}, r, a) { + let { init: s, info: c, loader_config: u, runtime: d = {}, exposed: l = !0 } = t; + if (!c) { + const e = (0, n.pV)(); + (s = e.init), (c = e.info), (u = e.loader_config); + } + (e.init = f(s || {})), + (e.loader_config = E(u || {})), + (c.jsAttributes ??= {}), + h.bv && (c.jsAttributes.isWorker = !0), + (e.info = (0, o.D)(c)); + const p = e.init, + g = [c.beacon, c.errorBeacon]; + A.has(e.agentIdentifier) || + (p.proxy.assets && (w(p.proxy.assets), g.push(p.proxy.assets)), + p.proxy.beacon && g.push(p.proxy.beacon), + (e.beacons = [...g]), + (function (e) { + const t = (0, n.pV)(); + Object.getOwnPropertyNames(i.W.prototype).forEach((r) => { + const n = i.W.prototype[r]; + if ('function' != typeof n || 'constructor' === n) return; + let o = t[r]; + e[r] && + !1 !== e.exposed && + 'micro-agent' !== e.runtime?.loaderType && + (t[r] = (...t) => { + const n = e[r](...t); + return o ? o(...t) : n; + }); + }); + })(e), + (0, n.US)('activatedFeatures', y.B), + (e.runSoftNavOverSpa &&= !0 === p.soft_navigations.enabled && p.feature_flags.includes('soft_nav'))), + (d.denyList = [...(p.ajax.deny_list || []), ...(p.ajax.block_internal ? g : [])]), + (d.ptid = e.agentIdentifier), + (d.loaderType = r), + (e.runtime = b(d)), + A.has(e.agentIdentifier) || + ((e.ee = R.ee.get(e.agentIdentifier)), + (e.exposed = l), + (0, x.W)({ + agentIdentifier: e.agentIdentifier, + drained: !!y.B?.[e.agentIdentifier], + type: 'lifecycle', + name: 'initialize', + feature: void 0, + data: e.config, + })), + A.add(e.agentIdentifier); + } + }, + 5270: (e, t, r) => { + 'use strict'; + r.d(t, { Aw: () => a, SR: () => o, rF: () => s }); + var n = r(384), + i = r(7767); + function o(e) { + return !!(0, n.dV)().o.MO && (0, i.V)(e) && !0 === e?.session_trace.enabled; + } + function a(e) { + return !0 === e?.session_replay.preload && o(e); + } + function s(e, t) { + try { + if ('string' == typeof t?.type) { + if ('password' === t.type.toLowerCase()) return '*'.repeat(e?.length || 0); + if (void 0 !== t?.dataset?.nrUnmask || t?.classList?.contains('nr-unmask')) return e; + } + } catch (e) {} + return 'string' == typeof e ? e.replace(/[\S]/g, '*') : '*'.repeat(e?.length || 0); + } + }, + 5289: (e, t, r) => { + 'use strict'; + r.d(t, { GG: () => o, Qr: () => s, sB: () => a }); + var n = r(3878); + function i() { + return 'undefined' == typeof document || 'complete' === document.readyState; + } + function o(e, t) { + if (i()) return e(); + (0, n.sp)('load', e, t); + } + function a(e) { + if (i()) return e(); + (0, n.DD)('DOMContentLoaded', e); + } + function s(e) { + if (i()) return e(); + (0, n.sp)('popstate', e); + } + }, + 5607: (e, t, r) => { + 'use strict'; + r.d(t, { W: () => n }); + const n = (0, r(9566).bz)(); + }, + 5701: (e, t, r) => { + 'use strict'; + r.d(t, { B: () => o, t: () => a }); + var n = r(3241); + const i = new Set(), + o = {}; + function a(e, t) { + const r = t.agentIdentifier; + (o[r] ??= {}), + e && + 'object' == typeof e && + (i.has(r) || + (t.ee.emit('rumresp', [e]), + (o[r] = e), + i.add(r), + (0, n.W)({ + agentIdentifier: r, + loaded: !0, + drained: !0, + type: 'lifecycle', + name: 'load', + feature: void 0, + data: e, + }))); + } + }, + 6154: (e, t, r) => { + 'use strict'; + r.d(t, { + A4: () => s, + OF: () => d, + RI: () => i, + WN: () => h, + bv: () => o, + gm: () => a, + lR: () => f, + m: () => u, + mw: () => c, + sb: () => l, + }); + var n = r(1863); + const i = 'undefined' != typeof window && !!window.document, + o = + 'undefined' != typeof WorkerGlobalScope && + (('undefined' != typeof self && + self instanceof WorkerGlobalScope && + self.navigator instanceof WorkerNavigator) || + ('undefined' != typeof globalThis && + globalThis instanceof WorkerGlobalScope && + globalThis.navigator instanceof WorkerNavigator)), + a = i + ? window + : 'undefined' != typeof WorkerGlobalScope && + (('undefined' != typeof self && self instanceof WorkerGlobalScope && self) || + ('undefined' != typeof globalThis && globalThis instanceof WorkerGlobalScope && globalThis)), + s = 'complete' === a?.document?.readyState, + c = Boolean('hidden' === a?.document?.visibilityState), + u = '' + a?.location, + d = /iPad|iPhone|iPod/.test(a.navigator?.userAgent), + l = d && 'undefined' == typeof SharedWorker, + f = (() => { + const e = a.navigator?.userAgent?.match(/Firefox[/\s](\d+\.\d+)/); + return Array.isArray(e) && e.length >= 2 ? +e[1] : 0; + })(), + h = Date.now() - (0, n.t)(); + }, + 6344: (e, t, r) => { + 'use strict'; + r.d(t, { + BB: () => d, + G4: () => o, + Qb: () => l, + TZ: () => i, + Ug: () => a, + _s: () => s, + bc: () => u, + yP: () => c, + }); + var n = r(2614); + const i = r(860).K7.sessionReplay, + o = { RECORD: 'recordReplay', PAUSE: 'pauseReplay', ERROR_DURING_REPLAY: 'errorDuringReplay' }, + a = 0.12, + s = { DomContentLoaded: 0, Load: 1, FullSnapshot: 2, IncrementalSnapshot: 3, Meta: 4, Custom: 5 }, + c = { [n.g.ERROR]: 15e3, [n.g.FULL]: 3e5, [n.g.OFF]: 0 }, + u = { + RESET: { message: 'Session was reset', sm: 'Reset' }, + IMPORT: { message: 'Recorder failed to import', sm: 'Import' }, + TOO_MANY: { message: '429: Too Many Requests', sm: 'Too-Many' }, + TOO_BIG: { message: 'Payload was too large', sm: 'Too-Big' }, + CROSS_TAB: { message: 'Session Entity was set to OFF on another tab', sm: 'Cross-Tab' }, + ENTITLEMENTS: { message: 'Session Replay is not allowed and will not be started', sm: 'Entitlement' }, + }, + d = 5e3, + l = { + API: 'api', + RESUME: 'resume', + SWITCH_TO_FULL: 'switchToFull', + INITIALIZE: 'initialize', + PRELOAD: 'preload', + }; + }, + 6389: (e, t, r) => { + 'use strict'; + function n(e, t = 500, r = {}) { + const n = r?.leading || !1; + let i; + return (...r) => { + n && + void 0 === i && + (e.apply(this, r), + (i = setTimeout(() => { + i = clearTimeout(i); + }, t))), + n || + (clearTimeout(i), + (i = setTimeout(() => { + e.apply(this, r); + }, t))); + }; + } + function i(e) { + let t = !1; + return (...r) => { + t || ((t = !0), e.apply(this, r)); + }; + } + r.d(t, { J: () => i, s: () => n }); + }, + 6630: (e, t, r) => { + 'use strict'; + r.d(t, { T: () => n }); + const n = r(860).K7.pageViewEvent; + }, + 6774: (e, t, r) => { + 'use strict'; + r.d(t, { T: () => n }); + const n = r(860).K7.jserrors; + }, + 7295: (e, t, r) => { + 'use strict'; + r.d(t, { Xv: () => a, gX: () => i, iW: () => o }); + var n = []; + function i(e) { + if (!e || o(e)) return !1; + if (0 === n.length) return !0; + for (var t = 0; t < n.length; t++) { + var r = n[t]; + if ('*' === r.hostname) return !1; + if (s(r.hostname, e.hostname) && c(r.pathname, e.pathname)) return !1; + } + return !0; + } + function o(e) { + return void 0 === e.hostname; + } + function a(e) { + if (((n = []), e && e.length)) + for (var t = 0; t < e.length; t++) { + let r = e[t]; + if (!r) continue; + 0 === r.indexOf('http://') ? (r = r.substring(7)) : 0 === r.indexOf('https://') && (r = r.substring(8)); + const i = r.indexOf('/'); + let o, a; + i > 0 ? ((o = r.substring(0, i)), (a = r.substring(i))) : ((o = r), (a = '')); + let [s] = o.split(':'); + n.push({ hostname: s, pathname: a }); + } + } + function s(e, t) { + return !(e.length > t.length) && t.indexOf(e) === t.length - e.length; + } + function c(e, t) { + return ( + 0 === e.indexOf('/') && (e = e.substring(1)), + 0 === t.indexOf('/') && (t = t.substring(1)), + '' === e || e === t + ); + } + }, + 7378: (e, t, r) => { + 'use strict'; + r.d(t, { + $p: () => x, + BR: () => b, + Kp: () => R, + L3: () => y, + Lc: () => c, + NC: () => o, + SG: () => d, + TZ: () => i, + U6: () => p, + UT: () => m, + d3: () => w, + dT: () => f, + e5: () => E, + gx: () => v, + l9: () => l, + oW: () => h, + op: () => g, + rw: () => u, + tH: () => A, + uP: () => s, + wW: () => T, + xq: () => a, + }); + var n = r(384); + const i = r(860).K7.spa, + o = ['click', 'submit', 'keypress', 'keydown', 'keyup', 'change'], + a = 999, + s = 'fn-start', + c = 'fn-end', + u = 'cb-start', + d = 'api-ixn-', + l = 'remaining', + f = 'interaction', + h = 'spaNode', + p = 'jsonpNode', + g = 'fetch-start', + m = 'fetch-done', + v = 'fetch-body-', + b = 'jsonp-end', + y = (0, n.dV)().o.ST, + w = '-start', + R = '-end', + x = '-body', + T = 'cb' + R, + E = 'jsTime', + A = 'fetch'; + }, + 7485: (e, t, r) => { + 'use strict'; + r.d(t, { D: () => i }); + var n = r(6154); + function i(e) { + if (0 === (e || '').indexOf('data:')) return { protocol: 'data' }; + try { + const t = new URL(e, location.href), + r = { + port: t.port, + hostname: t.hostname, + pathname: t.pathname, + search: t.search, + protocol: t.protocol.slice(0, t.protocol.indexOf(':')), + sameOrigin: t.protocol === n.gm?.location?.protocol && t.host === n.gm?.location?.host, + }; + return ( + (r.port && '' !== r.port) || + ('http:' === t.protocol && (r.port = '80'), 'https:' === t.protocol && (r.port = '443')), + r.pathname && '' !== r.pathname + ? r.pathname.startsWith('/') || (r.pathname = '/'.concat(r.pathname)) + : (r.pathname = '/'), + r + ); + } catch (e) { + return {}; + } + } + }, + 7699: (e, t, r) => { + 'use strict'; + r.d(t, { It: () => i, No: () => n, qh: () => a, uh: () => o }); + const n = 16e3, + i = 1e6, + o = 'NR_CONTAINER_AGENT', + a = 'SESSION_ERROR'; + }, + 7767: (e, t, r) => { + 'use strict'; + r.d(t, { V: () => i }); + var n = r(6154); + const i = (e) => n.RI && !0 === e?.privacy.cookies_enabled; + }, + 7836: (e, t, r) => { + 'use strict'; + r.d(t, { P: () => s, ee: () => c }); + var n = r(384), + i = r(8990), + o = r(2646), + a = r(5607); + const s = 'nr@context:'.concat(a.W), + c = (function e(t, r) { + var n = {}, + a = {}, + d = {}, + l = !1; + try { + l = 16 === r.length && u.initializedAgents?.[r]?.runtime.isolatedBacklog; + } catch (e) {} + var f = { + on: p, + addEventListener: p, + removeEventListener: function (e, t) { + var r = n[e]; + if (!r) return; + for (var i = 0; i < r.length; i++) r[i] === t && r.splice(i, 1); + }, + emit: function (e, r, n, i, o) { + !1 !== o && (o = !0); + if (c.aborted && !i) return; + t && o && t.emit(e, r, n); + var s = h(n); + g(e).forEach((e) => { + e.apply(s, r); + }); + var u = v()[a[e]]; + u && u.push([f, e, r, s]); + return s; + }, + get: m, + listeners: g, + context: h, + buffer: function (e, t) { + const r = v(); + if (((t = t || 'feature'), f.aborted)) return; + Object.entries(e || {}).forEach(([e, n]) => { + (a[n] = t), t in r || (r[t] = []); + }); + }, + abort: function () { + (f._aborted = !0), + Object.keys(f.backlog).forEach((e) => { + delete f.backlog[e]; + }); + }, + isBuffering: function (e) { + return !!v()[a[e]]; + }, + debugId: r, + backlog: l ? {} : t && 'object' == typeof t.backlog ? t.backlog : {}, + isolatedBacklog: l, + }; + return ( + Object.defineProperty(f, 'aborted', { + get: () => { + let e = f._aborted || !1; + return e || (t && (e = t.aborted), e); + }, + }), + f + ); + function h(e) { + return e && e instanceof o.y ? e : e ? (0, i.I)(e, s, () => new o.y(s)) : new o.y(s); + } + function p(e, t) { + n[e] = g(e).concat(t); + } + function g(e) { + return n[e] || []; + } + function m(t) { + return (d[t] = d[t] || e(f, t)); + } + function v() { + return f.backlog; + } + })(void 0, 'globalEE'), + u = (0, n.Zm)(); + u.ee || (u.ee = c); + }, + 8122: (e, t, r) => { + 'use strict'; + r.d(t, { a: () => i }); + var n = r(944); + function i(e, t) { + try { + if (!e || 'object' != typeof e) return (0, n.R)(3); + if (!t || 'object' != typeof t) return (0, n.R)(4); + const r = Object.create(Object.getPrototypeOf(t), Object.getOwnPropertyDescriptors(t)), + o = 0 === Object.keys(r).length ? e : r; + for (let a in o) + if (void 0 !== e[a]) + try { + if (null === e[a]) { + r[a] = null; + continue; + } + Array.isArray(e[a]) && Array.isArray(t[a]) + ? (r[a] = Array.from(new Set([...e[a], ...t[a]]))) + : 'object' == typeof e[a] && 'object' == typeof t[a] + ? (r[a] = i(e[a], t[a])) + : (r[a] = e[a]); + } catch (e) { + r[a] || (0, n.R)(1, e); + } + return r; + } catch (e) { + (0, n.R)(2, e); + } + } + }, + 8139: (e, t, r) => { + 'use strict'; + r.d(t, { u: () => f }); + var n = r(7836), + i = r(3434), + o = r(8990), + a = r(6154); + const s = {}, + c = a.gm.XMLHttpRequest, + u = 'addEventListener', + d = 'removeEventListener', + l = 'nr@wrapped:'.concat(n.P); + function f(e) { + var t = (function (e) { + return (e || n.ee).get('events'); + })(e); + if (s[t.debugId]++) return t; + s[t.debugId] = 1; + var r = (0, i.YM)(t, !0); + function f(e) { + r.inPlace(e, [u, d], '-', p); + } + function p(e, t) { + return e[1]; + } + return ( + 'getPrototypeOf' in Object && (a.RI && h(document, f), c && h(c.prototype, f), h(a.gm, f)), + t.on(u + '-start', function (e, t) { + var n = e[1]; + if (null !== n && ('function' == typeof n || 'object' == typeof n) && 'newrelic' !== e[0]) { + var i = (0, o.I)(n, l, function () { + var e = { + object: function () { + if ('function' != typeof n.handleEvent) return; + return n.handleEvent.apply(n, arguments); + }, + function: n, + }[typeof n]; + return e ? r(e, 'fn-', null, e.name || 'anonymous') : n; + }); + this.wrapped = e[1] = i; + } + }), + t.on(d + '-start', function (e) { + e[1] = this.wrapped || e[1]; + }), + t + ); + } + function h(e, t, ...r) { + let n = e; + for (; 'object' == typeof n && !Object.prototype.hasOwnProperty.call(n, u); ) n = Object.getPrototypeOf(n); + n && t(n, ...r); + } + }, + 8154: (e, t, r) => { + 'use strict'; + r.d(t, { z_: () => o, XG: () => s, TZ: () => n, rs: () => i, xV: () => a }); + r(6154), r(9566), r(384); + const n = r(860).K7.metrics, + i = 'sm', + o = 'cm', + a = 'storeSupportabilityMetrics', + s = 'storeEventMetrics'; + }, + 8374: (e, t, r) => { + r.nc = (() => { + try { + return document?.currentScript?.nonce; + } catch (e) {} + return ''; + })(); + }, + 8990: (e, t, r) => { + 'use strict'; + r.d(t, { I: () => i }); + var n = Object.prototype.hasOwnProperty; + function i(e, t, r) { + if (n.call(e, t)) return e[t]; + var i = r(); + if (Object.defineProperty && Object.keys) + try { + return Object.defineProperty(e, t, { value: i, writable: !0, enumerable: !1 }), i; + } catch (e) {} + return (e[t] = i), i; + } + }, + 9300: (e, t, r) => { + 'use strict'; + r.d(t, { T: () => n }); + const n = r(860).K7.ajax; + }, + 9324: (e, t, r) => { + 'use strict'; + r.d(t, { AJ: () => a, F3: () => i, Xs: () => o, Yq: () => s, xv: () => n }); + const n = '1.301.0', + i = 'PROD', + o = 'CDN', + a = '@newrelic/rrweb', + s = '1.0.1'; + }, + 9566: (e, t, r) => { + 'use strict'; + r.d(t, { LA: () => s, ZF: () => c, bz: () => a, el: () => u }); + var n = r(6154); + const i = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'; + function o(e, t) { + return e ? 15 & e[t] : (16 * Math.random()) | 0; + } + function a() { + const e = n.gm?.crypto || n.gm?.msCrypto; + let t, + r = 0; + return ( + e && e.getRandomValues && (t = e.getRandomValues(new Uint8Array(30))), + i + .split('') + .map((e) => ('x' === e ? o(t, r++).toString(16) : 'y' === e ? ((3 & o()) | 8).toString(16) : e)) + .join('') + ); + } + function s(e) { + const t = n.gm?.crypto || n.gm?.msCrypto; + let r, + i = 0; + t && t.getRandomValues && (r = t.getRandomValues(new Uint8Array(e))); + const a = []; + for (var s = 0; s < e; s++) a.push(o(r, i++).toString(16)); + return a.join(''); + } + function c() { + return s(16); + } + function u() { + return s(32); + } + }, + 9908: (e, t, r) => { + 'use strict'; + r.d(t, { d: () => n, p: () => i }); + var n = r(7836).ee.get('handle'); + function i(e, t, r, i, o) { + o ? (o.buffer([e], i), o.emit(e, t, r)) : (n.buffer([e], i), n.emit(e, t, r)); + } + }, + }, + n = {}; + function i(e) { + var t = n[e]; + if (void 0 !== t) return t.exports; + var o = (n[e] = { exports: {} }); + return r[e](o, o.exports, i), o.exports; + } + (i.m = r), + (i.d = (e, t) => { + for (var r in t) i.o(t, r) && !i.o(e, r) && Object.defineProperty(e, r, { enumerable: !0, get: t[r] }); + }), + (i.f = {}), + (i.e = (e) => Promise.all(Object.keys(i.f).reduce((t, r) => (i.f[r](e, t), t), []))), + (i.u = (e) => ({ 212: 'nr-spa-compressor', 249: 'nr-spa-recorder', 478: 'nr-spa' })[e] + '-1.301.0.min.js'), + (i.o = (e, t) => Object.prototype.hasOwnProperty.call(e, t)), + (e = {}), + (t = 'NRBA-1.301.0.PROD:'), + (i.l = (r, n, o, a) => { + if (e[r]) e[r].push(n); + else { + var s, c; + if (void 0 !== o) + for (var u = document.getElementsByTagName('script'), d = 0; d < u.length; d++) { + var l = u[d]; + if (l.getAttribute('src') == r || l.getAttribute('data-webpack') == t + o) { + s = l; + break; + } + } + if (!s) { + c = !0; + var f = { + 478: 'sha512-7qHClSVBtoyiwRvCkgyaF5Ps0RAENoPwjauK0I+0bYyBYefdZVshGSe8JQWh/Wexd7hFuUui5xp+2xn5U1a3ZA==', + 249: 'sha512-KuEP0gQ0mAldYT/AqNp3NW4kVb0kLtfIlXaPSFq4WQRFf8vKVNnSqiafso/bWR75halLwFsgmJtdLJEzntVZoQ==', + 212: 'sha512-fqWEILwVJyfYV9/SedvSjCZ6hDRNjOvwYfN73wxZtahaztcFZ2cr3Ns172tBGIDQeWO25QmSlihZm+awv8ma/w==', + }; + ((s = document.createElement('script')).charset = 'utf-8'), + i.nc && s.setAttribute('nonce', i.nc), + s.setAttribute('data-webpack', t + o), + (s.src = r), + 0 !== s.src.indexOf(window.location.origin + '/') && (s.crossOrigin = 'anonymous'), + f[a] && (s.integrity = f[a]); + } + e[r] = [n]; + var h = (t, n) => { + (s.onerror = s.onload = null), clearTimeout(p); + var i = e[r]; + if ((delete e[r], s.parentNode && s.parentNode.removeChild(s), i && i.forEach((e) => e(n)), t)) return t(n); + }, + p = setTimeout(h.bind(null, void 0, { type: 'timeout', target: s }), 12e4); + (s.onerror = h.bind(null, s.onerror)), (s.onload = h.bind(null, s.onload)), c && document.head.appendChild(s); + } + }), + (i.r = (e) => { + 'undefined' != typeof Symbol && + Symbol.toStringTag && + Object.defineProperty(e, Symbol.toStringTag, { value: 'Module' }), + Object.defineProperty(e, '__esModule', { value: !0 }); + }), + (i.p = 'https://js-agent.newrelic.com/'), + (() => { + var e = { 38: 0, 788: 0 }; + i.f.j = (t, r) => { + var n = i.o(e, t) ? e[t] : void 0; + if (0 !== n) + if (n) r.push(n[2]); + else { + var o = new Promise((r, i) => (n = e[t] = [r, i])); + r.push((n[2] = o)); + var a = i.p + i.u(t), + s = new Error(); + i.l( + a, + (r) => { + if (i.o(e, t) && (0 !== (n = e[t]) && (e[t] = void 0), n)) { + var o = r && ('load' === r.type ? 'missing' : r.type), + a = r && r.target && r.target.src; + (s.message = 'Loading chunk ' + t + ' failed.\n(' + o + ': ' + a + ')'), + (s.name = 'ChunkLoadError'), + (s.type = o), + (s.request = a), + n[1](s); + } + }, + 'chunk-' + t, + t + ); + } + }; + var t = (t, r) => { + var n, + o, + [a, s, c] = r, + u = 0; + if (a.some((t) => 0 !== e[t])) { + for (n in s) i.o(s, n) && (i.m[n] = s[n]); + if (c) c(i); + } + for (t && t(r); u < a.length; u++) (o = a[u]), i.o(e, o) && e[o] && e[o][0](), (e[o] = 0); + }, + r = (self['webpackChunk:NRBA-1.301.0.PROD'] = self['webpackChunk:NRBA-1.301.0.PROD'] || []); + r.forEach(t.bind(null, 0)), (r.push = t.bind(null, r.push.bind(r))); + })(), + (() => { + 'use strict'; + i(8374); + var e = i(9566), + t = i(1741); + class r extends t.W { + agentIdentifier = (0, e.LA)(16); + } + var n = i(860); + const o = Object.values(n.K7); + var a = i(5205); + var s = i(9908), + c = i(1863), + u = i(4261), + d = i(3241), + l = i(944), + f = i(5701), + h = i(8154); + function p(e, t, i, o) { + const a = o || i; + !a || + (a[e] && a[e] !== r.prototype[e]) || + (a[e] = function () { + (0, s.p)(h.xV, ['API/' + e + '/called'], void 0, n.K7.metrics, i.ee), + (0, d.W)({ + agentIdentifier: i.agentIdentifier, + drained: !!f.B?.[i.agentIdentifier], + type: 'data', + name: 'api', + feature: u.Pl + e, + data: {}, + }); + try { + return t.apply(this, arguments); + } catch (e) { + (0, l.R)(23, e); + } + }); + } + function g(e, t, r, n, i) { + const o = e.info; + null === r ? delete o.jsAttributes[t] : (o.jsAttributes[t] = r), + (i || null === r) && (0, s.p)(u.Pl + n, [(0, c.t)(), t, r], void 0, 'session', e.ee); + } + var m = i(1687), + v = i(4234), + b = i(5289), + y = i(6154), + w = i(5270), + R = i(7767), + x = i(6389), + T = i(7699); + class E extends v.W { + constructor(e, t) { + super(e.agentIdentifier, t), + (this.agentRef = e), + (this.abortHandler = void 0), + (this.featAggregate = void 0), + (this.onAggregateImported = void 0), + (this.deferred = Promise.resolve()), + !1 === e.init[this.featureName].autoStart + ? (this.deferred = new Promise((t, r) => { + this.ee.on( + 'manual-start-all', + (0, x.J)(() => { + (0, m.Ak)(e.agentIdentifier, this.featureName), t(); + }) + ); + })) + : (0, m.Ak)(e.agentIdentifier, t); + } + importAggregator(e, t, r = {}) { + if (this.featAggregate) return; + let n; + this.onAggregateImported = new Promise((e) => { + n = e; + }); + const o = async () => { + let o; + await this.deferred; + try { + if ((0, R.V)(e.init)) { + const { setupAgentSession: t } = await i.e(478).then(i.bind(i, 8766)); + o = t(e); + } + } catch (e) { + (0, l.R)(20, e), + this.ee.emit('internal-error', [e]), + (0, s.p)(T.qh, [e], void 0, this.featureName, this.ee); + } + try { + if (!this.#t(this.featureName, o, e.init)) + return (0, m.Ze)(this.agentIdentifier, this.featureName), void n(!1); + const { Aggregate: i } = await t(); + (this.featAggregate = new i(e, r)), + e.runtime.harvester.initializedAggregates.push(this.featAggregate), + n(!0); + } catch (e) { + (0, l.R)(34, e), + this.abortHandler?.(), + (0, m.Ze)(this.agentIdentifier, this.featureName, !0), + n(!1), + this.ee && this.ee.abort(); + } + }; + y.RI ? (0, b.GG)(() => o(), !0) : o(); + } + #t(e, t, r) { + if (this.blocked) return !1; + switch (e) { + case n.K7.sessionReplay: + return (0, w.SR)(r) && !!t; + case n.K7.sessionTrace: + return !!t; + default: + return !0; + } + } + } + var A = i(6630), + O = i(2614); + class S extends E { + static featureName = A.T; + constructor(e) { + var t; + super(e, A.T), + this.setupInspectionEvents(e.agentIdentifier), + (t = e), + p( + u.Fw, + function (e, r) { + 'string' == typeof e && + ('/' !== e.charAt(0) && (e = '/' + e), + (t.runtime.customTransaction = (r || 'http://custom.transaction') + e), + (0, s.p)(u.Pl + u.Fw, [(0, c.t)()], void 0, void 0, t.ee)); + }, + t + ), + this.ee.on('api-send-rum', (e, t) => (0, s.p)('send-rum', [e, t], void 0, this.featureName, this.ee)), + this.importAggregator(e, () => i.e(478).then(i.bind(i, 1983))); + } + setupInspectionEvents(e) { + const t = (t, r) => { + t && + (0, d.W)({ + agentIdentifier: e, + timeStamp: t.timeStamp, + loaded: 'complete' === t.target.readyState, + type: 'window', + name: r, + data: t.target.location + '', + }); + }; + (0, b.sB)((e) => { + t(e, 'DOMContentLoaded'); + }), + (0, b.GG)((e) => { + t(e, 'load'); + }), + (0, b.Qr)((e) => { + t(e, 'navigate'); + }), + this.ee.on(O.tS.UPDATE, (t, r) => { + (0, d.W)({ agentIdentifier: e, type: 'lifecycle', name: 'session', data: r }); + }); + } + } + var N = i(384); + var _ = i(2843), + I = i(3878), + P = i(782); + class j extends E { + static featureName = P.T; + constructor(e) { + super(e, P.T), + y.RI && + ((0, _.u)(() => (0, s.p)('docHidden', [(0, c.t)()], void 0, P.T, this.ee), !0), + (0, I.sp)('pagehide', () => (0, s.p)('winPagehide', [(0, c.t)()], void 0, P.T, this.ee)), + this.importAggregator(e, () => i.e(478).then(i.bind(i, 9917)))); + } + } + class C extends E { + static featureName = h.TZ; + constructor(e) { + super(e, h.TZ), + y.RI && + document.addEventListener('securitypolicyviolation', (e) => { + (0, s.p)(h.xV, ['Generic/CSPViolation/Detected'], void 0, this.featureName, this.ee); + }), + this.importAggregator(e, () => i.e(478).then(i.bind(i, 6555))); + } + } + var k = i(6774), + L = i(3304); + class H { + constructor(e, t, r, n, i) { + (this.name = 'UncaughtError'), + (this.message = 'string' == typeof e ? e : (0, L.A)(e)), + (this.sourceURL = t), + (this.line = r), + (this.column = n), + (this.__newrelic = i); + } + } + function M(e) { + return U(e) + ? e + : new H( + void 0 !== e?.message ? e.message : e, + e?.filename || e?.sourceURL, + e?.lineno || e?.line, + e?.colno || e?.col, + e?.__newrelic, + e?.cause + ); + } + function D(e) { + const t = 'Unhandled Promise Rejection: '; + if (!e?.reason) return; + if (U(e.reason)) { + try { + e.reason.message.startsWith(t) || (e.reason.message = t + e.reason.message); + } catch (e) {} + return M(e.reason); + } + const r = M(e.reason); + return (r.message || '').startsWith(t) || (r.message = t + r.message), r; + } + function K(e) { + if (e.error instanceof SyntaxError && !/:\d+$/.test(e.error.stack?.trim())) { + const t = new H(e.message, e.filename, e.lineno, e.colno, e.error.__newrelic, e.cause); + return (t.name = SyntaxError.name), t; + } + return U(e.error) ? e.error : M(e); + } + function U(e) { + return e instanceof Error && !!e.stack; + } + function F(e, t, r, i, o = (0, c.t)()) { + 'string' == typeof e && (e = new Error(e)), + (0, s.p)('err', [e, o, !1, t, r.runtime.isRecording, void 0, i], void 0, n.K7.jserrors, r.ee), + (0, s.p)('uaErr', [], void 0, n.K7.genericEvents, r.ee); + } + var W = i(3496), + B = i(993), + G = i(3785); + function V(e, { customAttributes: t = {}, level: r = B.p_.INFO } = {}, n, i, o = (0, c.t)()) { + (0, G.R)(n.ee, e, t, r, i, o); + } + function z(e, t, r, i, o = (0, c.t)()) { + (0, s.p)(u.Pl + u.hG, [o, e, t, i], void 0, n.K7.genericEvents, r.ee); + } + function Z(e) { + p( + u.eY, + function (t) { + return (function (e, t) { + const r = {}; + let i, o; + (0, l.R)(54, 'newrelic.register'), e.init.api.allow_registered_children || (i = () => (0, l.R)(55)); + (t && (0, W.I)(t)) || (i = () => (0, l.R)(48, t)); + const a = { + addPageAction: (n, i = {}) => { + u(z, [n, { ...r, ...i }, e], t); + }, + log: (n, i = {}) => { + u(V, [n, { ...i, customAttributes: { ...r, ...(i.customAttributes || {}) } }, e], t); + }, + noticeError: (n, i = {}) => { + u(F, [n, { ...r, ...i }, e], t); + }, + setApplicationVersion: (e) => { + r['application.version'] = e; + }, + setCustomAttribute: (e, t) => { + r[e] = t; + }, + setUserId: (e) => { + r['enduser.id'] = e; + }, + metadata: { + customAttributes: r, + target: t, + get connected() { + return o || Promise.reject(new Error('Failed to connect')); + }, + }, + }; + i + ? i() + : (o = new Promise((n, i) => { + try { + const o = e.runtime?.entityManager; + let s = !!o?.get().entityGuid, + c = o?.getEntityGuidFor(t.licenseKey, t.applicationID), + u = !!c; + if (s && u) (t.entityGuid = c), n(a); + else { + const d = setTimeout(() => i(new Error('Failed to connect - Timeout')), 15e3); + function l(r) { + (0, W.A)(r, e) + ? (s ||= !0) + : t.licenseKey === r.licenseKey && + t.applicationID === r.applicationID && + ((u = !0), (t.entityGuid = r.entityGuid)), + s && u && (clearTimeout(d), e.ee.removeEventListener('entity-added', l), n(a)); + } + e.ee.emit('api-send-rum', [r, t]), e.ee.on('entity-added', l); + } + } catch (f) { + i(f); + } + })); + const u = async (t, r, a) => { + if (i) return i(); + const u = (0, c.t)(); + (0, s.p)(h.xV, ['API/register/'.concat(t.name, '/called')], void 0, n.K7.metrics, e.ee); + try { + await o; + const n = e.init.api.duplicate_registered_data; + (!0 === n || (Array.isArray(n) && n.includes(a.entityGuid))) && t(...r, void 0, u), + t(...r, a.entityGuid, u); + } catch (e) { + (0, l.R)(50, e); + } + }; + return a; + })(e, t); + }, + e + ); + } + class q extends E { + static featureName = k.T; + constructor(e) { + var t; + super(e, k.T), + (t = e), + p(u.o5, (e, r) => F(e, r, t), t), + (function (e) { + p( + u.bt, + function (t) { + e.runtime.onerror = t; + }, + e + ); + })(e), + (function (e) { + let t = 0; + p( + u.k6, + function (e, r) { + ++t > 10 || (this.runtime.releaseIds[e.slice(-200)] = ('' + r).slice(-200)); + }, + e + ); + })(e), + Z(e); + try { + this.removeOnAbort = new AbortController(); + } catch (e) {} + this.ee.on('internal-error', (t, r) => { + this.abortHandler && + (0, s.p)('ierr', [M(t), (0, c.t)(), !0, {}, e.runtime.isRecording, r], void 0, this.featureName, this.ee); + }), + y.gm.addEventListener( + 'unhandledrejection', + (t) => { + this.abortHandler && + (0, s.p)( + 'err', + [D(t), (0, c.t)(), !1, { unhandledPromiseRejection: 1 }, e.runtime.isRecording], + void 0, + this.featureName, + this.ee + ); + }, + (0, I.jT)(!1, this.removeOnAbort?.signal) + ), + y.gm.addEventListener( + 'error', + (t) => { + this.abortHandler && + (0, s.p)('err', [K(t), (0, c.t)(), !1, {}, e.runtime.isRecording], void 0, this.featureName, this.ee); + }, + (0, I.jT)(!1, this.removeOnAbort?.signal) + ), + (this.abortHandler = this.#r), + this.importAggregator(e, () => i.e(478).then(i.bind(i, 2176))); + } + #r() { + this.removeOnAbort?.abort(), (this.abortHandler = void 0); + } + } + var X = i(8990); + let Y = 1; + function Q(e) { + const t = typeof e; + return !e || ('object' !== t && 'function' !== t) + ? -1 + : e === y.gm + ? 0 + : (0, X.I)(e, 'nr@id', function () { + return Y++; + }); + } + function J(e) { + if ('string' == typeof e && e.length) return e.length; + if ('object' == typeof e) { + if ('undefined' != typeof ArrayBuffer && e instanceof ArrayBuffer && e.byteLength) return e.byteLength; + if ('undefined' != typeof Blob && e instanceof Blob && e.size) return e.size; + if (!('undefined' != typeof FormData && e instanceof FormData)) + try { + return (0, L.A)(e).length; + } catch (e) { + return; + } + } + } + var ee = i(8139), + te = i(7836), + re = i(3434); + const ne = {}, + ie = ['open', 'send']; + function oe(e) { + var t = e || te.ee; + const r = (function (e) { + return (e || te.ee).get('xhr'); + })(t); + if (void 0 === y.gm.XMLHttpRequest) return r; + if (ne[r.debugId]++) return r; + (ne[r.debugId] = 1), (0, ee.u)(t); + var n = (0, re.YM)(r), + i = y.gm.XMLHttpRequest, + o = y.gm.MutationObserver, + a = y.gm.Promise, + s = y.gm.setInterval, + c = 'readystatechange', + u = ['onload', 'onerror', 'onabort', 'onloadstart', 'onloadend', 'onprogress', 'ontimeout'], + d = [], + f = (y.gm.XMLHttpRequest = function (e) { + const t = new i(e), + o = r.context(t); + try { + r.emit('new-xhr', [t], o), + t.addEventListener( + c, + ((a = o), + function () { + var e = this; + e.readyState > 3 && !a.resolved && ((a.resolved = !0), r.emit('xhr-resolved', [], e)), + n.inPlace(e, u, 'fn-', b); + }), + (0, I.jT)(!1) + ); + } catch (e) { + (0, l.R)(15, e); + try { + r.emit('internal-error', [e]); + } catch (e) {} + } + var a; + return t; + }); + function h(e, t) { + n.inPlace(t, ['onreadystatechange'], 'fn-', b); + } + if ( + ((function (e, t) { + for (var r in e) t[r] = e[r]; + })(i, f), + (f.prototype = i.prototype), + n.inPlace(f.prototype, ie, '-xhr-', b), + r.on('send-xhr-start', function (e, t) { + h(e, t), + (function (e) { + d.push(e), o && (p ? p.then(v) : s ? s(v) : ((g = -g), (m.data = g))); + })(t); + }), + r.on('open-xhr-start', h), + o) + ) { + var p = a && a.resolve(); + if (!s && !a) { + var g = 1, + m = document.createTextNode(g); + new o(v).observe(m, { characterData: !0 }); + } + } else + t.on('fn-end', function (e) { + (e[0] && e[0].type === c) || v(); + }); + function v() { + for (var e = 0; e < d.length; e++) h(0, d[e]); + d.length && (d = []); + } + function b(e, t) { + return t; + } + return r; + } + var ae = 'fetch-', + se = ae + 'body-', + ce = ['arrayBuffer', 'blob', 'json', 'text', 'formData'], + ue = y.gm.Request, + de = y.gm.Response, + le = 'prototype'; + const fe = {}; + function he(e) { + const t = (function (e) { + return (e || te.ee).get('fetch'); + })(e); + if (!(ue && de && y.gm.fetch)) return t; + if (fe[t.debugId]++) return t; + function r(e, r, n) { + var i = e[r]; + 'function' == typeof i && + (e[r] = function () { + var e, + r = [...arguments], + o = {}; + t.emit(n + 'before-start', [r], o), o[te.P] && o[te.P].dt && (e = o[te.P].dt); + var a = i.apply(this, r); + return ( + t.emit(n + 'start', [r, e], a), + a.then( + function (e) { + return t.emit(n + 'end', [null, e], a), e; + }, + function (e) { + throw (t.emit(n + 'end', [e], a), e); + } + ) + ); + }); + } + return ( + (fe[t.debugId] = 1), + ce.forEach((e) => { + r(ue[le], e, se), r(de[le], e, se); + }), + r(y.gm, 'fetch', ae), + t.on(ae + 'end', function (e, r) { + var n = this; + if (r) { + var i = r.headers.get('content-length'); + null !== i && (n.rxSize = i), t.emit(ae + 'done', [null, r], n); + } else t.emit(ae + 'done', [e], n); + }), + t + ); + } + var pe = i(7485); + class ge { + constructor(e) { + this.agentRef = e; + } + generateTracePayload(t) { + const r = this.agentRef.loader_config; + if (!this.shouldGenerateTrace(t) || !r) return null; + var n = (r.accountID || '').toString() || null, + i = (r.agentID || '').toString() || null, + o = (r.trustKey || '').toString() || null; + if (!n || !i) return null; + var a = (0, e.ZF)(), + s = (0, e.el)(), + c = Date.now(), + u = { spanId: a, traceId: s, timestamp: c }; + return ( + (t.sameOrigin || (this.isAllowedOrigin(t) && this.useTraceContextHeadersForCors())) && + ((u.traceContextParentHeader = this.generateTraceContextParentHeader(a, s)), + (u.traceContextStateHeader = this.generateTraceContextStateHeader(a, c, n, i, o))), + ((t.sameOrigin && !this.excludeNewrelicHeader()) || + (!t.sameOrigin && this.isAllowedOrigin(t) && this.useNewrelicHeaderForCors())) && + (u.newrelicHeader = this.generateTraceHeader(a, s, c, n, i, o)), + u + ); + } + generateTraceContextParentHeader(e, t) { + return '00-' + t + '-' + e + '-01'; + } + generateTraceContextStateHeader(e, t, r, n, i) { + return i + '@nr=0-1-' + r + '-' + n + '-' + e + '----' + t; + } + generateTraceHeader(e, t, r, n, i, o) { + if (!('function' == typeof y.gm?.btoa)) return null; + var a = { v: [0, 1], d: { ty: 'Browser', ac: n, ap: i, id: e, tr: t, ti: r } }; + return o && n !== o && (a.d.tk = o), btoa((0, L.A)(a)); + } + shouldGenerateTrace(e) { + return this.agentRef.init?.distributed_tracing?.enabled && this.isAllowedOrigin(e); + } + isAllowedOrigin(e) { + var t = !1; + const r = this.agentRef.init?.distributed_tracing; + if (e.sameOrigin) t = !0; + else if (r?.allowed_origins instanceof Array) + for (var n = 0; n < r.allowed_origins.length; n++) { + var i = (0, pe.D)(r.allowed_origins[n]); + if (e.hostname === i.hostname && e.protocol === i.protocol && e.port === i.port) { + t = !0; + break; + } + } + return t; + } + excludeNewrelicHeader() { + var e = this.agentRef.init?.distributed_tracing; + return !!e && !!e.exclude_newrelic_header; + } + useNewrelicHeaderForCors() { + var e = this.agentRef.init?.distributed_tracing; + return !!e && !1 !== e.cors_use_newrelic_header; + } + useTraceContextHeadersForCors() { + var e = this.agentRef.init?.distributed_tracing; + return !!e && !!e.cors_use_tracecontext_headers; + } + } + var me = i(9300), + ve = i(7295); + function be(e) { + return 'string' == typeof e + ? e + : e instanceof (0, N.dV)().o.REQ + ? e.url + : y.gm?.URL && e instanceof URL + ? e.href + : void 0; + } + var ye = ['load', 'error', 'abort', 'timeout'], + we = ye.length, + Re = (0, N.dV)().o.REQ, + xe = (0, N.dV)().o.XHR; + const Te = 'X-NewRelic-App-Data'; + class Ee extends E { + static featureName = me.T; + constructor(e) { + super(e, me.T), (this.dt = new ge(e)), (this.handler = (e, t, r, n) => (0, s.p)(e, t, r, n, this.ee)); + try { + const e = { xmlhttprequest: 'xhr', fetch: 'fetch', beacon: 'beacon' }; + y.gm?.performance?.getEntriesByType('resource').forEach((t) => { + if (t.initiatorType in e && 0 !== t.responseStatus) { + const r = { status: t.responseStatus }, + i = { rxSize: t.transferSize, duration: Math.floor(t.duration), cbTime: 0 }; + Ae(r, t.name), + this.handler('xhr', [r, i, t.startTime, t.responseEnd, e[t.initiatorType]], void 0, n.K7.ajax); + } + }); + } catch (e) {} + he(this.ee), + oe(this.ee), + (function (e, t, r, i) { + function o(e) { + var t = this; + (t.totalCbs = 0), + (t.called = 0), + (t.cbTime = 0), + (t.end = E), + (t.ended = !1), + (t.xhrGuids = {}), + (t.lastSize = null), + (t.loadCaptureCalled = !1), + (t.params = this.params || {}), + (t.metrics = this.metrics || {}), + (t.latestLongtaskEnd = 0), + e.addEventListener( + 'load', + function (r) { + A(t, e); + }, + (0, I.jT)(!1) + ), + y.lR || + e.addEventListener( + 'progress', + function (e) { + t.lastSize = e.loaded; + }, + (0, I.jT)(!1) + ); + } + function a(e) { + (this.params = { method: e[0] }), Ae(this, e[1]), (this.metrics = {}); + } + function u(t, r) { + e.loader_config.xpid && this.sameOrigin && r.setRequestHeader('X-NewRelic-ID', e.loader_config.xpid); + var n = i.generateTracePayload(this.parsedOrigin); + if (n) { + var o = !1; + n.newrelicHeader && (r.setRequestHeader('newrelic', n.newrelicHeader), (o = !0)), + n.traceContextParentHeader && + (r.setRequestHeader('traceparent', n.traceContextParentHeader), + n.traceContextStateHeader && r.setRequestHeader('tracestate', n.traceContextStateHeader), + (o = !0)), + o && (this.dt = n); + } + } + function d(e, r) { + var n = this.metrics, + i = e[0], + o = this; + if (n && i) { + var a = J(i); + a && (n.txSize = a); + } + (this.startTime = (0, c.t)()), + (this.body = i), + (this.listener = function (e) { + try { + 'abort' !== e.type || o.loadCaptureCalled || (o.params.aborted = !0), + ('load' !== e.type || + (o.called === o.totalCbs && + (o.onloadCalled || 'function' != typeof r.onload) && + 'function' == typeof o.end)) && + o.end(r); + } catch (e) { + try { + t.emit('internal-error', [e]); + } catch (e) {} + } + }); + for (var s = 0; s < we; s++) r.addEventListener(ye[s], this.listener, (0, I.jT)(!1)); + } + function l(e, t, r) { + (this.cbTime += e), + t ? (this.onloadCalled = !0) : (this.called += 1), + this.called !== this.totalCbs || + (!this.onloadCalled && 'function' == typeof r.onload) || + 'function' != typeof this.end || + this.end(r); + } + function f(e, t) { + var r = '' + Q(e) + !!t; + this.xhrGuids && !this.xhrGuids[r] && ((this.xhrGuids[r] = !0), (this.totalCbs += 1)); + } + function p(e, t) { + var r = '' + Q(e) + !!t; + this.xhrGuids && this.xhrGuids[r] && (delete this.xhrGuids[r], (this.totalCbs -= 1)); + } + function g() { + this.endTime = (0, c.t)(); + } + function m(e, r) { + r instanceof xe && 'load' === e[0] && t.emit('xhr-load-added', [e[1], e[2]], r); + } + function v(e, r) { + r instanceof xe && 'load' === e[0] && t.emit('xhr-load-removed', [e[1], e[2]], r); + } + function b(e, t, r) { + t instanceof xe && + ('onload' === r && (this.onload = !0), + ('load' === (e[0] && e[0].type) || this.onload) && (this.xhrCbStart = (0, c.t)())); + } + function w(e, r) { + this.xhrCbStart && t.emit('xhr-cb-time', [(0, c.t)() - this.xhrCbStart, this.onload, r], r); + } + function R(e) { + var t, + r = e[1] || {}; + if ( + ('string' == typeof e[0] + ? 0 === (t = e[0]).length && y.RI && (t = '' + y.gm.location.href) + : e[0] && e[0].url + ? (t = e[0].url) + : y.gm?.URL && e[0] && e[0] instanceof URL + ? (t = e[0].href) + : 'function' == typeof e[0].toString && (t = e[0].toString()), + 'string' == typeof t && 0 !== t.length) + ) { + t && ((this.parsedOrigin = (0, pe.D)(t)), (this.sameOrigin = this.parsedOrigin.sameOrigin)); + var n = i.generateTracePayload(this.parsedOrigin); + if (n && (n.newrelicHeader || n.traceContextParentHeader)) + if (e[0] && e[0].headers) s(e[0].headers, n) && (this.dt = n); + else { + var o = {}; + for (var a in r) o[a] = r[a]; + (o.headers = new Headers(r.headers || {})), + s(o.headers, n) && (this.dt = n), + e.length > 1 ? (e[1] = o) : e.push(o); + } + } + function s(e, t) { + var r = !1; + return ( + t.newrelicHeader && (e.set('newrelic', t.newrelicHeader), (r = !0)), + t.traceContextParentHeader && + (e.set('traceparent', t.traceContextParentHeader), + t.traceContextStateHeader && e.set('tracestate', t.traceContextStateHeader), + (r = !0)), + r + ); + } + } + function x(e, t) { + (this.params = {}), + (this.metrics = {}), + (this.startTime = (0, c.t)()), + (this.dt = t), + e.length >= 1 && (this.target = e[0]), + e.length >= 2 && (this.opts = e[1]); + var r = this.opts || {}, + n = this.target; + Ae(this, be(n)); + var i = ('' + ((n && n instanceof Re && n.method) || r.method || 'GET')).toUpperCase(); + (this.params.method = i), (this.body = r.body), (this.txSize = J(r.body) || 0); + } + function T(e, t) { + if (((this.endTime = (0, c.t)()), this.params || (this.params = {}), (0, ve.iW)(this.params))) return; + let i; + (this.params.status = t ? t.status : 0), + 'string' == typeof this.rxSize && this.rxSize.length > 0 && (i = +this.rxSize); + const o = { txSize: this.txSize, rxSize: i, duration: (0, c.t)() - this.startTime }; + r('xhr', [this.params, o, this.startTime, this.endTime, 'fetch'], this, n.K7.ajax); + } + function E(e) { + const t = this.params, + i = this.metrics; + if (!this.ended) { + this.ended = !0; + for (let t = 0; t < we; t++) e.removeEventListener(ye[t], this.listener, !1); + t.aborted || + (0, ve.iW)(t) || + ((i.duration = (0, c.t)() - this.startTime), + this.loadCaptureCalled || 4 !== e.readyState ? null == t.status && (t.status = 0) : A(this, e), + (i.cbTime = this.cbTime), + r('xhr', [t, i, this.startTime, this.endTime, 'xhr'], this, n.K7.ajax)); + } + } + function A(e, r) { + e.params.status = r.status; + var i = (function (e, t) { + var r = e.responseType; + return 'json' === r && null !== t + ? t + : 'arraybuffer' === r || 'blob' === r || 'json' === r + ? J(e.response) + : 'text' === r || '' === r || void 0 === r + ? J(e.responseText) + : void 0; + })(r, e.lastSize); + if ((i && (e.metrics.rxSize = i), e.sameOrigin && r.getAllResponseHeaders().indexOf(Te) >= 0)) { + var o = r.getResponseHeader(Te); + o && + ((0, s.p)(h.rs, ['Ajax/CrossApplicationTracing/Header/Seen'], void 0, n.K7.metrics, t), + (e.params.cat = o.split(', ').pop())); + } + e.loadCaptureCalled = !0; + } + t.on('new-xhr', o), + t.on('open-xhr-start', a), + t.on('open-xhr-end', u), + t.on('send-xhr-start', d), + t.on('xhr-cb-time', l), + t.on('xhr-load-added', f), + t.on('xhr-load-removed', p), + t.on('xhr-resolved', g), + t.on('addEventListener-end', m), + t.on('removeEventListener-end', v), + t.on('fn-end', w), + t.on('fetch-before-start', R), + t.on('fetch-start', x), + t.on('fn-start', b), + t.on('fetch-done', T); + })(e, this.ee, this.handler, this.dt), + this.importAggregator(e, () => i.e(478).then(i.bind(i, 3845))); + } + } + function Ae(e, t) { + var r = (0, pe.D)(t), + n = e.params || e; + (n.hostname = r.hostname), + (n.port = r.port), + (n.protocol = r.protocol), + (n.host = r.hostname + ':' + r.port), + (n.pathname = r.pathname), + (e.parsedOrigin = r), + (e.sameOrigin = r.sameOrigin); + } + const Oe = {}, + Se = ['pushState', 'replaceState']; + function Ne(e) { + const t = (function (e) { + return (e || te.ee).get('history'); + })(e); + return !y.RI || Oe[t.debugId]++ || ((Oe[t.debugId] = 1), (0, re.YM)(t).inPlace(window.history, Se, '-')), t; + } + var _e = i(3738); + function Ie(e) { + p( + u.BL, + function (t = Date.now()) { + const r = t - y.WN; + r < 0 && (0, l.R)(62, t), + (0, s.p)(h.XG, [u.BL, { time: r }], void 0, n.K7.metrics, e.ee), + e.addToTrace({ name: u.BL, start: t, origin: 'nr' }), + (0, s.p)(u.Pl + u.hG, [r, u.BL], void 0, n.K7.genericEvents, e.ee); + }, + e + ); + } + const { He: Pe, bD: je, d3: Ce, Kp: ke, TZ: Le, Lc: He, uP: Me, Rz: De } = _e; + class Ke extends E { + static featureName = Le; + constructor(e) { + var t; + super(e, Le), + (t = e), + p( + u.U2, + function (e) { + if (!(e && 'object' == typeof e && e.name && e.start)) return; + const r = { n: e.name, s: e.start - y.WN, e: (e.end || e.start) - y.WN, o: e.origin || '', t: 'api' }; + r.s < 0 || r.e < 0 || r.e < r.s + ? (0, l.R)(61, { start: r.s, end: r.e }) + : (0, s.p)('bstApi', [r], void 0, n.K7.sessionTrace, t.ee); + }, + t + ), + Ie(e); + if (!(0, R.V)(e.init)) return void this.deregisterDrain(); + const r = this.ee; + let o; + Ne(r), + (this.eventsEE = (0, ee.u)(r)), + this.eventsEE.on(Me, function (e, t) { + this.bstStart = (0, c.t)(); + }), + this.eventsEE.on(He, function (e, t) { + (0, s.p)('bst', [e[0], t, this.bstStart, (0, c.t)()], void 0, n.K7.sessionTrace, r); + }), + r.on(De + Ce, function (e) { + (this.time = (0, c.t)()), (this.startPath = location.pathname + location.hash); + }), + r.on(De + ke, function (e) { + (0, s.p)( + 'bstHist', + [location.pathname + location.hash, this.startPath, this.time], + void 0, + n.K7.sessionTrace, + r + ); + }); + try { + (o = new PerformanceObserver((e) => { + const t = e.getEntries(); + (0, s.p)(Pe, [t], void 0, n.K7.sessionTrace, r); + })), + o.observe({ type: je, buffered: !0 }); + } catch (e) {} + this.importAggregator(e, () => i.e(478).then(i.bind(i, 6974)), { resourceObserver: o }); + } + } + var Ue = i(6344); + class Fe extends E { + static featureName = Ue.TZ; + #n; + recorder; + constructor(e) { + var t; + let r; + super(e, Ue.TZ), + (t = e), + p( + u.CH, + function () { + (0, s.p)(u.CH, [], void 0, n.K7.sessionReplay, t.ee); + }, + t + ), + (function (e) { + p( + u.Tb, + function () { + (0, s.p)(u.Tb, [], void 0, n.K7.sessionReplay, e.ee); + }, + e + ); + })(e); + try { + r = JSON.parse(localStorage.getItem(''.concat(O.H3, '_').concat(O.uh))); + } catch (e) {} + (0, w.SR)(e.init) && this.ee.on(Ue.G4.RECORD, () => this.#i()), + this.#o(r) && + this.importRecorder().then((e) => { + e.startRecording(Ue.Qb.PRELOAD, r?.sessionReplayMode); + }), + this.importAggregator(this.agentRef, () => i.e(478).then(i.bind(i, 6167)), this), + this.ee.on('err', (e) => { + this.blocked || + (this.agentRef.runtime.isRecording && + ((this.errorNoticed = !0), + (0, s.p)(Ue.G4.ERROR_DURING_REPLAY, [e], void 0, this.featureName, this.ee))); + }); + } + #o(e) { + return ( + (e && (e.sessionReplayMode === O.g.FULL || e.sessionReplayMode === O.g.ERROR)) || + (0, w.Aw)(this.agentRef.init) + ); + } + importRecorder() { + return this.recorder + ? Promise.resolve(this.recorder) + : ((this.#n ??= Promise.all([i.e(478), i.e(249)]) + .then(i.bind(i, 4866)) + .then(({ Recorder: e }) => ((this.recorder = new e(this)), this.recorder)) + .catch((e) => { + throw (this.ee.emit('internal-error', [e]), (this.blocked = !0), e); + })), + this.#n); + } + #i() { + this.blocked || + (this.featAggregate + ? this.featAggregate.mode !== O.g.FULL && this.featAggregate.initializeRecording(O.g.FULL, !0, Ue.Qb.API) + : this.importRecorder().then(() => { + this.recorder.startRecording(Ue.Qb.API, O.g.FULL); + })); + } + } + var We = i(3962); + function Be(e) { + const t = e.ee.get('tracer'); + function r() {} + p( + u.dT, + function (e) { + return new r().get('object' == typeof e ? e : {}); + }, + e + ); + const i = (r.prototype = { + createTracer: function (r, i) { + var o = {}, + a = this, + d = 'function' == typeof i; + return ( + (0, s.p)(h.xV, ['API/createTracer/called'], void 0, n.K7.metrics, e.ee), + e.runSoftNavOverSpa || (0, s.p)(u.hw + 'tracer', [(0, c.t)(), r, o], a, n.K7.spa, e.ee), + function () { + if ((t.emit((d ? '' : 'no-') + 'fn-start', [(0, c.t)(), a, d], o), d)) + try { + return i.apply(this, arguments); + } catch (e) { + const r = 'string' == typeof e ? new Error(e) : e; + throw (t.emit('fn-err', [arguments, this, r], o), r); + } finally { + t.emit('fn-end', [(0, c.t)()], o); + } + } + ); + }, + }); + ['actionText', 'setName', 'setAttribute', 'save', 'ignore', 'onEnd', 'getContext', 'end', 'get'].forEach( + (t) => { + p.apply(this, [ + t, + function () { + return ( + (0, s.p)( + u.hw + t, + [(0, c.t)(), ...arguments], + this, + e.runSoftNavOverSpa ? n.K7.softNav : n.K7.spa, + e.ee + ), + this + ); + }, + e, + i, + ]); + } + ), + p( + u.PA, + function () { + e.runSoftNavOverSpa + ? (0, s.p)(u.hw + 'routeName', [performance.now(), ...arguments], void 0, n.K7.softNav, e.ee) + : (0, s.p)(u.Pl + 'routeName', [(0, c.t)(), ...arguments], this, n.K7.spa, e.ee); + }, + e + ); + } + class Ge extends E { + static featureName = We.TZ; + constructor(e) { + if ((super(e, We.TZ), Be(e), !y.RI || !(0, N.dV)().o.MO)) return; + const t = Ne(this.ee); + try { + this.removeOnAbort = new AbortController(); + } catch (e) {} + We.tC.forEach((e) => { + (0, I.sp)( + e, + (e) => { + a(e); + }, + !0, + this.removeOnAbort?.signal + ); + }); + const r = () => (0, s.p)('newURL', [(0, c.t)(), '' + window.location], void 0, this.featureName, this.ee); + t.on('pushState-end', r), + t.on('replaceState-end', r), + (0, I.sp)( + We.OV, + (e) => { + a(e), (0, s.p)('newURL', [e.timeStamp, '' + window.location], void 0, this.featureName, this.ee); + }, + !0, + this.removeOnAbort?.signal + ); + let n = !1; + const o = new ((0, N.dV)().o.MO)((e, t) => { + n || + ((n = !0), + requestAnimationFrame(() => { + (0, s.p)('newDom', [(0, c.t)()], void 0, this.featureName, this.ee), (n = !1); + })); + }), + a = (0, x.s)( + (e) => { + (0, s.p)('newUIEvent', [e], void 0, this.featureName, this.ee), + o.observe(document.body, { attributes: !0, childList: !0, subtree: !0, characterData: !0 }); + }, + 100, + { leading: !0 } + ); + (this.abortHandler = function () { + this.removeOnAbort?.abort(), o.disconnect(), (this.abortHandler = void 0); + }), + this.importAggregator(e, () => i.e(478).then(i.bind(i, 4393)), { domObserver: o }); + } + } + var Ve = i(7378); + const ze = {}, + Ze = ['appendChild', 'insertBefore', 'replaceChild']; + function qe(e) { + const t = (function (e) { + return (e || te.ee).get('jsonp'); + })(e); + if (!y.RI || ze[t.debugId]) return t; + ze[t.debugId] = !0; + var r = (0, re.YM)(t), + n = /[?&](?:callback|cb)=([^&#]+)/, + i = /(.*)\.([^.]+)/, + o = /^(\w+)(\.|$)(.*)$/; + function a(e, t) { + if (!e) return t; + const r = e.match(o), + n = r[1]; + return a(r[3], t[n]); + } + return ( + r.inPlace(Node.prototype, Ze, 'dom-'), + t.on('dom-start', function (e) { + !(function (e) { + if (!e || 'string' != typeof e.nodeName || 'script' !== e.nodeName.toLowerCase()) return; + if ('function' != typeof e.addEventListener) return; + var o = ((s = e.src), (c = s.match(n)), c ? c[1] : null); + var s, c; + if (!o) return; + var u = (function (e) { + var t = e.match(i); + if (t && t.length >= 3) return { key: t[2], parent: a(t[1], window) }; + return { key: e, parent: window }; + })(o); + if ('function' != typeof u.parent[u.key]) return; + var d = {}; + function l() { + t.emit('jsonp-end', [], d), + e.removeEventListener('load', l, (0, I.jT)(!1)), + e.removeEventListener('error', f, (0, I.jT)(!1)); + } + function f() { + t.emit('jsonp-error', [], d), + t.emit('jsonp-end', [], d), + e.removeEventListener('load', l, (0, I.jT)(!1)), + e.removeEventListener('error', f, (0, I.jT)(!1)); + } + r.inPlace(u.parent, [u.key], 'cb-', d), + e.addEventListener('load', l, (0, I.jT)(!1)), + e.addEventListener('error', f, (0, I.jT)(!1)), + t.emit('new-jsonp', [e.src], d); + })(e[0]); + }), + t + ); + } + const Xe = {}; + function Ye(e) { + const t = (function (e) { + return (e || te.ee).get('promise'); + })(e); + if (Xe[t.debugId]) return t; + Xe[t.debugId] = !0; + var r = t.context, + n = (0, re.YM)(t), + i = y.gm.Promise; + return ( + i && + (function () { + function e(r) { + var o = t.context(), + a = n(r, 'executor-', o, null, !1); + const s = Reflect.construct(i, [a], e); + return ( + (t.context(s).getCtx = function () { + return o; + }), + s + ); + } + (y.gm.Promise = e), + Object.defineProperty(e, 'name', { value: 'Promise' }), + (e.toString = function () { + return i.toString(); + }), + Object.setPrototypeOf(e, i), + ['all', 'race'].forEach(function (r) { + const n = i[r]; + e[r] = function (e) { + let i = !1; + [...(e || [])].forEach((e) => { + this.resolve(e).then(a('all' === r), a(!1)); + }); + const o = n.apply(this, arguments); + return o; + function a(e) { + return function () { + t.emit('propagate', [null, !i], o, !1, !1), (i = i || !e); + }; + } + }; + }), + ['resolve', 'reject'].forEach(function (r) { + const n = i[r]; + e[r] = function (e) { + const r = n.apply(this, arguments); + return e !== r && t.emit('propagate', [e, !0], r, !1, !1), r; + }; + }), + (e.prototype = i.prototype); + const o = i.prototype.then; + (i.prototype.then = function (...e) { + var i = this, + a = r(i); + (a.promise = i), (e[0] = n(e[0], 'cb-', a, null, !1)), (e[1] = n(e[1], 'cb-', a, null, !1)); + const s = o.apply(this, e); + return (a.nextPromise = s), t.emit('propagate', [i, !0], s, !1, !1), s; + }), + (i.prototype.then[re.Jt] = o), + t.on('executor-start', function (e) { + (e[0] = n(e[0], 'resolve-', this, null, !1)), (e[1] = n(e[1], 'resolve-', this, null, !1)); + }), + t.on('executor-err', function (e, t, r) { + e[1](r); + }), + t.on('cb-end', function (e, r, n) { + t.emit('propagate', [n, !0], this.nextPromise, !1, !1); + }), + t.on('propagate', function (e, r, n) { + (this.getCtx && !r) || + (this.getCtx = function () { + if (e instanceof Promise) var r = t.context(e); + return r && r.getCtx ? r.getCtx() : this; + }); + }); + })(), + t + ); + } + const Qe = {}, + $e = 'setTimeout', + Je = 'setInterval', + et = 'clearTimeout', + tt = '-start', + rt = [$e, 'setImmediate', Je, et, 'clearImmediate']; + function nt(e) { + const t = (function (e) { + return (e || te.ee).get('timer'); + })(e); + if (Qe[t.debugId]++) return t; + Qe[t.debugId] = 1; + var r = (0, re.YM)(t); + return ( + r.inPlace(y.gm, rt.slice(0, 2), $e + '-'), + r.inPlace(y.gm, rt.slice(2, 3), Je + '-'), + r.inPlace(y.gm, rt.slice(3), et + '-'), + t.on(Je + tt, function (e, t, n) { + e[0] = r(e[0], 'fn-', null, n); + }), + t.on($e + tt, function (e, t, n) { + (this.method = n), (this.timerDuration = isNaN(e[1]) ? 0 : +e[1]), (e[0] = r(e[0], 'fn-', this, n)); + }), + t + ); + } + const it = {}; + function ot(e) { + const t = (function (e) { + return (e || te.ee).get('mutation'); + })(e); + if (!y.RI || it[t.debugId]) return t; + it[t.debugId] = !0; + var r = (0, re.YM)(t), + n = y.gm.MutationObserver; + return ( + n && + ((window.MutationObserver = function (e) { + return this instanceof n ? new n(r(e, 'fn-')) : n.apply(this, arguments); + }), + (MutationObserver.prototype = n.prototype)), + t + ); + } + const { TZ: at, d3: st, Kp: ct, $p: ut, wW: dt, e5: lt, tH: ft, uP: ht, rw: pt, Lc: gt } = Ve; + class mt extends E { + static featureName = at; + constructor(e) { + if ((super(e, at), Be(e), !y.RI)) return; + try { + this.removeOnAbort = new AbortController(); + } catch (e) {} + let t, + r = 0; + const n = this.ee.get('tracer'), + o = qe(this.ee), + a = Ye(this.ee), + u = nt(this.ee), + d = oe(this.ee), + l = this.ee.get('events'), + f = he(this.ee), + h = Ne(this.ee), + p = ot(this.ee); + function g(e, t) { + h.emit('newURL', ['' + window.location, t]); + } + function m() { + r++, (t = window.location.hash), (this[ht] = (0, c.t)()); + } + function v() { + r--, window.location.hash !== t && g(0, !0); + var e = (0, c.t)(); + (this[lt] = ~~this[lt] + e - this[ht]), (this[gt] = e); + } + function b(e, t) { + e.on(t, function () { + this[t] = (0, c.t)(); + }); + } + this.ee.on(ht, m), + a.on(pt, m), + o.on(pt, m), + this.ee.on(gt, v), + a.on(dt, v), + o.on(dt, v), + this.ee.on('fn-err', (...t) => { + t[2]?.__newrelic?.[e.agentIdentifier] || + (0, s.p)('function-err', [...t], void 0, this.featureName, this.ee); + }), + this.ee.buffer([ht, gt, 'xhr-resolved'], this.featureName), + l.buffer([ht], this.featureName), + u.buffer(['setTimeout' + ct, 'clearTimeout' + st, ht], this.featureName), + d.buffer([ht, 'new-xhr', 'send-xhr' + st], this.featureName), + f.buffer([ft + st, ft + '-done', ft + ut + st, ft + ut + ct], this.featureName), + h.buffer(['newURL'], this.featureName), + p.buffer([ht], this.featureName), + a.buffer(['propagate', pt, dt, 'executor-err', 'resolve' + st], this.featureName), + n.buffer([ht, 'no-' + ht], this.featureName), + o.buffer(['new-jsonp', 'cb-start', 'jsonp-error', 'jsonp-end'], this.featureName), + b(f, ft + st), + b(f, ft + '-done'), + b(o, 'new-jsonp'), + b(o, 'jsonp-end'), + b(o, 'cb-start'), + h.on('pushState-end', g), + h.on('replaceState-end', g), + window.addEventListener('hashchange', g, (0, I.jT)(!0, this.removeOnAbort?.signal)), + window.addEventListener('load', g, (0, I.jT)(!0, this.removeOnAbort?.signal)), + window.addEventListener( + 'popstate', + function () { + g(0, r > 1); + }, + (0, I.jT)(!0, this.removeOnAbort?.signal) + ), + (this.abortHandler = this.#r), + this.importAggregator(e, () => i.e(478).then(i.bind(i, 5592))); + } + #r() { + this.removeOnAbort?.abort(), (this.abortHandler = void 0); + } + } + var vt = i(3333); + class bt extends E { + static featureName = vt.TZ; + constructor(e) { + super(e, vt.TZ); + const t = [ + e.init.page_action.enabled, + e.init.performance.capture_marks, + e.init.performance.capture_measures, + e.init.user_actions.enabled, + e.init.performance.resources.enabled, + ]; + var r; + if ( + ((r = e), + p(u.hG, (e, t) => z(e, t, r), r), + (function (e) { + p( + u.fF, + function () { + (0, s.p)(u.Pl + u.fF, [(0, c.t)(), ...arguments], void 0, n.K7.genericEvents, e.ee); + }, + e + ); + })(e), + Ie(e), + Z(e), + (function (e) { + p( + u.V1, + function (t, r) { + const i = (0, c.t)(), + { start: o, end: a, customAttributes: d } = r || {}, + f = { customAttributes: d || {} }; + if ('object' != typeof f.customAttributes || 'string' != typeof t || 0 === t.length) + return void (0, l.R)(57); + const h = (e, t) => + null == e ? t : 'number' == typeof e ? e : e instanceof PerformanceMark ? e.startTime : Number.NaN; + if (((f.start = h(o, 0)), (f.end = h(a, i)), Number.isNaN(f.start) || Number.isNaN(f.end))) + (0, l.R)(57); + else { + if (((f.duration = f.end - f.start), !(f.duration < 0))) + return (0, s.p)(u.Pl + u.V1, [f, t], void 0, n.K7.genericEvents, e.ee), f; + (0, l.R)(58); + } + }, + e + ); + })(e), + y.RI) + ) { + if ( + (e.init.user_actions.enabled && + (vt.Zp.forEach((e) => (0, I.sp)(e, (e) => (0, s.p)('ua', [e], void 0, this.featureName, this.ee), !0)), + vt.qN.forEach((e) => { + const t = (0, x.s)( + (e) => { + (0, s.p)('ua', [e], void 0, this.featureName, this.ee); + }, + 500, + { leading: !0 } + ); + (0, I.sp)(e, t); + })), + e.init.performance.resources.enabled && + y.gm.PerformanceObserver?.supportedEntryTypes.includes('resource')) + ) { + new PerformanceObserver((e) => { + e.getEntries().forEach((e) => { + (0, s.p)('browserPerformance.resource', [e], void 0, this.featureName, this.ee); + }); + }).observe({ type: 'resource', buffered: !0 }); + } + const a = Ne(this.ee); + function d() { + a.emit('navChange'); + } + a.on('pushState-end', d), + a.on('replaceState-end', d), + window.addEventListener('hashchange', d, (0, I.jT)(!0, this.removeOnAbort?.signal)), + window.addEventListener('popstate', d, (0, I.jT)(!0, this.removeOnAbort?.signal)); + } + try { + this.removeOnAbort = new AbortController(); + } catch (f) {} + function o(t) { + const r = (0, pe.D)(t); + return e.beacons.includes(r.hostname + ':' + r.port); + } + (this.abortHandler = () => { + this.removeOnAbort?.abort(), (this.abortHandler = void 0); + }), + y.gm.addEventListener( + 'error', + () => { + (0, s.p)('uaErr', [], void 0, n.K7.genericEvents, this.ee); + }, + (0, I.jT)(!1, this.removeOnAbort?.signal) + ), + he(this.ee), + oe(this.ee), + this.ee.on('open-xhr-start', (e, t) => { + o(e[1]) || + t.addEventListener('readystatechange', () => { + 2 === t.readyState && (0, s.p)('uaXhr', [], void 0, n.K7.genericEvents, this.ee); + }); + }), + this.ee.on('fetch-start', (e) => { + e.length >= 1 && !o(be(e[0])) && (0, s.p)('uaXhr', [], void 0, n.K7.genericEvents, this.ee); + }), + t.some((e) => e) ? this.importAggregator(e, () => i.e(478).then(i.bind(i, 8019))) : this.deregisterDrain(); + } + } + var yt = i(2646); + const wt = new Map(); + function Rt(e, t, r, n) { + if ('object' != typeof t || !t || 'string' != typeof r || !r || 'function' != typeof t[r]) return (0, l.R)(29); + const i = (function (e) { + return (e || te.ee).get('logger'); + })(e), + o = (0, re.YM)(i), + a = new yt.y(te.P); + (a.level = n.level), (a.customAttributes = n.customAttributes); + const s = t[r]?.[re.Jt] || t[r]; + return wt.set(s, a), o.inPlace(t, [r], 'wrap-logger-', () => wt.get(s)), i; + } + var xt = i(1910); + class Tt extends E { + static featureName = B.TZ; + constructor(e) { + var t; + super(e, B.TZ), + (t = e), + p(u.$9, (e, r) => V(e, r, t), t), + (function (e) { + p( + u.Wb, + (t, r, { customAttributes: n = {}, level: i = B.p_.INFO } = {}) => { + Rt(e.ee, t, r, { customAttributes: n, level: i }); + }, + e + ); + })(e), + Z(e); + const r = this.ee; + ['log', 'error', 'warn', 'info', 'debug', 'trace'].forEach((e) => { + (0, xt.i)(y.gm.console[e]), Rt(r, y.gm.console, e, { level: 'log' === e ? 'info' : e }); + }), + this.ee.on('wrap-logger-end', function ([e]) { + const { level: t, customAttributes: n } = this; + (0, G.R)(r, e, n, t); + }), + this.importAggregator(e, () => i.e(478).then(i.bind(i, 5288))); + } + } + new (class extends r { + constructor(e) { + var t; + (super(), y.gm) + ? ((this.features = {}), + (0, N.bQ)(this.agentIdentifier, this), + (this.desiredFeatures = new Set(e.features || [])), + this.desiredFeatures.add(S), + (this.runSoftNavOverSpa = [...this.desiredFeatures].some((e) => e.featureName === n.K7.softNav)), + (0, a.j)(this, e, e.loaderType || 'agent'), + (t = this), + p( + u.cD, + function (e, r, n = !1) { + if ('string' == typeof e) { + if (['string', 'number', 'boolean'].includes(typeof r) || null === r) return g(t, e, r, u.cD, n); + (0, l.R)(40, typeof r); + } else (0, l.R)(39, typeof e); + }, + t + ), + (function (e) { + p( + u.Dl, + function (t) { + if ('string' == typeof t || null === t) return g(e, 'enduser.id', t, u.Dl, !0); + (0, l.R)(41, typeof t); + }, + e + ); + })(this), + (function (e) { + p( + u.nb, + function (t) { + if ('string' == typeof t || null === t) return g(e, 'application.version', t, u.nb, !1); + (0, l.R)(42, typeof t); + }, + e + ); + })(this), + (function (e) { + p( + u.d3, + function () { + e.ee.emit('manual-start-all'); + }, + e + ); + })(this), + this.run()) + : (0, l.R)(21); + } + get config() { + return { info: this.info, init: this.init, loader_config: this.loader_config, runtime: this.runtime }; + } + get api() { + return this; + } + run() { + try { + const e = (function (e) { + const t = {}; + return ( + o.forEach((r) => { + t[r] = !!e[r]?.enabled; + }), + t + ); + })(this.init), + t = [...this.desiredFeatures]; + t.sort((e, t) => n.P3[e.featureName] - n.P3[t.featureName]), + t.forEach((t) => { + if (!e[t.featureName] && t.featureName !== n.K7.pageViewEvent) return; + if (this.runSoftNavOverSpa && t.featureName === n.K7.spa) return; + if (!this.runSoftNavOverSpa && t.featureName === n.K7.softNav) return; + const r = (function (e) { + switch (e) { + case n.K7.ajax: + return [n.K7.jserrors]; + case n.K7.sessionTrace: + return [n.K7.ajax, n.K7.pageViewEvent]; + case n.K7.sessionReplay: + return [n.K7.sessionTrace]; + case n.K7.pageViewTiming: + return [n.K7.pageViewEvent]; + default: + return []; + } + })(t.featureName).filter((e) => !(e in this.features)); + r.length > 0 && (0, l.R)(36, { targetFeature: t.featureName, missingDependencies: r }), + (this.features[t.featureName] = new t(this)); + }); + } catch (e) { + (0, l.R)(22, e); + for (const e in this.features) this.features[e].abortHandler?.(); + const t = (0, N.Zm)(); + delete t.initializedAgents[this.agentIdentifier]?.features, delete this.sharedAggregator; + return t.ee.get(this.agentIdentifier).abort(), !1; + } + } + })({ features: [Ee, S, j, Ke, Fe, C, q, bt, Tt, Ge, mt], loaderType: 'spa' }); + })(); +})(); diff --git a/src/environments/environment.development.ts b/src/environments/environment.development.ts index 9d7ee2eed..c4230b680 100644 --- a/src/environments/environment.development.ts +++ b/src/environments/environment.development.ts @@ -68,7 +68,7 @@ export const environment = { newRelicInfoSa: 1, newRelicLoaderConfigAccountID: '', newRelicLoaderConfigTrustKey: '', - newRelicLoaderConfigAgengID: '', + newRelicLoaderConfigAgentID: '', newRelicLoaderConfigLicenseKey: '', newRelicLoaderConfigApplicationID: '', }; diff --git a/src/environments/environment.docker.ts b/src/environments/environment.docker.ts index 9fd75b469..92a84f2ee 100644 --- a/src/environments/environment.docker.ts +++ b/src/environments/environment.docker.ts @@ -25,7 +25,7 @@ export const environment = { newRelicInfoSa: 1, newRelicLoaderConfigAccountID: '', newRelicLoaderConfigTrustKey: '', - newRelicLoaderConfigAgengID: '', + newRelicLoaderConfigAgentID: '', newRelicLoaderConfigLicenseKey: '', newRelicLoaderConfigApplicationID: '', }; diff --git a/src/environments/environment.staging.ts b/src/environments/environment.staging.ts index ff0f83afa..a7725102f 100644 --- a/src/environments/environment.staging.ts +++ b/src/environments/environment.staging.ts @@ -68,7 +68,7 @@ export const environment = { newRelicInfoSa: 1, newRelicLoaderConfigAccountID: '', newRelicLoaderConfigTrustKey: '', - newRelicLoaderConfigAgengID: '', + newRelicLoaderConfigAgentID: '', newRelicLoaderConfigLicenseKey: '', newRelicLoaderConfigApplicationID: '', }; diff --git a/src/environments/environment.test-osf.ts b/src/environments/environment.test-osf.ts index 7f50d1a6b..87f7a0c6f 100644 --- a/src/environments/environment.test-osf.ts +++ b/src/environments/environment.test-osf.ts @@ -28,7 +28,7 @@ export const environment = { newRelicInfoSa: 1, newRelicLoaderConfigAccountID: '', newRelicLoaderConfigTrustKey: '', - newRelicLoaderConfigAgengID: '', + newRelicLoaderConfigAgentID: '', newRelicLoaderConfigLicenseKey: '', newRelicLoaderConfigApplicationID: '', }; diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index 3d042aff7..08568d965 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -28,7 +28,7 @@ export const environment = { newRelicInfoSa: 1, newRelicLoaderConfigAccountID: '', newRelicLoaderConfigTrustKey: '', - newRelicLoaderConfigAgengID: '', + newRelicLoaderConfigAgentID: '', newRelicLoaderConfigLicenseKey: '', newRelicLoaderConfigApplicationID: '', }; diff --git a/src/environments/environment.ts b/src/environments/environment.ts index ff0f83afa..a7725102f 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -68,7 +68,7 @@ export const environment = { newRelicInfoSa: 1, newRelicLoaderConfigAccountID: '', newRelicLoaderConfigTrustKey: '', - newRelicLoaderConfigAgengID: '', + newRelicLoaderConfigAgentID: '', newRelicLoaderConfigLicenseKey: '', newRelicLoaderConfigApplicationID: '', }; diff --git a/src/index.html b/src/index.html index bf35c18ca..7c92fb628 100644 --- a/src/index.html +++ b/src/index.html @@ -8,6 +8,7 @@ + diff --git a/src/testing/mocks/environment.token.mock.ts b/src/testing/mocks/environment.token.mock.ts index 70f4d3ce1..02105aed2 100644 --- a/src/testing/mocks/environment.token.mock.ts +++ b/src/testing/mocks/environment.token.mock.ts @@ -44,7 +44,7 @@ export const EnvironmentTokenMock = { newRelicInfoSa: 1, newRelicLoaderConfigAccountID: '', newRelicLoaderConfigTrustKey: '', - newRelicLoaderConfigAgengID: '', + newRelicLoaderConfigAgentID: '', newRelicLoaderConfigLicenseKey: '', newRelicLoaderConfigApplicationID: '', }, diff --git a/src/testing/mocks/new-relic.mock.ts b/src/testing/mocks/new-relic.mock.ts index 6afb0ce1a..afec7c6c7 100644 --- a/src/testing/mocks/new-relic.mock.ts +++ b/src/testing/mocks/new-relic.mock.ts @@ -11,7 +11,7 @@ export const NEW_RELIC_CONFIG_MOCK = { newRelicInfoSa: 1, newRelicLoaderConfigAccountID: 'test-account-id', newRelicLoaderConfigTrustKey: 'test-trust-key', - newRelicLoaderConfigAgengID: 'test-agent-id', + newRelicLoaderConfigAgentID: 'test-agent-id', newRelicLoaderConfigLicenseKey: 'test-loader-license-key', newRelicLoaderConfigApplicationID: 'test-loader-app-id', };