diff --git a/angular.json b/angular.json
index a799f5f67..97bef37ee 100644
--- a/angular.json
+++ b/angular.json
@@ -48,7 +48,6 @@
"styles": [
"src/styles/styles.scss",
"node_modules/primeflex/primeflex.css",
- "node_modules/@fortawesome/fontawesome-free/css/all.min.css",
"node_modules/ngx-markdown-editor/assets/highlight.js/agate.min.css"
],
"stylePreprocessorOptions": {
@@ -74,7 +73,8 @@
"maximumError": "25kB"
}
],
- "outputHashing": "all"
+ "outputHashing": "all",
+ "optimization": true
},
"analyze-bundle": {
"sourceMap": true,
@@ -147,15 +147,15 @@
},
"development": {
"buildTarget": "osf:build:development",
- "hmr": false
+ "hmr": true
},
"docker": {
"buildTarget": "osf:build:docker",
- "hmr": false
+ "hmr": true
},
"staging": {
"buildTarget": "osf:build:staging",
- "hmr": false
+ "hmr": true
},
"test": {
"buildTarget": "osf:build:test",
diff --git a/jest.config.js b/jest.config.js
index ecdfa34e0..73c577307 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -50,10 +50,10 @@ module.exports = {
extensionsToTreatAsEsm: ['.ts'],
coverageThreshold: {
global: {
- branches: 24.1,
- functions: 28.73,
- lines: 56.52,
- statements: 56.82,
+ branches: 28.0,
+ functions: 32.0,
+ lines: 60.28,
+ statements: 60.77,
},
},
watchPathIgnorePatterns: [
diff --git a/package-lock.json b/package-lock.json
index 03cc9e29a..71fd20064 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -23,8 +23,6 @@
"@newrelic/browser-agent": "^1.301.0",
"@ngx-translate/core": "^16.0.4",
"@ngx-translate/http-loader": "^16.0.1",
- "@ngxs/devtools-plugin": "^19.0.0",
- "@ngxs/logger-plugin": "^19.0.0",
"@ngxs/store": "^19.0.0",
"@primeng/themes": "^19.0.9",
"@sentry/angular": "^10.10.0",
@@ -6470,42 +6468,6 @@
"@angular/core": ">=16"
}
},
- "node_modules/@ngxs/devtools-plugin": {
- "version": "19.0.0",
- "resolved": "https://registry.npmjs.org/@ngxs/devtools-plugin/-/devtools-plugin-19.0.0.tgz",
- "integrity": "sha512-z3O/G0fGeSc/mQRMBWwQ98W+kB0QpIMPZg2FLIubyZwWydouVatjhYck4IDLR/h5i6lq4McKioMK2tn/mXZqnQ==",
- "license": "MIT",
- "dependencies": {
- "tslib": "^2.3.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/ngxs"
- },
- "peerDependencies": {
- "@angular/core": ">=19.0.0 <20.0.0",
- "@ngxs/store": "^19.0.0 || ^19.0.0-dev",
- "rxjs": ">=6.5.5"
- }
- },
- "node_modules/@ngxs/logger-plugin": {
- "version": "19.0.0",
- "resolved": "https://registry.npmjs.org/@ngxs/logger-plugin/-/logger-plugin-19.0.0.tgz",
- "integrity": "sha512-qLGB4muiLlDDYVUOLgXalWYjd3DumMJDM/JCuyQD7xJl9wwixbMWVME1tnZ1e2/FFqKRPuL+54OnEtQ3SrSpOg==",
- "license": "MIT",
- "dependencies": {
- "tslib": "^2.3.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/ngxs"
- },
- "peerDependencies": {
- "@angular/core": ">=19.0.0 <20.0.0",
- "@ngxs/store": "^19.0.0 || ^19.0.0-dev",
- "rxjs": ">=6.5.5"
- }
- },
"node_modules/@ngxs/store": {
"version": "19.0.0",
"resolved": "https://registry.npmjs.org/@ngxs/store/-/store-19.0.0.tgz",
diff --git a/package.json b/package.json
index dc2585412..fff7a3540 100644
--- a/package.json
+++ b/package.json
@@ -47,8 +47,6 @@
"@newrelic/browser-agent": "^1.301.0",
"@ngx-translate/core": "^16.0.4",
"@ngx-translate/http-loader": "^16.0.1",
- "@ngxs/devtools-plugin": "^19.0.0",
- "@ngxs/logger-plugin": "^19.0.0",
"@ngxs/store": "^19.0.0",
"@primeng/themes": "^19.0.9",
"@sentry/angular": "^10.10.0",
diff --git a/setup-jest.ts b/setup-jest.ts
index dbb3d6b96..decf90381 100644
--- a/setup-jest.ts
+++ b/setup-jest.ts
@@ -31,8 +31,16 @@ class ResizeObserver {
// eslint-disable-next-line @typescript-eslint/no-empty-function
disconnect() {}
}
+
Object.defineProperty(window, 'ResizeObserver', {
writable: true,
configurable: true,
value: ResizeObserver,
});
+
+jest.mock('@newrelic/browser-agent/loaders/browser-agent', () => ({
+ BrowserAgent: jest.fn().mockImplementation(() => ({
+ start: jest.fn(),
+ stop: jest.fn(),
+ })),
+}));
diff --git a/src/app/app.config.ts b/src/app/app.config.ts
index 0f53a5184..45474cd08 100644
--- a/src/app/app.config.ts
+++ b/src/app/app.config.ts
@@ -1,4 +1,3 @@
-import { withNgxsReduxDevtoolsPlugin } from '@ngxs/devtools-plugin';
import { provideStore } from '@ngxs/store';
import { TranslateModule } from '@ngx-translate/core';
@@ -49,7 +48,7 @@ export const appConfig: ApplicationConfig = {
}),
provideHttpClient(withInterceptors([authInterceptor, viewOnlyInterceptor, errorInterceptor])),
provideRouter(routes, withInMemoryScrolling({ scrollPositionRestoration: 'top', anchorScrolling: 'enabled' })),
- provideStore(STATES, withNgxsReduxDevtoolsPlugin({ disabled: true })),
+ provideStore(STATES),
provideZoneChangeDetection({ eventCoalescing: true }),
SENTRY_PROVIDER,
],
diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts
index fefa6332e..0c2bb9259 100644
--- a/src/app/app.routes.ts
+++ b/src/app/app.routes.ts
@@ -3,17 +3,18 @@ import { provideStates } from '@ngxs/store';
import { Routes } from '@angular/router';
import { isFileGuard } from '@core/guards/is-file.guard';
-import { BookmarksState, ProjectsState } from '@shared/stores';
import { authGuard, redirectIfLoggedInGuard } from './core/guards';
import { isProjectGuard } from './core/guards/is-project.guard';
import { isRegistryGuard } from './core/guards/is-registry.guard';
-import { PreprintState } from './features/preprints/store/preprint';
+import { MyPreprintsState } from './features/preprints/store/my-preprints';
import { ProfileState } from './features/profile/store';
import { RegistriesState } from './features/registries/store';
import { LicensesHandlers, ProjectsHandlers, ProvidersHandlers } from './features/registries/store/handlers';
import { FilesHandlers } from './features/registries/store/handlers/files.handlers';
import { LicensesService } from './shared/services';
+import { BookmarksState } from './shared/stores/bookmarks';
+import { ProjectsState } from './shared/stores/projects';
export const routes: Routes = [
{
@@ -89,7 +90,7 @@ export const routes: Routes = [
import('@osf/features/preprints/pages/my-preprints/my-preprints.component').then(
(m) => m.MyPreprintsComponent
),
- providers: [provideStates([PreprintState])],
+ providers: [provideStates([MyPreprintsState])],
},
{
path: 'preprints',
diff --git a/src/app/core/components/nav-menu/nav-menu.component.spec.ts b/src/app/core/components/nav-menu/nav-menu.component.spec.ts
index 2ab3830a4..26a43c3aa 100644
--- a/src/app/core/components/nav-menu/nav-menu.component.spec.ts
+++ b/src/app/core/components/nav-menu/nav-menu.component.spec.ts
@@ -8,7 +8,7 @@ import { CustomMenuItem } from '@osf/core/models';
import { AuthService } from '@osf/core/services';
import { ProviderSelectors } from '@osf/core/store/provider/provider.selectors';
import { UserSelectors } from '@osf/core/store/user/user.selectors';
-import { CurrentResourceSelectors } from '@osf/shared/stores';
+import { CurrentResourceSelectors } from '@osf/shared/stores/current-resource';
import { NavMenuComponent } from './nav-menu.component';
diff --git a/src/app/core/components/nav-menu/nav-menu.component.ts b/src/app/core/components/nav-menu/nav-menu.component.ts
index 496ef7c72..6ac7d0bbd 100644
--- a/src/app/core/components/nav-menu/nav-menu.component.ts
+++ b/src/app/core/components/nav-menu/nav-menu.component.ts
@@ -20,7 +20,7 @@ import { IconComponent } from '@osf/shared/components';
import { CurrentResourceType, ReviewPermissions } from '@osf/shared/enums';
import { getViewOnlyParam } from '@osf/shared/helpers';
import { WrapFnPipe } from '@osf/shared/pipes';
-import { CurrentResourceSelectors, GetResourceDetails } from '@osf/shared/stores';
+import { CurrentResourceSelectors, GetResourceDetails } from '@osf/shared/stores/current-resource';
@Component({
selector: 'osf-nav-menu',
diff --git a/src/app/core/constants/ngxs-states.constant.ts b/src/app/core/constants/ngxs-states.constant.ts
index 409a3d258..08e715223 100644
--- a/src/app/core/constants/ngxs-states.constant.ts
+++ b/src/app/core/constants/ngxs-states.constant.ts
@@ -6,8 +6,11 @@ import { FilesState } from '@osf/features/files/store';
import { MetadataState } from '@osf/features/metadata/store';
import { ProjectOverviewState } from '@osf/features/project/overview/store';
import { RegistrationsState } from '@osf/features/project/registrations/store';
-import { AddonsState, CurrentResourceState, WikiState } from '@osf/shared/stores';
+import { AddonsState } from '@osf/shared/stores/addons';
import { BannersState } from '@osf/shared/stores/banners';
+import { ContributorsState } from '@osf/shared/stores/contributors';
+import { CurrentResourceState } from '@osf/shared/stores/current-resource';
+import { WikiState } from '@osf/shared/stores/wiki';
import { GlobalSearchState } from '@shared/stores/global-search';
import { InstitutionsState } from '@shared/stores/institutions';
import { InstitutionsSearchState } from '@shared/stores/institutions-search';
@@ -36,4 +39,5 @@ export const STATES = [
GlobalSearchState,
BannersState,
LinkedProjectsState,
+ ContributorsState,
];
diff --git a/src/app/core/guards/is-file.guard.ts b/src/app/core/guards/is-file.guard.ts
index d94cfbdc3..c1e543139 100644
--- a/src/app/core/guards/is-file.guard.ts
+++ b/src/app/core/guards/is-file.guard.ts
@@ -5,8 +5,9 @@ import { map, switchMap } from 'rxjs/operators';
import { inject } from '@angular/core';
import { CanMatchFn, Route, Router, UrlSegment } from '@angular/router';
+import { CurrentResourceSelectors, GetResource } from '@osf/shared/stores/current-resource';
+
import { CurrentResourceType } from '../../shared/enums';
-import { CurrentResourceSelectors, GetResource } from '../../shared/stores';
export const isFileGuard: CanMatchFn = (route: Route, segments: UrlSegment[]) => {
const store = inject(Store);
diff --git a/src/app/core/guards/is-project.guard.ts b/src/app/core/guards/is-project.guard.ts
index d1eb1d6fb..43fbc078a 100644
--- a/src/app/core/guards/is-project.guard.ts
+++ b/src/app/core/guards/is-project.guard.ts
@@ -6,8 +6,8 @@ import { inject } from '@angular/core';
import { CanMatchFn, Route, Router, UrlSegment } from '@angular/router';
import { UserSelectors } from '@core/store/user';
+import { CurrentResourceSelectors, GetResource } from '@osf/shared/stores/current-resource';
import { CurrentResourceType } from '@shared/enums';
-import { CurrentResourceSelectors, GetResource } from '@shared/stores';
export const isProjectGuard: CanMatchFn = (route: Route, segments: UrlSegment[]) => {
const store = inject(Store);
diff --git a/src/app/core/guards/is-registry.guard.ts b/src/app/core/guards/is-registry.guard.ts
index 63dfe4a28..ca713439e 100644
--- a/src/app/core/guards/is-registry.guard.ts
+++ b/src/app/core/guards/is-registry.guard.ts
@@ -7,7 +7,7 @@ import { CanMatchFn, Route, Router, UrlSegment } from '@angular/router';
import { UserSelectors } from '@core/store/user';
import { CurrentResourceType } from '@shared/enums';
-import { CurrentResourceSelectors, GetResource } from '@shared/stores';
+import { CurrentResourceSelectors, GetResource } from '@shared/stores/current-resource';
export const isRegistryGuard: CanMatchFn = (route: Route, segments: UrlSegment[]) => {
const store = inject(Store);
diff --git a/src/app/core/provider/application.initialization.provider.spec.ts b/src/app/core/provider/application.initialization.provider.spec.ts
index 12b73f067..feaa49742 100644
--- a/src/app/core/provider/application.initialization.provider.spec.ts
+++ b/src/app/core/provider/application.initialization.provider.spec.ts
@@ -7,7 +7,9 @@ import { OSFConfigService } from '@core/services/osf-config.service';
import { initializeApplication } from './application.initialization.provider';
import { ENVIRONMENT } from './environment.provider';
+import { BrowserAgent } from '@newrelic/browser-agent/loaders/browser-agent';
import * as Sentry from '@sentry/angular';
+import { NEW_RELIC_CONFIG_MOCK } from '@testing/mocks/new-relic.mock';
import { OSFTestingModule } from '@testing/osf.testing.module';
import { GoogleTagManagerConfiguration } from 'angular-google-tag-manager';
@@ -92,4 +94,28 @@ describe('Provider: sentry', () => {
expect(googleTagManagerConfigurationMock.set).not.toHaveBeenCalled();
expect(httpMock.verify).toBeTruthy();
});
+
+ it('should initialize New Relic if enabled', async () => {
+ const environment = TestBed.inject(ENVIRONMENT);
+ Object.assign(environment, NEW_RELIC_CONFIG_MOCK);
+
+ await runInInjectionContext(TestBed, async () => {
+ await initializeApplication()();
+ });
+
+ expect(BrowserAgent).toHaveBeenCalledTimes(1);
+ expect(httpMock.verify).toBeTruthy();
+ });
+
+ it('should not initialize New Relic if disabled', async () => {
+ const environment = TestBed.inject(ENVIRONMENT);
+ environment.newRelicEnabled = false;
+
+ await runInInjectionContext(TestBed, async () => {
+ await initializeApplication()();
+ });
+
+ expect(BrowserAgent).not.toHaveBeenCalled();
+ expect(httpMock.verify).toBeTruthy();
+ });
});
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/core/store/provider/provider.selectors.ts b/src/app/core/store/provider/provider.selectors.ts
index c63f3b537..1da68985e 100644
--- a/src/app/core/store/provider/provider.selectors.ts
+++ b/src/app/core/store/provider/provider.selectors.ts
@@ -1,5 +1,6 @@
import { Selector } from '@ngxs/store';
+import { ReviewPermissions } from '@osf/shared/enums';
import { ProviderShortInfoModel } from '@osf/shared/models';
import { ProviderStateModel } from './provider.model';
@@ -10,4 +11,12 @@ export class ProviderSelectors {
static getCurrentProvider(state: ProviderStateModel): ProviderShortInfoModel | null {
return state.currentProvider;
}
+
+ @Selector([ProviderState])
+ static hasAdminAccess(state: ProviderStateModel): boolean {
+ return (
+ state.currentProvider?.permissions?.some((permission) => permission === ReviewPermissions.SetUpModeration) ||
+ false
+ );
+ }
}
diff --git a/src/app/features/analytics/components/view-duplicates/view-duplicates.component.spec.ts b/src/app/features/analytics/components/view-duplicates/view-duplicates.component.spec.ts
index dbec46a29..fbdec8174 100644
--- a/src/app/features/analytics/components/view-duplicates/view-duplicates.component.spec.ts
+++ b/src/app/features/analytics/components/view-duplicates/view-duplicates.component.spec.ts
@@ -10,7 +10,7 @@ import { ActivatedRoute, Router } from '@angular/router';
import { ProjectOverviewSelectors } from '@osf/features/project/overview/store';
import { RegistryOverviewSelectors } from '@osf/features/registry/store/registry-overview';
import { ResourceType } from '@osf/shared/enums';
-import { DuplicatesSelectors } from '@osf/shared/stores';
+import { DuplicatesSelectors } from '@osf/shared/stores/duplicates';
import {
ContributorsListComponent,
CustomPaginatorComponent,
diff --git a/src/app/features/analytics/components/view-duplicates/view-duplicates.component.ts b/src/app/features/analytics/components/view-duplicates/view-duplicates.component.ts
index 176e26edd..519453b58 100644
--- a/src/app/features/analytics/components/view-duplicates/view-duplicates.component.ts
+++ b/src/app/features/analytics/components/view-duplicates/view-duplicates.component.ts
@@ -41,7 +41,8 @@ import {
import { ResourceType, UserPermissions } from '@osf/shared/enums';
import { BaseNodeModel, ToolbarResource } from '@osf/shared/models';
import { CustomDialogService, LoaderService } from '@osf/shared/services';
-import { ClearDuplicates, DuplicatesSelectors, GetAllDuplicates, GetResourceWithChildren } from '@osf/shared/stores';
+import { GetResourceWithChildren } from '@osf/shared/stores/current-resource';
+import { ClearDuplicates, DuplicatesSelectors, GetAllDuplicates } from '@osf/shared/stores/duplicates';
@Component({
selector: 'osf-view-duplicates',
diff --git a/src/app/features/collections/collections.routes.ts b/src/app/features/collections/collections.routes.ts
index cb95bfe26..bbdeac48a 100644
--- a/src/app/features/collections/collections.routes.ts
+++ b/src/app/features/collections/collections.routes.ts
@@ -6,15 +6,12 @@ import { authGuard } from '@osf/core/guards';
import { AddToCollectionState } from '@osf/features/collections/store/add-to-collection';
import { CollectionsModerationState } from '@osf/features/moderation/store/collections-moderation';
import { ConfirmLeavingGuard } from '@shared/guards';
-import {
- BookmarksState,
- CitationsState,
- ContributorsState,
- NodeLinksState,
- ProjectsState,
- SubjectsState,
-} from '@shared/stores';
+import { BookmarksState } from '@shared/stores/bookmarks';
+import { CitationsState } from '@shared/stores/citations';
import { CollectionsState } from '@shared/stores/collections';
+import { NodeLinksState } from '@shared/stores/node-links';
+import { ProjectsState } from '@shared/stores/projects';
+import { SubjectsState } from '@shared/stores/subjects';
export const collectionsRoutes: Routes = [
{
@@ -47,7 +44,7 @@ export const collectionsRoutes: Routes = [
import('@osf/features/collections/components/add-to-collection/add-to-collection.component').then(
(mod) => mod.AddToCollectionComponent
),
- providers: [provideStates([ProjectsState, CollectionsState, AddToCollectionState, ContributorsState])],
+ providers: [provideStates([ProjectsState, CollectionsState, AddToCollectionState])],
canActivate: [authGuard],
canDeactivate: [ConfirmLeavingGuard],
},
diff --git a/src/app/features/collections/components/add-to-collection/add-to-collection.component.html b/src/app/features/collections/components/add-to-collection/add-to-collection.component.html
index 204620fa5..e60b9c227 100644
--- a/src/app/features/collections/components/add-to-collection/add-to-collection.component.html
+++ b/src/app/features/collections/components/add-to-collection/add-to-collection.component.html
@@ -32,6 +32,7 @@
{{ collectionProvider()?
/>
{{ 'collections.addToCollection.projectContributors' | translate }}
[(contributors)]="projectContributors"
[tableParams]="tableParams()"
[isLoading]="isContributorsLoading()"
+ [isLoadingMore]="isLoadingMore()"
(remove)="handleRemoveContributor($event)"
+ (loadMore)="loadMoreContributors()"
>
diff --git a/src/app/features/collections/components/add-to-collection/project-contributors-step/project-contributors-step.component.ts b/src/app/features/collections/components/add-to-collection/project-contributors-step/project-contributors-step.component.ts
index f6518e2d4..d532a621c 100644
--- a/src/app/features/collections/components/add-to-collection/project-contributors-step/project-contributors-step.component.ts
+++ b/src/app/features/collections/components/add-to-collection/project-contributors-step/project-contributors-step.component.ts
@@ -40,8 +40,9 @@ import {
BulkUpdateContributors,
ContributorsSelectors,
DeleteContributor,
- ProjectsSelectors,
-} from '@osf/shared/stores';
+ LoadMoreContributors,
+} from '@osf/shared/stores/contributors';
+import { ProjectsSelectors } from '@osf/shared/stores/projects';
@Component({
selector: 'osf-project-contributors-step',
@@ -61,20 +62,26 @@ export class ProjectContributorsStepComponent {
readonly contributorsTotalCount = select(ContributorsSelectors.getContributorsTotalCount);
readonly selectedProject = select(ProjectsSelectors.getSelectedProject);
readonly currentUser = select(UserSelectors.getCurrentUser);
+ isLoadingMore = select(ContributorsSelectors.isContributorsLoadingMore);
private initialContributors = select(ContributorsSelectors.getContributors);
readonly projectContributors = signal
([]);
+ pageSize = select(ContributorsSelectors.getContributorsPageSize);
readonly tableParams = computed(() => ({
...DEFAULT_TABLE_PARAMS,
totalRecords: this.contributorsTotalCount(),
- paginator: this.contributorsTotalCount() > DEFAULT_TABLE_PARAMS.rows,
+ paginator: false,
+ scrollable: true,
+ firstRowIndex: 0,
+ rows: this.pageSize(),
}));
stepperActiveValue = input.required();
targetStepValue = input.required();
isDisabled = input.required();
isProjectMetadataSaved = input(false);
+ projectId = input();
stepChange = output();
contributorsSaved = output();
@@ -84,6 +91,7 @@ export class ProjectContributorsStepComponent {
bulkAddContributors: BulkAddContributors,
bulkUpdateContributors: BulkUpdateContributors,
deleteContributor: DeleteContributor,
+ loadMoreContributors: LoadMoreContributors,
});
constructor() {
@@ -150,14 +158,15 @@ export class ProjectContributorsStepComponent {
this.stepChange.emit(this.targetStepValue());
}
- private openAddContributorDialog() {
- const addedContributorIds = this.projectContributors().map((x) => x.userId);
+ loadMoreContributors(): void {
+ this.actions.loadMoreContributors(this.projectId(), ResourceType.Project);
+ }
+ private openAddContributorDialog() {
this.customDialogService
.open(AddContributorDialogComponent, {
header: 'project.contributors.addDialog.addRegisteredContributor',
width: '448px',
- data: addedContributorIds,
})
.onClose.pipe(
filter((res: ContributorDialogAddModel) => !!res),
diff --git a/src/app/features/collections/components/add-to-collection/project-metadata-step/project-metadata-step.component.html b/src/app/features/collections/components/add-to-collection/project-metadata-step/project-metadata-step.component.html
index a9c4d29d1..e7193524d 100644
--- a/src/app/features/collections/components/add-to-collection/project-metadata-step/project-metadata-step.component.html
+++ b/src/app/features/collections/components/add-to-collection/project-metadata-step/project-metadata-step.component.html
@@ -20,7 +20,7 @@ {{ 'collections.addToCollection.resourceMetadata' | translate }}
-
{{ 'collections.addToCollection.form.license' | translate }}
+
{{ 'common.labels.license' | translate }}
{{ projectLicense()?.name || 'collections.addToCollection.noLicense' | translate }}
diff --git a/src/app/features/collections/components/add-to-collection/project-metadata-step/project-metadata-step.component.ts b/src/app/features/collections/components/add-to-collection/project-metadata-step/project-metadata-step.component.ts
index a9df80864..47c2a3862 100644
--- a/src/app/features/collections/components/add-to-collection/project-metadata-step/project-metadata-step.component.ts
+++ b/src/app/features/collections/components/add-to-collection/project-metadata-step/project-metadata-step.component.ts
@@ -40,7 +40,8 @@ import { LicenseModel } from '@shared/models';
import { ProjectModel } from '@shared/models/projects';
import { InterpolatePipe } from '@shared/pipes';
import { ToastService } from '@shared/services';
-import { ClearProjects, GetAllContributors, UpdateProjectMetadata } from '@shared/stores';
+import { GetAllContributors } from '@shared/stores/contributors';
+import { ClearProjects, UpdateProjectMetadata } from '@shared/stores/projects';
import { ProjectsSelectors } from '@shared/stores/projects/projects.selectors';
@Component({
diff --git a/src/app/features/collections/components/add-to-collection/select-project-step/select-project-step.component.spec.ts b/src/app/features/collections/components/add-to-collection/select-project-step/select-project-step.component.spec.ts
index dcd0917d5..237d3c88d 100644
--- a/src/app/features/collections/components/add-to-collection/select-project-step/select-project-step.component.spec.ts
+++ b/src/app/features/collections/components/add-to-collection/select-project-step/select-project-step.component.spec.ts
@@ -6,7 +6,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ProjectSelectorComponent } from '@shared/components';
import { ToastService } from '@shared/services';
-import { CollectionsSelectors } from '@shared/stores';
+import { CollectionsSelectors } from '@shared/stores/collections';
import { ProjectsSelectors } from '@shared/stores/projects/projects.selectors';
import { SelectProjectStepComponent } from './select-project-step.component';
diff --git a/src/app/features/collections/components/add-to-collection/select-project-step/select-project-step.component.ts b/src/app/features/collections/components/add-to-collection/select-project-step/select-project-step.component.ts
index 78d9953c3..bef7c72c5 100644
--- a/src/app/features/collections/components/add-to-collection/select-project-step/select-project-step.component.ts
+++ b/src/app/features/collections/components/add-to-collection/select-project-step/select-project-step.component.ts
@@ -8,7 +8,7 @@ import { Step, StepItem, StepPanel } from 'primeng/stepper';
import { ChangeDetectionStrategy, Component, computed, input, output, signal } from '@angular/core';
import { AddToCollectionSteps } from '@osf/features/collections/enums';
-import { SetSelectedProject } from '@osf/shared/stores';
+import { SetSelectedProject } from '@osf/shared/stores/projects';
import { ProjectSelectorComponent } from '@shared/components';
import { ProjectModel } from '@shared/models/projects';
import { CollectionsSelectors, GetUserCollectionSubmissions } from '@shared/stores/collections';
diff --git a/src/app/features/collections/components/collections-discover/collections-discover.component.spec.ts b/src/app/features/collections/components/collections-discover/collections-discover.component.spec.ts
index 3f2ad5eeb..27c54b634 100644
--- a/src/app/features/collections/components/collections-discover/collections-discover.component.spec.ts
+++ b/src/app/features/collections/components/collections-discover/collections-discover.component.spec.ts
@@ -7,7 +7,7 @@ import { SENTRY_TOKEN } from '@core/provider/sentry.provider';
import { CollectionsMainContentComponent } from '@osf/features/collections/components';
import { LoadingSpinnerComponent, SearchInputComponent } from '@shared/components';
import { CustomDialogService, ToastService } from '@shared/services';
-import { CollectionsSelectors } from '@shared/stores';
+import { CollectionsSelectors } from '@shared/stores/collections';
import { CollectionsDiscoverComponent } from './collections-discover.component';
diff --git a/src/app/features/collections/components/collections-discover/collections-discover.component.ts b/src/app/features/collections/components/collections-discover/collections-discover.component.ts
index 77a940823..80a7bf084 100644
--- a/src/app/features/collections/components/collections-discover/collections-discover.component.ts
+++ b/src/app/features/collections/components/collections-discover/collections-discover.component.ts
@@ -25,7 +25,7 @@ import {
SearchCollectionSubmissions,
SetPageNumber,
SetSearchValue,
-} from '@osf/shared/stores';
+} from '@osf/shared/stores/collections';
import { CollectionsQuerySyncService } from '../../services';
import { CollectionsHelpDialogComponent } from '../collections-help-dialog/collections-help-dialog.component';
diff --git a/src/app/features/contributors/components/create-view-link-dialog/create-view-link-dialog.component.spec.ts b/src/app/features/contributors/components/create-view-link-dialog/create-view-link-dialog.component.spec.ts
index e7d9849a2..688c92f82 100644
--- a/src/app/features/contributors/components/create-view-link-dialog/create-view-link-dialog.component.spec.ts
+++ b/src/app/features/contributors/components/create-view-link-dialog/create-view-link-dialog.component.spec.ts
@@ -4,7 +4,7 @@ import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { CurrentResourceSelectors } from '@osf/shared/stores';
+import { CurrentResourceSelectors } from '@osf/shared/stores/current-resource';
import { CreateViewLinkDialogComponent } from './create-view-link-dialog.component';
diff --git a/src/app/features/contributors/components/create-view-link-dialog/create-view-link-dialog.component.ts b/src/app/features/contributors/components/create-view-link-dialog/create-view-link-dialog.component.ts
index f6c0c68b8..e3c1b4d75 100644
--- a/src/app/features/contributors/components/create-view-link-dialog/create-view-link-dialog.component.ts
+++ b/src/app/features/contributors/components/create-view-link-dialog/create-view-link-dialog.component.ts
@@ -13,7 +13,7 @@ import { ComponentCheckboxItemComponent, LoadingSpinnerComponent, TextInputCompo
import { InputLimits } from '@osf/shared/constants';
import { CustomValidators } from '@osf/shared/helpers';
import { ComponentCheckboxItemModel } from '@osf/shared/models';
-import { CurrentResourceSelectors, GetResourceWithChildren } from '@osf/shared/stores';
+import { CurrentResourceSelectors, GetResourceWithChildren } from '@osf/shared/stores/current-resource';
import { ResourceInfoModel, ViewOnlyLinkComponentItem } from '../../models';
diff --git a/src/app/features/contributors/contributors.component.html b/src/app/features/contributors/contributors.component.html
index dd3d74d45..3a5785bee 100644
--- a/src/app/features/contributors/contributors.component.html
+++ b/src/app/features/contributors/contributors.component.html
@@ -63,6 +63,7 @@
{{ 'navigation.contributors' | translate }
class="w-full"
[(contributors)]="contributors"
[isLoading]="isContributorsLoading()"
+ [isLoadingMore]="isLoadingMore()"
[tableParams]="tableParams()"
[hasAdminAccess]="hasAdminAccess()"
[currentUserId]="currentUser()?.id"
@@ -70,7 +71,7 @@ {{ 'navigation.contributors' | translate }
[showInfo]="true"
[resourceType]="resourceType()"
(remove)="removeContributor($event)"
- (pageChanged)="pageChanged($event)"
+ (loadMore)="loadMoreContributors()"
>
@if (hasChanges) {
diff --git a/src/app/features/contributors/contributors.component.spec.ts b/src/app/features/contributors/contributors.component.spec.ts
index 34f3afa23..4831001a0 100644
--- a/src/app/features/contributors/contributors.component.spec.ts
+++ b/src/app/features/contributors/contributors.component.spec.ts
@@ -10,7 +10,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ContributorPermission } from '@shared/enums';
import { ContributorModel } from '@shared/models';
import { CustomConfirmationService } from '@shared/services';
-import { ContributorsSelectors, CurrentResourceSelectors, ViewOnlyLinkSelectors } from '@shared/stores';
+import { ContributorsSelectors } from '@shared/stores/contributors';
+import { CurrentResourceSelectors } from '@shared/stores/current-resource';
+import { ViewOnlyLinkSelectors } from '@shared/stores/view-only-links';
import { ContributorsComponent } from './contributors.component';
diff --git a/src/app/features/contributors/contributors.component.ts b/src/app/features/contributors/contributors.component.ts
index 25754e14a..5f620123e 100644
--- a/src/app/features/contributors/contributors.component.ts
+++ b/src/app/features/contributors/contributors.component.ts
@@ -4,7 +4,7 @@ import { TranslatePipe } from '@ngx-translate/core';
import { Button } from 'primeng/button';
import { Select } from 'primeng/select';
-import { TableModule, TablePageEvent } from 'primeng/table';
+import { TableModule } from 'primeng/table';
import { debounceTime, distinctUntilChanged, filter, map, of, switchMap } from 'rxjs';
@@ -15,6 +15,7 @@ import {
DestroyRef,
effect,
inject,
+ OnDestroy,
OnInit,
signal,
} from '@angular/core';
@@ -49,23 +50,30 @@ import {
AcceptRequestAccess,
AddContributor,
BulkAddContributors,
+ BulkAddContributorsFromParentProject,
BulkUpdateContributors,
ContributorsSelectors,
- CreateViewOnlyLink,
- CurrentResourceSelectors,
DeleteContributor,
- DeleteViewOnlyLink,
- FetchViewOnlyLinks,
GetAllContributors,
GetRequestAccessContributors,
- GetResourceDetails,
- GetResourceWithChildren,
+ LoadMoreContributors,
RejectRequestAccess,
+ ResetContributorsState,
UpdateBibliographyFilter,
UpdateContributorsSearchValue,
UpdatePermissionFilter,
+} from '@osf/shared/stores/contributors';
+import {
+ CurrentResourceSelectors,
+ GetResourceDetails,
+ GetResourceWithChildren,
+} from '@osf/shared/stores/current-resource';
+import {
+ CreateViewOnlyLink,
+ DeleteViewOnlyLink,
+ FetchViewOnlyLinks,
ViewOnlyLinkSelectors,
-} from '@osf/shared/stores';
+} from '@osf/shared/stores/view-only-links';
import { CreateViewLinkDialogComponent } from './components';
import { ResourceInfoModel } from './models';
@@ -87,7 +95,7 @@ import { ResourceInfoModel } from './models';
styleUrl: './contributors.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class ContributorsComponent implements OnInit {
+export class ContributorsComponent implements OnInit, OnDestroy {
searchControl = new FormControl('');
readonly destroyRef = inject(DestroyRef);
@@ -123,14 +131,15 @@ export class ContributorsComponent implements OnInit {
readonly hasAdminAccess = select(CurrentResourceSelectors.hasResourceAdminAccess);
readonly resourceAccessRequestEnabled = select(CurrentResourceSelectors.resourceAccessRequestEnabled);
readonly currentUser = select(UserSelectors.getCurrentUser);
- page = select(ContributorsSelectors.getContributorsPageNumber);
pageSize = select(ContributorsSelectors.getContributorsPageSize);
+ isLoadingMore = select(ContributorsSelectors.isContributorsLoadingMore);
readonly tableParams = computed(() => ({
...DEFAULT_TABLE_PARAMS,
totalRecords: this.contributorsTotalCount(),
- paginator: this.contributorsTotalCount() > DEFAULT_TABLE_PARAMS.rows,
- firstRowIndex: (this.page() - 1) * this.pageSize(),
+ paginator: false,
+ scrollable: true,
+ firstRowIndex: 0,
rows: this.pageSize(),
}));
@@ -153,12 +162,14 @@ export class ContributorsComponent implements OnInit {
getViewOnlyLinks: FetchViewOnlyLinks,
getResourceDetails: GetResourceDetails,
getContributors: GetAllContributors,
+ loadMoreContributors: LoadMoreContributors,
updateSearchValue: UpdateContributorsSearchValue,
updatePermissionFilter: UpdatePermissionFilter,
updateBibliographyFilter: UpdateBibliographyFilter,
deleteContributor: DeleteContributor,
bulkUpdateContributors: BulkUpdateContributors,
bulkAddContributors: BulkAddContributors,
+ bulkAddContributorsFromParentProject: BulkAddContributorsFromParentProject,
addContributor: AddContributor,
createViewOnlyLink: CreateViewOnlyLink,
deleteViewOnlyLink: DeleteViewOnlyLink,
@@ -166,6 +177,7 @@ export class ContributorsComponent implements OnInit {
acceptRequestAccess: AcceptRequestAccess,
rejectRequestAccess: RejectRequestAccess,
getResourceWithChildren: GetResourceWithChildren,
+ resetContributorsState: ResetContributorsState,
});
get hasChanges(): boolean {
@@ -187,6 +199,10 @@ export class ContributorsComponent implements OnInit {
this.setSearchSubscription();
}
+ ngOnDestroy(): void {
+ this.actions.resetContributorsState();
+ }
+
private setSearchSubscription() {
this.searchControl.valueChanges
.pipe(debounceTime(500), distinctUntilChanged(), takeUntilDestroyed(this.destroyRef))
@@ -245,27 +261,32 @@ export class ContributorsComponent implements OnInit {
}
openAddContributorDialog() {
- const addedContributorIds = this.initialContributors().map((x) => x.userId);
- const rootParentId = this.resourceDetails().rootParentId ?? this.resourceId();
+ const resourceDetails = this.resourceDetails();
+ const resourceId = this.resourceId();
+ const rootParentId = resourceDetails.rootParentId ?? resourceId;
this.loaderService.show();
this.actions
- .getResourceWithChildren(rootParentId, this.resourceId(), this.resourceType())
+ .getResourceWithChildren(rootParentId, resourceId, this.resourceType())
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => {
this.loaderService.hide();
- const components = this.mapNodesToComponentCheckboxItems(this.resourceChildren(), this.resourceId());
+ const components = this.mapNodesToComponentCheckboxItems(this.resourceChildren(), resourceId);
this.customDialogService
.open(AddContributorDialogComponent, {
header: 'project.contributors.addDialog.addRegisteredContributor',
width: '448px',
data: {
- addedContributorIds,
components,
- resourceName: this.resourceDetails().title,
+ resourceName: resourceDetails.title,
+ parentResourceName: resourceDetails.parent?.title,
+ allowAddingContributorsFromParentProject:
+ this.resourceType() === ResourceType.Project &&
+ resourceDetails.rootParentId &&
+ resourceDetails.rootParentId !== resourceId,
},
})
.onClose.pipe(
@@ -273,7 +294,9 @@ export class ContributorsComponent implements OnInit {
takeUntilDestroyed(this.destroyRef)
)
.subscribe((res: ContributorDialogAddModel) => {
- if (res.type === AddContributorType.Unregistered) {
+ if (res.type === AddContributorType.ParentProject) {
+ this.addContributorsFromParentProjectToComponents();
+ } else if (res.type === AddContributorType.Unregistered) {
this.openAddUnregisteredContributorDialog();
} else {
this.addContributorsToComponents(res);
@@ -303,6 +326,13 @@ export class ContributorsComponent implements OnInit {
.subscribe(() => this.toastService.showSuccess('project.contributors.toastMessages.multipleAddSuccessMessage'));
}
+ private addContributorsFromParentProjectToComponents(): void {
+ this.actions
+ .bulkAddContributorsFromParentProject(this.resourceId(), this.resourceType())
+ .pipe(takeUntilDestroyed(this.destroyRef))
+ .subscribe(() => this.toastService.showSuccess('project.contributors.toastMessages.multipleAddSuccessMessage'));
+ }
+
openAddUnregisteredContributorDialog() {
this.customDialogService
.open(AddUnregisteredContributorDialogComponent, {
@@ -383,11 +413,8 @@ export class ContributorsComponent implements OnInit {
});
}
- pageChanged(event: TablePageEvent) {
- const page = Math.floor(event.first / event.rows) + 1;
- const pageSize = event.rows;
-
- this.actions.getContributors(this.resourceId(), this.resourceType(), page, pageSize);
+ loadMoreContributors(): void {
+ this.actions.loadMoreContributors(this.resourceId(), this.resourceType());
}
createViewLink() {
diff --git a/src/app/features/files/components/move-file-dialog/move-file-dialog.component.spec.ts b/src/app/features/files/components/move-file-dialog/move-file-dialog.component.spec.ts
index e329e8643..97b2e0ca8 100644
--- a/src/app/features/files/components/move-file-dialog/move-file-dialog.component.spec.ts
+++ b/src/app/features/files/components/move-file-dialog/move-file-dialog.component.spec.ts
@@ -7,7 +7,7 @@ import { signal } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CustomConfirmationService, FilesService, ToastService } from '@osf/shared/services';
-import { CurrentResourceSelectors } from '@shared/stores';
+import { CurrentResourceSelectors } from '@shared/stores/current-resource';
import { FilesSelectors } from '../../store';
diff --git a/src/app/features/files/components/move-file-dialog/move-file-dialog.component.ts b/src/app/features/files/components/move-file-dialog/move-file-dialog.component.ts
index 6b7016433..40c0f12bc 100644
--- a/src/app/features/files/components/move-file-dialog/move-file-dialog.component.ts
+++ b/src/app/features/files/components/move-file-dialog/move-file-dialog.component.ts
@@ -23,7 +23,11 @@ import {
import { FileKind, ResourceType, SupportedFeature } from '@osf/shared/enums';
import { FilesMapper } from '@osf/shared/mappers/files/files.mapper';
import { FileFolderModel, FileModel } from '@osf/shared/models';
-import { CurrentResourceSelectors, GetResourceDetails, GetResourceWithChildren } from '@osf/shared/stores';
+import {
+ CurrentResourceSelectors,
+ GetResourceDetails,
+ GetResourceWithChildren,
+} from '@osf/shared/stores/current-resource';
import { FileSelectDestinationComponent, IconComponent, LoadingSpinnerComponent } from '@shared/components';
import { CustomConfirmationService, FilesService, ToastService } from '@shared/services';
@@ -182,6 +186,8 @@ export class MoveFileDialogComponent {
}
this.isFilesUpdating.set(true);
+ const headerKey = this.isMoveAction ? 'files.dialogs.moveFile.movingHeader' : 'files.dialogs.moveFile.copingHeader';
+ this.config.header = this.translateService.instant(headerKey);
const action = this.config.data.action;
const files: FileModel[] = this.config.data.files;
const totalFiles = files.length;
@@ -209,6 +215,7 @@ export class MoveFileDialogComponent {
this.openReplaceMoveDialog(conflictFiles, path, action);
} else {
this.showToast(action);
+ this.config.header = this.translateService.instant('files.dialogs.moveFile.title');
this.completeMove();
}
}
diff --git a/src/app/features/files/pages/files/files.component.spec.ts b/src/app/features/files/pages/files/files.component.spec.ts
index 1c2955d04..7abc3d793 100644
--- a/src/app/features/files/pages/files/files.component.spec.ts
+++ b/src/app/features/files/pages/files/files.component.spec.ts
@@ -21,7 +21,7 @@ import {
ViewOnlyLinkMessageComponent,
} from '@osf/shared/components';
import { CustomConfirmationService, FilesService } from '@osf/shared/services';
-import { CurrentResourceSelectors } from '@osf/shared/stores';
+import { CurrentResourceSelectors } from '@osf/shared/stores/current-resource';
import { GoogleFilePickerComponent } from '@shared/components/google-file-picker/google-file-picker.component';
import { FilesSelectors } from '../../store';
diff --git a/src/app/features/files/pages/files/files.component.ts b/src/app/features/files/pages/files/files.component.ts
index 431277fc1..28bde3f50 100644
--- a/src/app/features/files/pages/files/files.component.ts
+++ b/src/app/features/files/pages/files/files.component.ts
@@ -45,7 +45,7 @@ import {
import { ALL_SORT_OPTIONS, FILE_SIZE_LIMIT } from '@osf/shared/constants';
import { FileMenuType, ResourceType, SupportedFeature, UserPermissions } from '@osf/shared/enums';
import { getViewOnlyParamFromUrl, hasViewOnlyParam } from '@osf/shared/helpers';
-import { CurrentResourceSelectors, GetResourceDetails } from '@osf/shared/stores';
+import { CurrentResourceSelectors, GetResourceDetails } from '@osf/shared/stores/current-resource';
import {
FilesTreeComponent,
FileUploadDialogComponent,
diff --git a/src/app/features/home/pages/dashboard/dashboard.component.spec.ts b/src/app/features/home/pages/dashboard/dashboard.component.spec.ts
index 7a0213dac..005ddcf6d 100644
--- a/src/app/features/home/pages/dashboard/dashboard.component.spec.ts
+++ b/src/app/features/home/pages/dashboard/dashboard.component.spec.ts
@@ -13,7 +13,7 @@ import {
SearchInputComponent,
SubHeaderComponent,
} from '@shared/components';
-import { MyResourcesSelectors } from '@shared/stores';
+import { MyResourcesSelectors } from '@shared/stores/my-resources';
import { DashboardComponent } from './dashboard.component';
diff --git a/src/app/features/home/pages/dashboard/dashboard.component.ts b/src/app/features/home/pages/dashboard/dashboard.component.ts
index 54aa5bba9..8a04adc12 100644
--- a/src/app/features/home/pages/dashboard/dashboard.component.ts
+++ b/src/app/features/home/pages/dashboard/dashboard.component.ts
@@ -26,7 +26,7 @@ import { DEFAULT_TABLE_PARAMS } from '@osf/shared/constants';
import { SortOrder } from '@osf/shared/enums';
import { MyResourcesItem, MyResourcesSearchFilters, TableParameters } from '@osf/shared/models';
import { CustomDialogService, ProjectRedirectDialogService } from '@osf/shared/services';
-import { ClearMyResources, GetMyProjects, MyResourcesSelectors } from '@osf/shared/stores';
+import { ClearMyResources, GetMyProjects, MyResourcesSelectors } from '@osf/shared/stores/my-resources';
@Component({
selector: 'osf-dashboard',
diff --git a/src/app/features/institutions/pages/institutions-list/institutions-list.component.ts b/src/app/features/institutions/pages/institutions-list/institutions-list.component.ts
index 17bbdac17..4b1fefc08 100644
--- a/src/app/features/institutions/pages/institutions-list/institutions-list.component.ts
+++ b/src/app/features/institutions/pages/institutions-list/institutions-list.component.ts
@@ -12,7 +12,7 @@ import { ActivatedRoute, Router, RouterLink } from '@angular/router';
import { ScheduledBannerComponent } from '@core/components/osf-banners/scheduled-banner/scheduled-banner.component';
import { LoadingSpinnerComponent, SearchInputComponent, SubHeaderComponent } from '@osf/shared/components';
-import { FetchInstitutions, InstitutionsSelectors } from '@osf/shared/stores';
+import { FetchInstitutions, InstitutionsSelectors } from '@osf/shared/stores/institutions';
@Component({
selector: 'osf-institutions-list',
diff --git a/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.ts b/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.ts
index d5af60476..16a562e69 100644
--- a/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.ts
+++ b/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.ts
@@ -24,16 +24,18 @@ import { toSignal } from '@angular/core/rxjs-interop';
import { ActivatedRoute } from '@angular/router';
import { ENVIRONMENT } from '@core/provider/environment.provider';
-import { CEDAR_CONFIG, CEDAR_VIEWER_CONFIG } from '@osf/features/metadata/constants';
-import { CedarMetadataHelper } from '@osf/features/metadata/helpers';
+
+import 'cedar-artifact-viewer';
+import 'cedar-embeddable-editor';
+
+import { CEDAR_CONFIG, CEDAR_VIEWER_CONFIG } from '../../constants';
+import { CedarMetadataHelper } from '../../helpers';
import {
CedarEditorElement,
CedarMetadataDataTemplateJsonApi,
CedarMetadataRecordData,
CedarRecordDataBinding,
-} from '@osf/features/metadata/models';
-
-import 'cedar-artifact-viewer';
+} from '../../models';
@Component({
selector: 'osf-cedar-template-form',
diff --git a/src/app/features/metadata/components/metadata-contributors/metadata-contributors.component.html b/src/app/features/metadata/components/metadata-contributors/metadata-contributors.component.html
index e7956426c..3ec6df984 100644
--- a/src/app/features/metadata/components/metadata-contributors/metadata-contributors.component.html
+++ b/src/app/features/metadata/components/metadata-contributors/metadata-contributors.component.html
@@ -12,9 +12,14 @@ {{ 'project.overview.metadata.contributors' | translate }}
}
- @if (contributors()) {
-
-
+ @if (contributors().length) {
+
+
}
diff --git a/src/app/features/metadata/components/metadata-contributors/metadata-contributors.component.ts b/src/app/features/metadata/components/metadata-contributors/metadata-contributors.component.ts
index 461edea00..360c4b225 100644
--- a/src/app/features/metadata/components/metadata-contributors/metadata-contributors.component.ts
+++ b/src/app/features/metadata/components/metadata-contributors/metadata-contributors.component.ts
@@ -15,7 +15,11 @@ import { ContributorModel } from '@osf/shared/models';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MetadataContributorsComponent {
- openEditContributorDialog = output
();
contributors = input([]);
+ isLoading = input(false);
+ hasMoreContributors = input(false);
readonly = input(false);
+
+ openEditContributorDialog = output();
+ loadMoreContributors = output();
}
diff --git a/src/app/features/metadata/components/metadata-license/metadata-license.component.html b/src/app/features/metadata/components/metadata-license/metadata-license.component.html
index 092b81ff8..a4207154f 100644
--- a/src/app/features/metadata/components/metadata-license/metadata-license.component.html
+++ b/src/app/features/metadata/components/metadata-license/metadata-license.component.html
@@ -1,6 +1,6 @@
-
{{ 'project.overview.metadata.license' | translate }}
+
{{ 'common.labels.license' | translate }}
@if (!readonly()) {
@if (hasChanges) {
diff --git a/src/app/features/metadata/dialogs/contributors-dialog/contributors-dialog.component.spec.ts b/src/app/features/metadata/dialogs/contributors-dialog/contributors-dialog.component.spec.ts
index 448901f5a..8f1fa353e 100644
--- a/src/app/features/metadata/dialogs/contributors-dialog/contributors-dialog.component.spec.ts
+++ b/src/app/features/metadata/dialogs/contributors-dialog/contributors-dialog.component.spec.ts
@@ -6,7 +6,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ContributorModel } from '@osf/shared/models';
import { CustomDialogService } from '@osf/shared/services';
-import { ContributorsSelectors } from '@osf/shared/stores';
+import { ContributorsSelectors } from '@osf/shared/stores/contributors';
import { SearchInputComponent } from '@shared/components';
import { ContributorsTableComponent } from '@shared/components/contributors';
diff --git a/src/app/features/metadata/dialogs/contributors-dialog/contributors-dialog.component.ts b/src/app/features/metadata/dialogs/contributors-dialog/contributors-dialog.component.ts
index dd5693767..7f7d430e0 100644
--- a/src/app/features/metadata/dialogs/contributors-dialog/contributors-dialog.component.ts
+++ b/src/app/features/metadata/dialogs/contributors-dialog/contributors-dialog.component.ts
@@ -4,7 +4,6 @@ import { TranslatePipe } from '@ngx-translate/core';
import { Button } from 'primeng/button';
import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
-import { TablePageEvent } from 'primeng/table';
import { filter } from 'rxjs';
@@ -41,10 +40,11 @@ import {
ContributorsSelectors,
DeleteContributor,
GetAllContributors,
+ LoadMoreContributors,
UpdateBibliographyFilter,
UpdateContributorsSearchValue,
UpdatePermissionFilter,
-} from '@osf/shared/stores';
+} from '@osf/shared/stores/contributors';
import { MetadataSelectors } from '../../store';
@@ -70,16 +70,18 @@ export class ContributorsDialogComponent implements OnInit {
contributorsTotalCount = select(ContributorsSelectors.getContributorsTotalCount);
hasAdminAccess = select(MetadataSelectors.hasAdminAccess);
contributors = signal([]);
- page = select(ContributorsSelectors.getContributorsPageNumber);
+ isLoadingMore = select(ContributorsSelectors.isContributorsLoadingMore);
pageSize = select(ContributorsSelectors.getContributorsPageSize);
+ changesMade = signal(false);
currentUser = select(UserSelectors.getCurrentUser);
readonly tableParams = computed(() => ({
...DEFAULT_TABLE_PARAMS,
totalRecords: this.contributorsTotalCount(),
- paginator: this.contributorsTotalCount() > DEFAULT_TABLE_PARAMS.rows,
- firstRowIndex: (this.page() - 1) * this.pageSize(),
+ paginator: false,
+ scrollable: true,
+ firstRowIndex: 0,
rows: this.pageSize(),
}));
@@ -92,6 +94,7 @@ export class ContributorsDialogComponent implements OnInit {
addContributor: AddContributor,
bulkAddContributors: BulkAddContributors,
bulkUpdateContributors: BulkUpdateContributors,
+ loadMoreContributors: LoadMoreContributors,
});
private readonly resourceType: ResourceType;
@@ -117,6 +120,7 @@ export class ContributorsDialogComponent implements OnInit {
}
ngOnInit(): void {
+ this.actions.getContributors(this.resourceId, this.resourceType);
this.setSearchSubscription();
}
@@ -127,13 +131,10 @@ export class ContributorsDialogComponent implements OnInit {
}
openAddContributorDialog(): void {
- const addedContributorIds = this.initialContributors().map((x) => x.userId);
-
this.customDialogService
.open(AddContributorDialogComponent, {
header: 'project.contributors.addDialog.addRegisteredContributor',
width: '448px',
- data: addedContributorIds,
})
.onClose.pipe(
filter((res: ContributorDialogAddModel) => !!res),
@@ -147,9 +148,10 @@ export class ContributorsDialogComponent implements OnInit {
this.actions
.bulkAddContributors(this.resourceId, this.resourceType, res.data)
.pipe(takeUntilDestroyed(this.destroyRef))
- .subscribe(() =>
- this.toastService.showSuccess('project.contributors.toastMessages.multipleAddSuccessMessage')
- );
+ .subscribe(() => {
+ this.changesMade.set(true);
+ this.toastService.showSuccess('project.contributors.toastMessages.multipleAddSuccessMessage');
+ });
}
}
});
@@ -172,7 +174,10 @@ export class ContributorsDialogComponent implements OnInit {
const params = { name: res.data[0].fullName };
this.actions.addContributor(this.resourceId, this.resourceType, res.data[0]).subscribe({
- next: () => this.toastService.showSuccess('project.contributors.toastMessages.addSuccessMessage', params),
+ next: () => {
+ this.changesMade.set(true);
+ this.toastService.showSuccess('project.contributors.toastMessages.addSuccessMessage', params);
+ },
});
}
});
@@ -192,12 +197,13 @@ export class ContributorsDialogComponent implements OnInit {
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe({
next: () => {
+ this.changesMade.set(true);
this.toastService.showSuccess('project.contributors.removeDialog.successMessage', {
name: contributor.fullName,
});
if (isDeletingSelf) {
- this.dialogRef.close();
+ this.dialogRef.close(this.changesMade());
this.router.navigate(['/']);
}
},
@@ -206,11 +212,8 @@ export class ContributorsDialogComponent implements OnInit {
});
}
- pageChanged(event: TablePageEvent) {
- const page = Math.floor(event.first / event.rows) + 1;
- const pageSize = event.rows;
-
- this.actions.getContributors(this.resourceId, this.resourceType, page, pageSize);
+ loadMoreContributors(): void {
+ this.actions.loadMoreContributors(this.resourceId, this.resourceType);
}
cancel() {
@@ -218,7 +221,7 @@ export class ContributorsDialogComponent implements OnInit {
}
onClose(): void {
- this.dialogRef.close();
+ this.dialogRef.close(this.changesMade());
}
onSave(): void {
@@ -227,8 +230,9 @@ export class ContributorsDialogComponent implements OnInit {
this.actions
.bulkUpdateContributors(this.resourceId, this.resourceType, updatedContributors)
.pipe(takeUntilDestroyed(this.destroyRef))
- .subscribe(() =>
- this.toastService.showSuccess('project.contributors.toastMessages.multipleUpdateSuccessMessage')
- );
+ .subscribe(() => {
+ this.changesMade.set(true);
+ this.toastService.showSuccess('project.contributors.toastMessages.multipleUpdateSuccessMessage');
+ });
}
}
diff --git a/src/app/features/metadata/metadata.component.html b/src/app/features/metadata/metadata.component.html
index a5adc3975..fe576760d 100644
--- a/src/app/features/metadata/metadata.component.html
+++ b/src/app/features/metadata/metadata.component.html
@@ -38,7 +38,10 @@
this.isMetadataLoading() ||
- this.isContributorsLoading() ||
this.areInstitutionsLoading() ||
this.isSubmitting() ||
this.areResourceInstitutionsSubmitting()
@@ -320,23 +326,24 @@ export class MetadataComponent implements OnInit {
this.actions.updateMetadata(this.resourceId, this.resourceType(), { tags });
}
+ handleLoadMoreContributors(): void {
+ this.actions.loadMoreBibliographicContributors(this.resourceId, this.resourceType());
+ }
+
openEditContributorDialog(): void {
this.customDialogService
.open(ContributorsDialogComponent, {
header: 'project.metadata.contributors.editContributors',
- width: '600px',
+ width: '800px',
data: {
resourceId: this.resourceId,
resourceType: this.resourceType(),
},
})
- .onClose.subscribe((result) => {
- if (result) {
- this.actions.getResourceMetadata(this.resourceId, this.resourceType());
- this.toastService.showSuccess('project.metadata.contributors.updateSucceed');
+ .onClose.subscribe((changesMade) => {
+ if (changesMade) {
+ this.actions.getContributors(this.resourceId, this.resourceType());
}
-
- this.actions.updateContributorsSearchValue(null);
});
}
diff --git a/src/app/features/moderation/components/collection-moderation-submissions/collection-moderation-submissions.component.spec.ts b/src/app/features/moderation/components/collection-moderation-submissions/collection-moderation-submissions.component.spec.ts
index bd7f22322..88d55311b 100644
--- a/src/app/features/moderation/components/collection-moderation-submissions/collection-moderation-submissions.component.spec.ts
+++ b/src/app/features/moderation/components/collection-moderation-submissions/collection-moderation-submissions.component.spec.ts
@@ -4,7 +4,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ActivatedRoute, Router } from '@angular/router';
import { CollectionSubmissionsListComponent } from '@osf/features/moderation/components';
-import { CollectionsSelectors } from '@osf/shared/stores';
+import { CollectionsSelectors } from '@osf/shared/stores/collections';
import { CustomPaginatorComponent, IconComponent, LoadingSpinnerComponent, SelectComponent } from '@shared/components';
import { SubmissionReviewStatus } from '../../enums';
diff --git a/src/app/features/moderation/components/collection-moderation-submissions/collection-moderation-submissions.component.ts b/src/app/features/moderation/components/collection-moderation-submissions/collection-moderation-submissions.component.ts
index 867e22cda..16f0fb2e4 100644
--- a/src/app/features/moderation/components/collection-moderation-submissions/collection-moderation-submissions.component.ts
+++ b/src/app/features/moderation/components/collection-moderation-submissions/collection-moderation-submissions.component.ts
@@ -24,7 +24,7 @@ import {
GetCollectionDetails,
SearchCollectionSubmissions,
SetPageNumber,
-} from '@osf/shared/stores';
+} from '@osf/shared/stores/collections';
import { COLLECTIONS_SUBMISSIONS_REVIEW_OPTIONS } from '../../constants';
import { SubmissionReviewStatus } from '../../enums';
diff --git a/src/app/features/moderation/components/collection-submission-item/collection-submission-item.component.spec.ts b/src/app/features/moderation/components/collection-submission-item/collection-submission-item.component.spec.ts
index 64180a945..6160eae16 100644
--- a/src/app/features/moderation/components/collection-submission-item/collection-submission-item.component.spec.ts
+++ b/src/app/features/moderation/components/collection-submission-item/collection-submission-item.component.spec.ts
@@ -4,7 +4,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ActivatedRoute, Router } from '@angular/router';
import { CollectionSubmissionWithGuid } from '@osf/shared/models';
-import { CollectionsSelectors } from '@osf/shared/stores';
+import { CollectionsSelectors } from '@osf/shared/stores/collections';
import { IconComponent } from '@shared/components';
import { DateAgoPipe } from '@shared/pipes';
diff --git a/src/app/features/moderation/components/collection-submission-item/collection-submission-item.component.ts b/src/app/features/moderation/components/collection-submission-item/collection-submission-item.component.ts
index 01e5e221b..cea58dfca 100644
--- a/src/app/features/moderation/components/collection-submission-item/collection-submission-item.component.ts
+++ b/src/app/features/moderation/components/collection-submission-item/collection-submission-item.component.ts
@@ -11,7 +11,7 @@ import { collectionFilterNames } from '@osf/features/collections/constants';
import { IconComponent, TruncatedTextComponent } from '@osf/shared/components';
import { CollectionSubmissionWithGuid } from '@osf/shared/models';
import { DateAgoPipe } from '@osf/shared/pipes';
-import { CollectionsSelectors } from '@osf/shared/stores';
+import { CollectionsSelectors } from '@osf/shared/stores/collections';
import { ReviewStatusIcon } from '../../constants';
import { SubmissionReviewStatus } from '../../enums';
diff --git a/src/app/features/moderation/components/moderators-list/moderators-list.component.html b/src/app/features/moderation/components/moderators-list/moderators-list.component.html
index b221012f6..65e154244 100644
--- a/src/app/features/moderation/components/moderators-list/moderators-list.component.html
+++ b/src/app/features/moderation/components/moderators-list/moderators-list.component.html
@@ -1,7 +1,7 @@
- @if (isCurrentUserAdminModerator()) {
+ @if (hasAdminAccess()) {
{
signals: [
{ selector: UserSelectors.getCurrentUser, value: mockCurrentUser },
{ selector: ModeratorsSelectors.getModerators, value: mockModerators },
+ { selector: ProviderSelectors.hasAdminAccess, value: false },
{ selector: ModeratorsSelectors.isModeratorsLoading, value: false },
],
}),
@@ -110,7 +112,7 @@ describe('ModeratorsListComponent', () => {
fixture.detectChanges();
- expect(component.isCurrentUserAdminModerator()).toBe(false);
+ expect(component.hasAdminAccess()).toBe(false);
});
it('should return false for admin moderator when user is not found', () => {
@@ -121,7 +123,7 @@ describe('ModeratorsListComponent', () => {
fixture.detectChanges();
- expect(component.isCurrentUserAdminModerator()).toBe(false);
+ expect(component.hasAdminAccess()).toBe(false);
});
it('should load moderators on initialization', () => {
diff --git a/src/app/features/moderation/components/moderators-list/moderators-list.component.ts b/src/app/features/moderation/components/moderators-list/moderators-list.component.ts
index cb2b979ff..e7af36f1e 100644
--- a/src/app/features/moderation/components/moderators-list/moderators-list.component.ts
+++ b/src/app/features/moderation/components/moderators-list/moderators-list.component.ts
@@ -22,6 +22,7 @@ import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
import { FormControl } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
+import { ProviderSelectors } from '@core/store/provider';
import { UserSelectors } from '@core/store/user';
import { SearchInputComponent } from '@osf/shared/components';
import { DEFAULT_TABLE_PARAMS } from '@osf/shared/constants';
@@ -29,7 +30,7 @@ import { ResourceType } from '@osf/shared/enums';
import { TableParameters } from '@osf/shared/models';
import { CustomConfirmationService, CustomDialogService, ToastService } from '@osf/shared/services';
-import { AddModeratorType, ModeratorPermission } from '../../enums';
+import { AddModeratorType } from '../../enums';
import { ModeratorDialogAddModel, ModeratorModel } from '../../models';
import {
AddModerator,
@@ -70,6 +71,7 @@ export class ModeratorsListComponent implements OnInit {
initialModerators = select(ModeratorsSelectors.getModerators);
isModeratorsLoading = select(ModeratorsSelectors.isModeratorsLoading);
moderatorsTotalCount = select(ModeratorsSelectors.getModeratorsTotalCount);
+ hasAdminAccess = select(ProviderSelectors.hasAdminAccess);
currentUser = select(UserSelectors.getCurrentUser);
readonly tableParams = computed(() => ({
@@ -78,17 +80,6 @@ export class ModeratorsListComponent implements OnInit {
paginator: this.moderatorsTotalCount() > DEFAULT_TABLE_PARAMS.rows,
}));
- isCurrentUserAdminModerator = computed(() => {
- const currentUserId = this.currentUser()?.id;
- const initialModerators = this.initialModerators();
- if (!currentUserId) return false;
-
- return initialModerators.some(
- (moderator: ModeratorModel) =>
- moderator.userId === currentUserId && moderator.permission === ModeratorPermission.Admin
- );
- });
-
actions = createDispatchMap({
loadModerators: LoadModerators,
updateSearchValue: UpdateModeratorsSearchValue,
diff --git a/src/app/features/moderation/components/moderators-table/moderators-table.component.html b/src/app/features/moderation/components/moderators-table/moderators-table.component.html
index 15588e823..03af03e71 100644
--- a/src/app/features/moderation/components/moderators-table/moderators-table.component.html
+++ b/src/app/features/moderation/components/moderators-table/moderators-table.component.html
@@ -37,7 +37,7 @@
|
- @if (isCurrentUserAdminModerator()) {
+ @if (hasAdminAccess()) {
|
- @if (isCurrentUserAdminModerator() || currentUserId() === item.id) {
+ @if (hasAdminAccess() || currentUserId() === item.id) {
}
|
diff --git a/src/app/features/moderation/components/moderators-table/moderators-table.component.spec.ts b/src/app/features/moderation/components/moderators-table/moderators-table.component.spec.ts
index 312360ba1..0df3b64a4 100644
--- a/src/app/features/moderation/components/moderators-table/moderators-table.component.spec.ts
+++ b/src/app/features/moderation/components/moderators-table/moderators-table.component.spec.ts
@@ -44,7 +44,7 @@ describe('ModeratorsTableComponent', () => {
fixture.componentRef.setInput('tableParams', mockTableParams);
fixture.componentRef.setInput('currentUserId', 'test-user-id');
- fixture.componentRef.setInput('isCurrentUserAdminModerator', false);
+ fixture.componentRef.setInput('hasAdminAccess', false);
});
it('should create', () => {
@@ -56,7 +56,7 @@ describe('ModeratorsTableComponent', () => {
fixture.componentRef.setInput('items', mockModerators);
fixture.componentRef.setInput('isLoading', true);
fixture.componentRef.setInput('currentUserId', 'current-user-123');
- fixture.componentRef.setInput('isCurrentUserAdminModerator', true);
+ fixture.componentRef.setInput('hasAdminAccess', true);
fixture.componentRef.setInput('tableParams', mockTableParams);
fixture.detectChanges();
@@ -64,7 +64,7 @@ describe('ModeratorsTableComponent', () => {
expect(component.items()).toEqual(mockModerators);
expect(component.isLoading()).toBe(true);
expect(component.currentUserId()).toBe('current-user-123');
- expect(component.isCurrentUserAdminModerator()).toBe(true);
+ expect(component.hasAdminAccess()).toBe(true);
expect(component.tableParams()).toEqual(mockTableParams);
});
@@ -118,7 +118,7 @@ describe('ModeratorsTableComponent', () => {
fixture.componentRef.setInput('items', []);
fixture.componentRef.setInput('tableParams', mockTableParams);
fixture.componentRef.setInput('currentUserId', 'test-user-id');
- fixture.componentRef.setInput('isCurrentUserAdminModerator', false);
+ fixture.componentRef.setInput('hasAdminAccess', false);
fixture.detectChanges();
@@ -128,7 +128,7 @@ describe('ModeratorsTableComponent', () => {
it('should handle undefined currentUserId', () => {
fixture.componentRef.setInput('currentUserId', undefined);
fixture.componentRef.setInput('tableParams', mockTableParams);
- fixture.componentRef.setInput('isCurrentUserAdminModerator', false);
+ fixture.componentRef.setInput('hasAdminAccess', false);
fixture.detectChanges();
diff --git a/src/app/features/moderation/components/moderators-table/moderators-table.component.ts b/src/app/features/moderation/components/moderators-table/moderators-table.component.ts
index 058bb7977..80e61d8d7 100644
--- a/src/app/features/moderation/components/moderators-table/moderators-table.component.ts
+++ b/src/app/features/moderation/components/moderators-table/moderators-table.component.ts
@@ -31,7 +31,7 @@ export class ModeratorsTableComponent {
isLoading = input(false);
tableParams = input.required();
currentUserId = input.required();
- isCurrentUserAdminModerator = input.required();
+ hasAdminAccess = input.required();
update = output();
remove = output();
diff --git a/src/app/features/moderation/components/preprint-submission-item/preprint-submission-item.component.html b/src/app/features/moderation/components/preprint-submission-item/preprint-submission-item.component.html
index ae7decd48..8de29f158 100644
--- a/src/app/features/moderation/components/preprint-submission-item/preprint-submission-item.component.html
+++ b/src/app/features/moderation/components/preprint-submission-item/preprint-submission-item.component.html
@@ -39,7 +39,7 @@
-
+
{{ 'common.labels.contributors' | translate }}:
@if (submission().contributorsLoading) {
diff --git a/src/app/features/moderation/mappers/preprint-moderation.mapper.ts b/src/app/features/moderation/mappers/preprint-moderation.mapper.ts
index 0912104be..cf4500f0b 100644
--- a/src/app/features/moderation/mappers/preprint-moderation.mapper.ts
+++ b/src/app/features/moderation/mappers/preprint-moderation.mapper.ts
@@ -50,6 +50,7 @@ export class PreprintModerationMapper {
return {
id: response.id,
name: response.attributes.name,
+ permissions: response.attributes.permissions,
reviewsCommentsAnonymous: response.attributes.reviews_comments_anonymous,
reviewsCommentsPrivate: response.attributes.reviews_comments_private,
reviewsWorkflow: response.attributes.reviews_workflow,
diff --git a/src/app/features/moderation/models/preprint-provider-moderation-info.model.ts b/src/app/features/moderation/models/preprint-provider-moderation-info.model.ts
index af069ba44..73649885b 100644
--- a/src/app/features/moderation/models/preprint-provider-moderation-info.model.ts
+++ b/src/app/features/moderation/models/preprint-provider-moderation-info.model.ts
@@ -1,9 +1,12 @@
+import { ReviewPermissions } from '@osf/shared/enums';
+
export interface PreprintProviderModerationInfo {
id: string;
name: string;
- submissionCount?: number;
+ permissions: ReviewPermissions[];
reviewsCommentsAnonymous: boolean;
reviewsCommentsPrivate: boolean;
reviewsWorkflow: string;
+ submissionCount?: number;
supportEmail: string | null;
}
diff --git a/src/app/features/moderation/pages/collection-moderation/collection-moderation.component.ts b/src/app/features/moderation/pages/collection-moderation/collection-moderation.component.ts
index a6ea58f3d..df433f843 100644
--- a/src/app/features/moderation/pages/collection-moderation/collection-moderation.component.ts
+++ b/src/app/features/moderation/pages/collection-moderation/collection-moderation.component.ts
@@ -12,7 +12,7 @@ import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
import { ClearCurrentProvider } from '@core/store/provider';
import { SelectComponent, SubHeaderComponent } from '@osf/shared/components';
import { IS_MEDIUM, Primitive } from '@osf/shared/helpers';
-import { GetCollectionProvider } from '@osf/shared/stores';
+import { GetCollectionProvider } from '@osf/shared/stores/collections';
import { COLLECTION_MODERATION_TABS } from '../../constants';
import { CollectionModerationTab } from '../../enums';
diff --git a/src/app/features/moderation/pages/preprint-moderation/preprint-moderation.component.ts b/src/app/features/moderation/pages/preprint-moderation/preprint-moderation.component.ts
index 5a07a1385..90d1538d1 100644
--- a/src/app/features/moderation/pages/preprint-moderation/preprint-moderation.component.ts
+++ b/src/app/features/moderation/pages/preprint-moderation/preprint-moderation.component.ts
@@ -1,3 +1,5 @@
+import { createDispatchMap } from '@ngxs/store';
+
import { TranslatePipe } from '@ngx-translate/core';
import { Tab, TabList, TabPanels, Tabs } from 'primeng/tabs';
@@ -13,6 +15,7 @@ import { IS_MEDIUM, Primitive } from '@osf/shared/helpers';
import { PREPRINT_MODERATION_TABS } from '../../constants';
import { PreprintModerationTab } from '../../enums';
+import { GetPreprintProvider } from '../../store/preprint-moderation';
@Component({
selector: 'osf-preprint-moderation',
@@ -41,8 +44,19 @@ export class PreprintModerationComponent implements OnInit {
selectedTab = PreprintModerationTab.Submissions;
+ private readonly actions = createDispatchMap({ getPreprintProvider: GetPreprintProvider });
+
ngOnInit(): void {
this.selectedTab = this.route.snapshot.firstChild?.data['tab'] as PreprintModerationTab;
+
+ const id = this.route.snapshot.params['providerId'];
+
+ if (!id) {
+ this.router.navigate(['/not-found']);
+ return;
+ }
+
+ this.actions.getPreprintProvider(id);
}
onTabChange(value: Primitive): void {
diff --git a/src/app/features/moderation/services/preprint-moderation.service.ts b/src/app/features/moderation/services/preprint-moderation.service.ts
index c33a8dd9b..0696c5f2c 100644
--- a/src/app/features/moderation/services/preprint-moderation.service.ts
+++ b/src/app/features/moderation/services/preprint-moderation.service.ts
@@ -3,7 +3,7 @@ import { catchError, forkJoin, map, Observable, of, switchMap } from 'rxjs';
import { inject, Injectable } from '@angular/core';
import { ENVIRONMENT } from '@core/provider/environment.provider';
-import { JsonApiResponse, PaginatedData, ResponseJsonApi } from '@osf/shared/models';
+import { PaginatedData, ResponseJsonApi } from '@osf/shared/models';
import { JsonApiService } from '@osf/shared/services';
import { PreprintSubmissionsSort } from '../enums';
@@ -36,7 +36,7 @@ export class PreprintModerationService {
const baseUrl = `${this.apiUrl}/providers/preprints/?filter[permissions]=view_actions,set_up_moderation`;
return this.jsonApiService
- .get
>(baseUrl)
+ .get>(baseUrl)
.pipe(map((response) => response.data.map((x) => PreprintModerationMapper.fromPreprintRelatedCounts(x))));
}
@@ -44,7 +44,7 @@ export class PreprintModerationService {
const baseUrl = `${this.apiUrl}/providers/preprints/${id}/?related_counts=true`;
return this.jsonApiService
- .get>(baseUrl)
+ .get>(baseUrl)
.pipe(map((response) => PreprintModerationMapper.fromPreprintRelatedCounts(response.data)));
}
diff --git a/src/app/features/moderation/store/moderators/moderators.state.ts b/src/app/features/moderation/store/moderators/moderators.state.ts
index 866b7d17d..51138ccfb 100644
--- a/src/app/features/moderation/store/moderators/moderators.state.ts
+++ b/src/app/features/moderation/store/moderators/moderators.state.ts
@@ -88,7 +88,7 @@ export class ModeratorsState {
}
@Action(UpdateModerator)
- updateCollectionModerator(ctx: StateContext, action: UpdateModerator) {
+ updateModerator(ctx: StateContext, action: UpdateModerator) {
const state = ctx.getState();
if (!action.resourceType) {
@@ -108,7 +108,7 @@ export class ModeratorsState {
}
@Action(DeleteModerator)
- deleteCollectionModerator(ctx: StateContext, action: DeleteModerator) {
+ deleteModerator(ctx: StateContext, action: DeleteModerator) {
const state = ctx.getState();
if (!action.resourceType) {
diff --git a/src/app/features/moderation/store/preprint-moderation/preprint-moderation.state.ts b/src/app/features/moderation/store/preprint-moderation/preprint-moderation.state.ts
index a8189c0fc..bae189e49 100644
--- a/src/app/features/moderation/store/preprint-moderation/preprint-moderation.state.ts
+++ b/src/app/features/moderation/store/preprint-moderation/preprint-moderation.state.ts
@@ -5,8 +5,9 @@ import { catchError, forkJoin, map, switchMap, tap } from 'rxjs';
import { inject, Injectable } from '@angular/core';
+import { SetCurrentProvider } from '@core/store/provider';
import { DEFAULT_TABLE_PARAMS } from '@osf/shared/constants';
-import { ResourceType } from '@osf/shared/enums';
+import { CurrentResourceType, ResourceType } from '@osf/shared/enums';
import { handleSectionError } from '@osf/shared/helpers';
import { ContributorsService } from '@osf/shared/services';
@@ -92,6 +93,16 @@ export class PreprintModerationState {
tap((data) => {
const exists = ctx.getState().preprintProviders.data.some((p) => p.id === data.id);
+ ctx.dispatch(
+ new SetCurrentProvider({
+ id: data.id,
+ name: data.name,
+ type: CurrentResourceType.Preprints,
+ permissions: data.permissions,
+ reviewsWorkflow: data.reviewsWorkflow,
+ })
+ );
+
ctx.setState(
patch({
preprintProviders: patch({
diff --git a/src/app/features/my-projects/components/create-project-dialog/create-project-dialog.component.spec.ts b/src/app/features/my-projects/components/create-project-dialog/create-project-dialog.component.spec.ts
index 65f89d8c6..f3743edb5 100644
--- a/src/app/features/my-projects/components/create-project-dialog/create-project-dialog.component.spec.ts
+++ b/src/app/features/my-projects/components/create-project-dialog/create-project-dialog.component.spec.ts
@@ -10,7 +10,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DEFAULT_TABLE_PARAMS } from '@osf/shared/constants';
import { ProjectFormControls } from '@osf/shared/enums';
-import { CreateProject, GetMyProjects, MyResourcesSelectors } from '@osf/shared/stores';
+import { CreateProject, GetMyProjects, MyResourcesSelectors } from '@osf/shared/stores/my-resources';
import { AddProjectFormComponent } from '@shared/components';
import { CreateProjectDialogComponent } from './create-project-dialog.component';
diff --git a/src/app/features/my-projects/components/create-project-dialog/create-project-dialog.component.ts b/src/app/features/my-projects/components/create-project-dialog/create-project-dialog.component.ts
index 6f74d333a..90eacddad 100644
--- a/src/app/features/my-projects/components/create-project-dialog/create-project-dialog.component.ts
+++ b/src/app/features/my-projects/components/create-project-dialog/create-project-dialog.component.ts
@@ -13,7 +13,7 @@ import { DEFAULT_TABLE_PARAMS } from '@osf/shared/constants';
import { ProjectFormControls } from '@osf/shared/enums';
import { CustomValidators } from '@osf/shared/helpers';
import { ProjectForm } from '@osf/shared/models';
-import { CreateProject, GetMyProjects, MyResourcesSelectors } from '@osf/shared/stores';
+import { CreateProject, GetMyProjects, MyResourcesSelectors } from '@osf/shared/stores/my-resources';
@Component({
selector: 'osf-create-project-dialog',
diff --git a/src/app/features/my-projects/my-projects.component.spec.ts b/src/app/features/my-projects/my-projects.component.spec.ts
index e8b958e17..a5868a1d5 100644
--- a/src/app/features/my-projects/my-projects.component.spec.ts
+++ b/src/app/features/my-projects/my-projects.component.spec.ts
@@ -8,7 +8,8 @@ import { ActivatedRoute, Router } from '@angular/router';
import { MyProjectsTab } from '@osf/features/my-projects/enums';
import { SortOrder } from '@osf/shared/enums';
import { IS_MEDIUM } from '@osf/shared/helpers';
-import { BookmarksSelectors, MyResourcesSelectors } from '@osf/shared/stores';
+import { BookmarksSelectors } from '@osf/shared/stores/bookmarks';
+import { MyResourcesSelectors } from '@osf/shared/stores/my-resources';
import {
MyProjectsTableComponent,
SearchInputComponent,
diff --git a/src/app/features/my-projects/my-projects.component.ts b/src/app/features/my-projects/my-projects.component.ts
index b9381dc6a..618c2df18 100644
--- a/src/app/features/my-projects/my-projects.component.ts
+++ b/src/app/features/my-projects/my-projects.component.ts
@@ -33,16 +33,15 @@ import { DEFAULT_TABLE_PARAMS } from '@osf/shared/constants';
import { ResourceType, SortOrder } from '@osf/shared/enums';
import { IS_MEDIUM } from '@osf/shared/helpers';
import { MyResourcesItem, MyResourcesSearchFilters, QueryParams, TableParameters } from '@osf/shared/models';
+import { BookmarksSelectors, GetBookmarksCollectionId } from '@osf/shared/stores/bookmarks';
import {
- BookmarksSelectors,
ClearMyResources,
- GetBookmarksCollectionId,
GetMyBookmarks,
GetMyPreprints,
GetMyProjects,
GetMyRegistrations,
MyResourcesSelectors,
-} from '@osf/shared/stores';
+} from '@osf/shared/stores/my-resources';
import { CustomDialogService, ProjectRedirectDialogService } from '@shared/services';
import { PROJECT_FILTER_OPTIONS } from './constants/project-filter-options.const';
diff --git a/src/app/features/preprints/components/preprint-details/additional-info/additional-info.component.html b/src/app/features/preprints/components/preprint-details/additional-info/additional-info.component.html
index c5c929c7e..18a160459 100644
--- a/src/app/features/preprints/components/preprint-details/additional-info/additional-info.component.html
+++ b/src/app/features/preprints/components/preprint-details/additional-info/additional-info.component.html
@@ -30,18 +30,9 @@ {{ 'preprints.details.publicationDoi' | translate }}
}
- {{ 'preprints.preprintStepper.review.sections.metadata.license' | translate }}
+ {{ 'common.labels.license' | translate }}
-
-
-
- {{ license()!.name }}
-
-
- {{ license()!.text | interpolate: licenseOptionsRecord() }}
-
-
-
+
@@ -53,7 +44,7 @@ {{ 'preprints.preprintStepper.review.sections.metadata.subjects' | translate
}
@if (areSelectedSubjectsLoading()) {
-
+
}
@@ -83,8 +74,8 @@
{{ 'preprints.preprintStepper.review.sections.metadata.tags' | translate }}<
@for (i of skeletonData; track $index) {
}
diff --git a/src/app/features/preprints/components/preprint-details/additional-info/additional-info.component.spec.ts b/src/app/features/preprints/components/preprint-details/additional-info/additional-info.component.spec.ts
index 5461b28aa..d49611b63 100644
--- a/src/app/features/preprints/components/preprint-details/additional-info/additional-info.component.spec.ts
+++ b/src/app/features/preprints/components/preprint-details/additional-info/additional-info.component.spec.ts
@@ -1,11 +1,13 @@
import { MockComponent, MockPipe } from 'ng-mocks';
import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { Router } from '@angular/router';
-import { CitationSectionComponent } from '@osf/features/preprints/components/preprint-details/citation-section/citation-section.component';
import { PreprintSelectors } from '@osf/features/preprints/store/preprint';
-import { InterpolatePipe } from '@shared/pipes';
-import { SubjectsSelectors } from '@shared/stores';
+import { InterpolatePipe } from '@osf/shared/pipes';
+import { SubjectsSelectors } from '@osf/shared/stores/subjects';
+
+import { CitationSectionComponent } from '../citation-section/citation-section.component';
import { AdditionalInfoComponent } from './additional-info.component';
@@ -76,13 +78,14 @@ describe('AdditionalInfoComponent', () => {
expect(component.skeletonData.every((item) => item === null)).toBe(true);
});
- it('should return license from preprint when available', () => {
- const license = component.license();
- expect(license).toBe(mockPreprint.embeddedLicense);
- });
+ it('should navigate to search page with tag when tagClicked is called', () => {
+ const router = TestBed.inject(Router);
+ const navigateSpy = jest.spyOn(router, 'navigate');
- it('should return license options record from preprint when available', () => {
- const licenseOptionsRecord = component.licenseOptionsRecord();
- expect(licenseOptionsRecord).toEqual(mockPreprint.licenseOptions);
+ component.tagClicked('test-tag');
+
+ expect(navigateSpy).toHaveBeenCalledWith(['/search'], {
+ queryParams: { search: 'test-tag' },
+ });
});
});
diff --git a/src/app/features/preprints/components/preprint-details/additional-info/additional-info.component.ts b/src/app/features/preprints/components/preprint-details/additional-info/additional-info.component.ts
index c18ba618d..894b8c32b 100644
--- a/src/app/features/preprints/components/preprint-details/additional-info/additional-info.component.ts
+++ b/src/app/features/preprints/components/preprint-details/additional-info/additional-info.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';
@@ -11,27 +10,16 @@ import { DatePipe } from '@angular/common';
import { ChangeDetectionStrategy, Component, computed, effect, inject, input } from '@angular/core';
import { Router } from '@angular/router';
-import { CitationSectionComponent } from '@osf/features/preprints/components/preprint-details/citation-section/citation-section.component';
import { PreprintSelectors } from '@osf/features/preprints/store/preprint';
+import { LicenseDisplayComponent } from '@osf/shared/components';
import { ResourceType } from '@shared/enums';
-import { InterpolatePipe } from '@shared/pipes';
-import { FetchSelectedSubjects, SubjectsSelectors } from '@shared/stores';
+import { FetchSelectedSubjects, SubjectsSelectors } from '@shared/stores/subjects';
+
+import { CitationSectionComponent } from '../citation-section/citation-section.component';
@Component({
selector: 'osf-preprint-additional-info',
- imports: [
- Card,
- TranslatePipe,
- Tag,
- Skeleton,
- DatePipe,
- Accordion,
- AccordionPanel,
- AccordionHeader,
- AccordionContent,
- InterpolatePipe,
- CitationSectionComponent,
- ],
+ imports: [Card, TranslatePipe, Tag, Skeleton, DatePipe, CitationSectionComponent, LicenseDisplayComponent],
templateUrl: './additional-info.component.html',
styleUrl: './additional-info.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
diff --git a/src/app/features/preprints/components/preprint-details/citation-section/citation-section.component.spec.ts b/src/app/features/preprints/components/preprint-details/citation-section/citation-section.component.spec.ts
index 599b465d6..a6512b47c 100644
--- a/src/app/features/preprints/components/preprint-details/citation-section/citation-section.component.spec.ts
+++ b/src/app/features/preprints/components/preprint-details/citation-section/citation-section.component.spec.ts
@@ -1,7 +1,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CitationStyle } from '@shared/models';
-import { CitationsSelectors } from '@shared/stores';
+import { CitationsSelectors } from '@shared/stores/citations';
import { CitationSectionComponent } from './citation-section.component';
diff --git a/src/app/features/preprints/components/preprint-details/citation-section/citation-section.component.ts b/src/app/features/preprints/components/preprint-details/citation-section/citation-section.component.ts
index 81edf77a1..7e595f6ab 100644
--- a/src/app/features/preprints/components/preprint-details/citation-section/citation-section.component.ts
+++ b/src/app/features/preprints/components/preprint-details/citation-section/citation-section.component.ts
@@ -30,7 +30,7 @@ import {
GetCitationStyles,
GetStyledCitation,
UpdateCustomCitation,
-} from '@shared/stores';
+} from '@shared/stores/citations';
@Component({
selector: 'osf-preprint-citation-section',
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 1864e4d31..a20d43ff2 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
@@ -7,11 +7,12 @@
{{ 'preprints.preprintStepper.review.sections.metadata.authors' | translate }}
-
-
- @if (areContributorsLoading()) {
-
- }
+
@@ -118,8 +119,8 @@ {{ 'preprints.preprintStepper.review.sections.authorAssertions.conflictOfInt
@for (i of skeletonData; track $index) {
}
diff --git a/src/app/features/preprints/components/preprint-details/general-information/general-information.component.spec.ts b/src/app/features/preprints/components/preprint-details/general-information/general-information.component.spec.ts
index e1e4de9c4..9baa56810 100644
--- a/src/app/features/preprints/components/preprint-details/general-information/general-information.component.spec.ts
+++ b/src/app/features/preprints/components/preprint-details/general-information/general-information.component.spec.ts
@@ -11,7 +11,8 @@ import {
IconComponent,
TruncatedTextComponent,
} from '@shared/components';
-import { ContributorsSelectors, InstitutionsSelectors } from '@shared/stores';
+import { ContributorsSelectors } from '@shared/stores/contributors';
+import { InstitutionsSelectors } from '@shared/stores/institutions';
import { PreprintDoiSectionComponent } from '../preprint-doi-section/preprint-doi-section.component';
@@ -47,9 +48,7 @@ describe('GeneralInformationComponent', () => {
),
],
providers: [
- MockProvider(ENVIRONMENT, {
- webUrl: mockWebUrl,
- }),
+ MockProvider(ENVIRONMENT, { webUrl: mockWebUrl }),
provideMockStore({
signals: [
{
@@ -61,11 +60,15 @@ describe('GeneralInformationComponent', () => {
value: false,
},
{
- selector: ContributorsSelectors.getContributors,
+ selector: ContributorsSelectors.getBibliographicContributors,
value: mockContributors,
},
{
- selector: ContributorsSelectors.isContributorsLoading,
+ selector: ContributorsSelectors.isBibliographicContributorsLoading,
+ value: false,
+ },
+ {
+ selector: ContributorsSelectors.hasMoreBibliographicContributors,
value: false,
},
{
@@ -83,26 +86,16 @@ describe('GeneralInformationComponent', () => {
fixture.componentRef.setInput('preprintProvider', mockPreprintProvider);
});
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-
it('should return preprint from store', () => {
const preprint = component.preprint();
expect(preprint).toBe(mockPreprint);
});
it('should return contributors from store', () => {
- const contributors = component.contributors();
+ const contributors = component.bibliographicContributors();
expect(contributors).toBe(mockContributors);
});
- it('should filter bibliographic contributors', () => {
- const bibliographicContributors = component.bibliographicContributors();
- expect(bibliographicContributors).toHaveLength(1);
- expect(bibliographicContributors.every((contributor) => contributor.isBibliographic)).toBe(true);
- });
-
it('should return affiliated institutions from store', () => {
const institutions = component.affiliatedInstitutions();
expect(institutions).toBe(mockInstitutions);
diff --git a/src/app/features/preprints/components/preprint-details/general-information/general-information.component.ts b/src/app/features/preprints/components/preprint-details/general-information/general-information.component.ts
index c3cbfac6b..4f9b1650f 100644
--- a/src/app/features/preprints/components/preprint-details/general-information/general-information.component.ts
+++ b/src/app/features/preprints/components/preprint-details/general-information/general-information.component.ts
@@ -21,11 +21,11 @@ import {
import { ResourceType } from '@osf/shared/enums';
import {
ContributorsSelectors,
- FetchResourceInstitutions,
- GetAllContributors,
- InstitutionsSelectors,
+ GetBibliographicContributors,
+ LoadMoreBibliographicContributors,
ResetContributorsState,
-} from '@osf/shared/stores';
+} from '@osf/shared/stores/contributors';
+import { FetchResourceInstitutions, InstitutionsSelectors } from '@osf/shared/stores/institutions';
import { PreprintDoiSectionComponent } from '../preprint-doi-section/preprint-doi-section.component';
@@ -53,10 +53,11 @@ export class GeneralInformationComponent implements OnDestroy {
readonly PreregLinkInfo = PreregLinkInfo;
private actions = createDispatchMap({
- getContributors: GetAllContributors,
+ getBibliographicContributors: GetBibliographicContributors,
resetContributorsState: ResetContributorsState,
fetchPreprintById: FetchPreprintById,
fetchResourceInstitutions: FetchResourceInstitutions,
+ loadMoreBibliographicContributors: LoadMoreBibliographicContributors,
});
preprintProvider = input.required();
@@ -67,9 +68,9 @@ export class GeneralInformationComponent implements OnDestroy {
affiliatedInstitutions = select(InstitutionsSelectors.getResourceInstitutions);
- contributors = select(ContributorsSelectors.getContributors);
- areContributorsLoading = select(ContributorsSelectors.isContributorsLoading);
- bibliographicContributors = computed(() => this.contributors().filter((contributor) => contributor.isBibliographic));
+ bibliographicContributors = select(ContributorsSelectors.getBibliographicContributors);
+ areContributorsLoading = select(ContributorsSelectors.isBibliographicContributorsLoading);
+ hasMoreBibliographicContributors = select(ContributorsSelectors.hasMoreBibliographicContributors);
skeletonData = Array.from({ length: 5 }, () => null);
@@ -80,7 +81,7 @@ export class GeneralInformationComponent implements OnDestroy {
const preprint = this.preprint();
if (!preprint) return;
- this.actions.getContributors(this.preprint()!.id, ResourceType.Preprint);
+ this.actions.getBibliographicContributors(this.preprint()!.id, ResourceType.Preprint);
this.actions.fetchResourceInstitutions(this.preprint()!.id, ResourceType.Preprint);
});
}
@@ -88,4 +89,8 @@ export class GeneralInformationComponent implements OnDestroy {
ngOnDestroy(): void {
this.actions.resetContributorsState();
}
+
+ handleLoadMoreContributors(): void {
+ this.actions.loadMoreBibliographicContributors(this.preprint()?.id, ResourceType.Preprint);
+ }
}
diff --git a/src/app/features/preprints/components/preprint-details/preprint-doi-section/preprint-doi-section.component.html b/src/app/features/preprints/components/preprint-details/preprint-doi-section/preprint-doi-section.component.html
index cceb3171b..b32a5e842 100644
--- a/src/app/features/preprints/components/preprint-details/preprint-doi-section/preprint-doi-section.component.html
+++ b/src/app/features/preprints/components/preprint-details/preprint-doi-section/preprint-doi-section.component.html
@@ -1,31 +1,32 @@
-@let preprintValue = preprint()!;
+@let preprintValue = preprint();
@let preprintProviderValue = preprintProvider()!;
+
- {{ 'preprints.details.doi.title' | translate: { documentType: preprintProviderValue.preprintWord } }}
+ {{ 'preprints.details.doi.title' | translate: { documentType: preprintProviderValue?.preprintWord } }}
- @if (preprintValue.preprintDoiLink) {
- @if (preprintValue.preprintDoiCreated) {
-
- {{ preprintValue.preprintDoiLink }}
+ @if (preprintValue?.preprintDoiLink) {
+ @if (preprintValue?.preprintDoiCreated) {
+
+ {{ preprintValue?.preprintDoiLink }}
} @else {
- {{ preprintValue.preprintDoiLink }}
+ {{ preprintValue?.preprintDoiLink }}
{{ 'preprints.details.doi.pendingDoiMinted' | translate }}
}
} @else {
- @if (!preprintValue.isPublic) {
+ @if (!preprintValue?.isPublic) {
{{ 'preprints.details.doi.pendingDoi' | translate: { documentType: preprintProviderValue.preprintWord } }}
- } @else if (preprintProvider()?.reviewsWorkflow && !preprintValue.isPublished) {
+ } @else if (preprintProvider()?.reviewsWorkflow && !preprintValue?.isPublished) {
{{ 'preprints.details.doi.pendingDoiModeration' | translate }}
} @else {
{{ 'preprints.details.doi.noDoi' | translate }}
diff --git a/src/app/features/preprints/components/preprint-details/preprint-doi-section/preprint-doi-section.component.spec.ts b/src/app/features/preprints/components/preprint-details/preprint-doi-section/preprint-doi-section.component.spec.ts
index 67b5d5463..e4a95c947 100644
--- a/src/app/features/preprints/components/preprint-details/preprint-doi-section/preprint-doi-section.component.spec.ts
+++ b/src/app/features/preprints/components/preprint-details/preprint-doi-section/preprint-doi-section.component.spec.ts
@@ -1,6 +1,6 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-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 { PreprintDoiSectionComponent } from './preprint-doi-section.component';
@@ -15,7 +15,7 @@ describe('PreprintDoiSectionComponent', () => {
let component: PreprintDoiSectionComponent;
let fixture: ComponentFixture;
- const mockPreprint: Preprint = PREPRINT_MOCK;
+ const mockPreprint: PreprintModel = PREPRINT_MOCK;
const mockProvider: PreprintProviderDetails = PREPRINT_PROVIDER_DETAILS_MOCK;
const mockVersionIds = ['version-1', 'version-2', 'version-3'];
@@ -50,10 +50,6 @@ describe('PreprintDoiSectionComponent', () => {
fixture.componentRef.setInput('preprintProvider', mockProvider);
});
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-
it('should compute versions dropdown options from version IDs', () => {
const options = component.versionsDropdownOptions();
expect(options).toEqual([
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 121fc728a..3e17a370b 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,13 +1,14 @@
@if (safeLink()) {
}
@if (isIframeLoading || isFileLoading()) {
diff --git a/src/app/features/preprints/components/preprint-details/preprint-file-section/preprint-file-section.component.scss b/src/app/features/preprints/components/preprint-details/preprint-file-section/preprint-file-section.component.scss
index c205f2091..7aa21b9e3 100644
--- a/src/app/features/preprints/components/preprint-details/preprint-file-section/preprint-file-section.component.scss
+++ b/src/app/features/preprints/components/preprint-details/preprint-file-section/preprint-file-section.component.scss
@@ -4,7 +4,7 @@
}
.file-section-height {
- min-height: 400px;
+ min-height: 600px;
}
.card {
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 901890563..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
@@ -13,11 +13,12 @@ {{ 'preprints.details.reasonForWithdrawal' | translate }}
{{ 'preprints.preprintStepper.review.sections.metadata.authors' | translate }}
-
-
- @if (areContributorsLoading()) {
-
- }
+
@@ -32,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() }}
-
-
-
+
@@ -51,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.spec.ts b/src/app/features/preprints/components/preprint-details/preprint-tombstone/preprint-tombstone.component.spec.ts
index ecd1053a5..8b8a19bd3 100644
--- a/src/app/features/preprints/components/preprint-details/preprint-tombstone/preprint-tombstone.component.spec.ts
+++ b/src/app/features/preprints/components/preprint-details/preprint-tombstone/preprint-tombstone.component.spec.ts
@@ -6,7 +6,8 @@ import { PreprintProviderDetails } from '@osf/features/preprints/models';
import { PreprintSelectors } from '@osf/features/preprints/store/preprint';
import { ContributorsListComponent, TruncatedTextComponent } from '@osf/shared/components';
import { InterpolatePipe } from '@osf/shared/pipes';
-import { ContributorsSelectors, SubjectsSelectors } from '@osf/shared/stores';
+import { ContributorsSelectors } from '@osf/shared/stores/contributors';
+import { SubjectsSelectors } from '@osf/shared/stores/subjects';
import { PreprintDoiSectionComponent } from '../preprint-doi-section/preprint-doi-section.component';
@@ -50,11 +51,15 @@ describe('PreprintTombstoneComponent', () => {
value: false,
},
{
- selector: ContributorsSelectors.getContributors,
+ selector: ContributorsSelectors.getBibliographicContributors,
value: mockContributors,
},
{
- selector: ContributorsSelectors.isContributorsLoading,
+ selector: ContributorsSelectors.isBibliographicContributorsLoading,
+ value: false,
+ },
+ {
+ selector: ContributorsSelectors.hasMoreBibliographicContributors,
value: false,
},
{
@@ -76,16 +81,6 @@ describe('PreprintTombstoneComponent', () => {
fixture.componentRef.setInput('preprintProvider', mockProvider);
});
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-
- it('should compute bibliographic contributors', () => {
- const bibliographicContributors = component.bibliographicContributors();
- expect(bibliographicContributors).toHaveLength(1);
- expect(bibliographicContributors[0].isBibliographic).toBe(true);
- });
-
it('should compute license from preprint', () => {
const license = component.license();
expect(license).toBe(mockPreprint.embeddedLicense);
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 347a8a2ad..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,16 +13,15 @@ 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,
- FetchSelectedSubjects,
- GetAllContributors,
+ GetBibliographicContributors,
+ LoadMoreBibliographicContributors,
ResetContributorsState,
- SubjectsSelectors,
-} from '@osf/shared/stores';
+} from '@osf/shared/stores/contributors';
+import { FetchSelectedSubjects, SubjectsSelectors } from '@osf/shared/stores/subjects';
import { PreprintDoiSectionComponent } from '../preprint-doi-section/preprint-doi-section.component';
@@ -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',
@@ -53,10 +47,11 @@ export class PreprintTombstoneComponent implements OnDestroy {
readonly PreregLinkInfo = PreregLinkInfo;
private actions = createDispatchMap({
- getContributors: GetAllContributors,
+ getBibliographicContributors: GetBibliographicContributors,
resetContributorsState: ResetContributorsState,
fetchPreprintById: FetchPreprintById,
fetchSubjects: FetchSelectedSubjects,
+ loadMoreBibliographicContributors: LoadMoreBibliographicContributors,
});
private router = inject(Router);
@@ -67,9 +62,9 @@ export class PreprintTombstoneComponent implements OnDestroy {
preprint = select(PreprintSelectors.getPreprint);
isPreprintLoading = select(PreprintSelectors.isPreprintLoading);
- contributors = select(ContributorsSelectors.getContributors);
- areContributorsLoading = select(ContributorsSelectors.isContributorsLoading);
- bibliographicContributors = computed(() => this.contributors().filter((contributor) => contributor.isBibliographic));
+ bibliographicContributors = select(ContributorsSelectors.getBibliographicContributors);
+ areContributorsLoading = select(ContributorsSelectors.isBibliographicContributorsLoading);
+ hasMoreBibliographicContributors = select(ContributorsSelectors.hasMoreBibliographicContributors);
subjects = select(SubjectsSelectors.getSelectedSubjects);
areSelectedSubjectsLoading = select(SubjectsSelectors.areSelectedSubjectsLoading);
@@ -88,7 +83,7 @@ export class PreprintTombstoneComponent implements OnDestroy {
const preprint = this.preprint();
if (!preprint) return;
- this.actions.getContributors(this.preprint()!.id, ResourceType.Preprint);
+ this.actions.getBibliographicContributors(this.preprint()?.id, ResourceType.Preprint);
this.actions.fetchSubjects(this.preprint()!.id, ResourceType.Preprint);
});
}
@@ -100,4 +95,8 @@ export class PreprintTombstoneComponent implements OnDestroy {
tagClicked(tag: string) {
this.router.navigate(['/search'], { queryParams: { search: tag } });
}
+
+ loadMoreContributors(): void {
+ this.actions.loadMoreBibliographicContributors(this.preprint()?.id, ResourceType.Preprint);
+ }
}
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-contributors/preprints-contributors.component.html b/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-contributors/preprints-contributors.component.html
index 87e081260..c5dab158f 100644
--- a/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-contributors/preprints-contributors.component.html
+++ b/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-contributors/preprints-contributors.component.html
@@ -12,8 +12,9 @@ {{ 'project.overview.metadata.contributors' | translate }}
[(contributors)]="contributors"
[tableParams]="tableParams()"
[isLoading]="isContributorsLoading()"
+ [isLoadingMore]="isLoadingMore()"
(remove)="removeContributor($event)"
- (pageChanged)="pageChanged($event)"
+ (loadMore)="loadMoreContributors()"
/>
@if (hasChanges) {
diff --git a/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-contributors/preprints-contributors.component.spec.ts b/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-contributors/preprints-contributors.component.spec.ts
index abf7884cf..68481f6c6 100644
--- a/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-contributors/preprints-contributors.component.spec.ts
+++ b/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-contributors/preprints-contributors.component.spec.ts
@@ -6,7 +6,7 @@ import { UserSelectors } from '@core/store/user';
import { ContributorsTableComponent } from '@osf/shared/components/contributors';
import { ContributorModel } from '@shared/models';
import { CustomConfirmationService, CustomDialogService, ToastService } from '@shared/services';
-import { ContributorsSelectors } from '@shared/stores';
+import { ContributorsSelectors } from '@shared/stores/contributors';
import { PreprintsContributorsComponent } from './preprints-contributors.component';
diff --git a/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-contributors/preprints-contributors.component.ts b/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-contributors/preprints-contributors.component.ts
index b2c08a55f..d11782ce5 100644
--- a/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-contributors/preprints-contributors.component.ts
+++ b/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-contributors/preprints-contributors.component.ts
@@ -5,7 +5,7 @@ import { TranslatePipe } from '@ngx-translate/core';
import { Button } from 'primeng/button';
import { Card } from 'primeng/card';
import { Message } from 'primeng/message';
-import { TableModule, TablePageEvent } from 'primeng/table';
+import { TableModule } from 'primeng/table';
import { filter } from 'rxjs';
@@ -41,7 +41,8 @@ import {
ContributorsSelectors,
DeleteContributor,
GetAllContributors,
-} from '@osf/shared/stores';
+ LoadMoreContributors,
+} from '@osf/shared/stores/contributors';
@Component({
selector: 'osf-preprints-contributors',
@@ -63,14 +64,15 @@ export class PreprintsContributorsComponent implements OnInit {
contributors = signal
([]);
contributorsTotalCount = select(ContributorsSelectors.getContributorsTotalCount);
isContributorsLoading = select(ContributorsSelectors.isContributorsLoading);
- page = select(ContributorsSelectors.getContributorsPageNumber);
+ isLoadingMore = select(ContributorsSelectors.isContributorsLoadingMore);
pageSize = select(ContributorsSelectors.getContributorsPageSize);
readonly tableParams = computed(() => ({
...DEFAULT_TABLE_PARAMS,
totalRecords: this.contributorsTotalCount(),
- paginator: this.contributorsTotalCount() > DEFAULT_TABLE_PARAMS.rows,
- firstRowIndex: (this.page() - 1) * this.pageSize(),
+ paginator: false,
+ scrollable: true,
+ firstRowIndex: 0,
rows: this.pageSize(),
}));
@@ -80,6 +82,7 @@ export class PreprintsContributorsComponent implements OnInit {
bulkUpdateContributors: BulkUpdateContributors,
bulkAddContributors: BulkAddContributors,
addContributor: AddContributor,
+ loadMoreContributors: LoadMoreContributors,
});
get hasChanges(): boolean {
@@ -112,13 +115,10 @@ export class PreprintsContributorsComponent implements OnInit {
}
openAddContributorDialog() {
- const addedContributorIds = this.initialContributors().map((x) => x.userId);
-
this.customDialogService
.open(AddContributorDialogComponent, {
header: 'project.contributors.addDialog.addRegisteredContributor',
width: '448px',
- data: addedContributorIds,
})
.onClose.pipe(
filter((res: ContributorDialogAddModel) => !!res),
@@ -182,10 +182,7 @@ export class PreprintsContributorsComponent implements OnInit {
});
}
- pageChanged(event: TablePageEvent) {
- const page = Math.floor(event.first / event.rows) + 1;
- const pageSize = event.rows;
-
- this.actions.getContributors(this.preprintId(), ResourceType.Preprint, page, pageSize);
+ loadMoreContributors(): void {
+ this.actions.loadMoreContributors(this.preprintId(), ResourceType.Preprint);
}
}
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/preprints-metadata-step/preprints-subjects/preprints-subjects.component.spec.ts b/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-subjects/preprints-subjects.component.spec.ts
index 578e1174a..42e91469b 100644
--- a/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-subjects/preprints-subjects.component.spec.ts
+++ b/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-subjects/preprints-subjects.component.spec.ts
@@ -6,7 +6,7 @@ import { FormControl } from '@angular/forms';
import { PreprintStepperSelectors } from '@osf/features/preprints/store/preprint-stepper';
import { SubjectsComponent } from '@osf/shared/components';
import { SubjectModel } from '@osf/shared/models';
-import { SubjectsSelectors } from '@osf/shared/stores';
+import { SubjectsSelectors } from '@osf/shared/stores/subjects';
import { PreprintsSubjectsComponent } from './preprints-subjects.component';
diff --git a/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-subjects/preprints-subjects.component.ts b/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-subjects/preprints-subjects.component.ts
index b7d18cdbe..8551451f4 100644
--- a/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-subjects/preprints-subjects.component.ts
+++ b/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-subjects/preprints-subjects.component.ts
@@ -19,7 +19,7 @@ import {
FetchSubjects,
SubjectsSelectors,
UpdateResourceSubjects,
-} from '@osf/shared/stores';
+} from '@osf/shared/stores/subjects';
@Component({
selector: 'osf-preprints-subjects',
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 c4eb7d713..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
@@ -62,7 +62,12 @@ {{ 'preprints.preprintStepper.review.sections.metadata.title' | translate }}
{{ 'common.labels.contributors' | translate }}
-
+
@if (affiliatedInstitutions().length) {
@@ -71,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.spec.ts b/src/app/features/preprints/components/stepper/review-step/review-step.component.spec.ts
index 16530d553..455f94667 100644
--- a/src/app/features/preprints/components/stepper/review-step/review-step.component.spec.ts
+++ b/src/app/features/preprints/components/stepper/review-step/review-step.component.spec.ts
@@ -8,7 +8,9 @@ import { PreprintProviderDetails } from '@osf/features/preprints/models';
import { PreprintStepperSelectors } from '@osf/features/preprints/store/preprint-stepper';
import { InterpolatePipe } from '@shared/pipes';
import { ToastService } from '@shared/services';
-import { ContributorsSelectors, InstitutionsSelectors, SubjectsSelectors } from '@shared/stores';
+import { ContributorsSelectors } from '@shared/stores/contributors';
+import { InstitutionsSelectors } from '@shared/stores/institutions';
+import { SubjectsSelectors } from '@shared/stores/subjects';
import { ReviewStepComponent } from './review-step.component';
@@ -52,7 +54,9 @@ describe('ReviewStepComponent', () => {
{ selector: PreprintStepperSelectors.isPreprintSubmitting, value: false },
{ selector: PreprintStepperSelectors.getPreprintLicense, value: mockLicense },
{ selector: PreprintStepperSelectors.getPreprintProject, value: mockPreprintProject },
- { selector: ContributorsSelectors.getContributors, value: mockContributors },
+ { selector: ContributorsSelectors.getBibliographicContributors, value: mockContributors },
+ { selector: ContributorsSelectors.isBibliographicContributorsLoading, value: false },
+ { selector: ContributorsSelectors.hasMoreBibliographicContributors, value: false },
{ selector: SubjectsSelectors.getSelectedSubjects, value: mockSubjects },
{ selector: InstitutionsSelectors.getResourceInstitutions, value: mockInstitutions },
],
@@ -68,20 +72,10 @@ describe('ReviewStepComponent', () => {
fixture.componentRef.setInput('provider', mockProvider);
});
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-
it('should have required provider input', () => {
expect(component.provider()).toEqual(mockProvider);
});
- it('should filter bibliographic contributors', () => {
- const bibliographicContributors = component.bibliographicContributors();
- expect(bibliographicContributors).toHaveLength(1);
- expect(bibliographicContributors.every((c) => c.isBibliographic)).toBe(true);
- });
-
it('should create license options record', () => {
const licenseOptionsRecord = component.licenseOptionsRecord();
expect(licenseOptionsRecord).toEqual({ copyrightHolders: 'John Doe', year: '2023' });
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 132fa9dde..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,13 +25,18 @@ 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, FetchSelectedSubjects, GetAllContributors, SubjectsSelectors } from '@shared/stores';
+import {
+ ContributorsSelectors,
+ GetBibliographicContributors,
+ LoadMoreBibliographicContributors,
+} from '@shared/stores/contributors';
import { FetchResourceInstitutions, InstitutionsSelectors } from '@shared/stores/institutions';
+import { FetchSelectedSubjects, SubjectsSelectors } from '@shared/stores/subjects';
@Component({
selector: 'osf-review-step',
@@ -44,13 +48,9 @@ import { FetchResourceInstitutions, InstitutionsSelectors } from '@shared/stores
Button,
TitleCasePipe,
TranslatePipe,
- Accordion,
- AccordionContent,
- AccordionHeader,
- AccordionPanel,
- InterpolatePipe,
AffiliatedInstitutionsViewComponent,
ContributorsListComponent,
+ LicenseDisplayComponent,
],
templateUrl: './review-step.component.html',
styleUrl: './review-step.component.scss',
@@ -60,7 +60,7 @@ export class ReviewStepComponent implements OnInit {
private router = inject(Router);
private toastService = inject(ToastService);
private actions = createDispatchMap({
- getContributors: GetAllContributors,
+ getBibliographicContributors: GetBibliographicContributors,
fetchSubjects: FetchSelectedSubjects,
fetchLicenses: FetchLicenses,
fetchPreprintProject: FetchPreprintProject,
@@ -68,6 +68,7 @@ export class ReviewStepComponent implements OnInit {
fetchResourceInstitutions: FetchResourceInstitutions,
updatePrimaryFileRelationship: UpdatePrimaryFileRelationship,
updatePreprint: UpdatePreprint,
+ loadMoreBibliographicContributors: LoadMoreBibliographicContributors,
});
provider = input.required();
@@ -76,8 +77,9 @@ export class ReviewStepComponent implements OnInit {
preprintFile = select(PreprintStepperSelectors.getPreprintFile);
isPreprintSubmitting = select(PreprintStepperSelectors.isPreprintSubmitting);
- contributors = select(ContributorsSelectors.getContributors);
- bibliographicContributors = computed(() => this.contributors().filter((contributor) => contributor.isBibliographic));
+ bibliographicContributors = select(ContributorsSelectors.getBibliographicContributors);
+ areContributorsLoading = select(ContributorsSelectors.isBibliographicContributorsLoading);
+ hasMoreBibliographicContributors = select(ContributorsSelectors.hasMoreBibliographicContributors);
subjects = select(SubjectsSelectors.getSelectedSubjects);
affiliatedInstitutions = select(InstitutionsSelectors.getResourceInstitutions);
license = select(PreprintStepperSelectors.getPreprintLicense);
@@ -88,7 +90,7 @@ export class ReviewStepComponent implements OnInit {
readonly PreregLinkInfo = PreregLinkInfo;
ngOnInit(): void {
- this.actions.getContributors(this.preprint()!.id, ResourceType.Preprint);
+ this.actions.getBibliographicContributors(this.preprint()?.id, ResourceType.Preprint);
this.actions.fetchSubjects(this.preprint()!.id, ResourceType.Preprint);
this.actions.fetchLicenses();
this.actions.fetchPreprintProject();
@@ -123,4 +125,8 @@ export class ReviewStepComponent implements OnInit {
cancelSubmission() {
this.router.navigateByUrl('/preprints');
}
+
+ loadMoreContributors(): void {
+ this.actions.loadMoreBibliographicContributors(this.preprint()?.id, ResourceType.Preprint);
+ }
}
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 cc766c833..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,11 +5,10 @@ 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';
-import { ContributorsSelectors } from '@osf/shared/stores';
+import { ContributorsSelectors } from '@osf/shared/stores/contributors';
import {
AdditionalInfoComponent,
@@ -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 8db420ff6..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';
+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/preprints.routes.ts b/src/app/features/preprints/preprints.routes.ts
index 93a9f8654..a1d8b12ad 100644
--- a/src/app/features/preprints/preprints.routes.ts
+++ b/src/app/features/preprints/preprints.routes.ts
@@ -9,7 +9,9 @@ import { PreprintState } from '@osf/features/preprints/store/preprint';
import { PreprintProvidersState } from '@osf/features/preprints/store/preprint-providers';
import { PreprintStepperState } from '@osf/features/preprints/store/preprint-stepper';
import { ConfirmLeavingGuard } from '@shared/guards';
-import { CitationsState, ContributorsState, ProjectsState, SubjectsState } from '@shared/stores';
+import { CitationsState } from '@shared/stores/citations';
+import { ProjectsState } from '@shared/stores/projects';
+import { SubjectsState } from '@shared/stores/subjects';
import { PreprintModerationState } from '../moderation/store/preprint-moderation';
@@ -18,14 +20,7 @@ export const preprintsRoutes: Routes = [
path: '',
component: PreprintsComponent,
providers: [
- provideStates([
- PreprintProvidersState,
- PreprintStepperState,
- ContributorsState,
- SubjectsState,
- PreprintState,
- CitationsState,
- ]),
+ provideStates([PreprintProvidersState, PreprintStepperState, SubjectsState, PreprintState, CitationsState]),
],
children: [
{
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/profile/components/profile-information/profile-information.component.html b/src/app/features/profile/components/profile-information/profile-information.component.html
index d9b6f8aa6..e21756c3f 100644
--- a/src/app/features/profile/components/profile-information/profile-information.component.html
+++ b/src/app/features/profile/components/profile-information/profile-information.component.html
@@ -21,8 +21,8 @@ {{ currentUser()?.fullName }}
-
-
+
+
@if (currentUser()?.social?.orcid) {
![orcid]()
@@ -39,7 +39,7 @@
{{ currentUser()?.fullName }}
@if (isEmploymentAndEducationVisible()) {
-
+
@if (currentUser()?.education?.length) {
@@ -51,7 +51,6 @@
@if (currentUser()?.employment?.length) {
-
{{ currentUser()?.employment?.[0]?.institution }}
diff --git a/src/app/features/profile/components/profile-information/profile-information.component.spec.ts b/src/app/features/profile/components/profile-information/profile-information.component.spec.ts
index c1ca8d324..eb0415ab0 100644
--- a/src/app/features/profile/components/profile-information/profile-information.component.spec.ts
+++ b/src/app/features/profile/components/profile-information/profile-information.component.spec.ts
@@ -156,15 +156,6 @@ describe('ProfileInformationComponent', () => {
expect(socials).toEqual([]);
});
- it('should not include profileWebsites in social links', () => {
- fixture.componentRef.setInput('currentUser', mockUser);
- fixture.detectChanges();
-
- const socials = component.userSocials();
- const websites = socials.filter((s) => s.alt === 'settings.profileSettings.social.labels.profileWebsites');
- expect(websites.length).toBe(0);
- });
-
it('should emit editProfile event when called', (done) => {
component.editProfile.subscribe(() => {
expect(true).toBe(true);
diff --git a/src/app/features/profile/helpers/user-socials.helper.ts b/src/app/features/profile/helpers/user-socials.helper.ts
index e4fd979f1..65b739f42 100644
--- a/src/app/features/profile/helpers/user-socials.helper.ts
+++ b/src/app/features/profile/helpers/user-socials.helper.ts
@@ -26,7 +26,7 @@ export function mapUserSocials(
url = social.address + value;
}
- if (url && social.key !== 'profileWebsites') {
+ if (url) {
acc.push({
url,
icon: `assets/icons/socials/${social.icon}`,
diff --git a/src/app/features/project/linked-services/linked-services.component.spec.ts b/src/app/features/project/linked-services/linked-services.component.spec.ts
index 909cb5837..011fc0289 100644
--- a/src/app/features/project/linked-services/linked-services.component.spec.ts
+++ b/src/app/features/project/linked-services/linked-services.component.spec.ts
@@ -5,7 +5,7 @@ import { ActivatedRoute } from '@angular/router';
import { UserSelectors } from '@core/store/user';
import { LoadingSpinnerComponent, SubHeaderComponent } from '@osf/shared/components';
-import { AddonsSelectors } from '@shared/stores';
+import { AddonsSelectors } from '@shared/stores/addons';
import { CurrentResourceSelectors } from '@shared/stores/current-resource';
import { LinkedServicesComponent } from './linked-services.component';
diff --git a/src/app/features/project/linked-services/linked-services.component.ts b/src/app/features/project/linked-services/linked-services.component.ts
index ff1e5834f..aea781e17 100644
--- a/src/app/features/project/linked-services/linked-services.component.ts
+++ b/src/app/features/project/linked-services/linked-services.component.ts
@@ -11,7 +11,7 @@ import { UserSelectors } from '@core/store/user';
import { LoadingSpinnerComponent, SubHeaderComponent } from '@shared/components';
import { AddonServiceNames } from '@shared/enums';
import { convertCamelCaseToNormal } from '@shared/helpers';
-import { AddonsSelectors, GetAddonsResourceReference, GetConfiguredLinkAddons } from '@shared/stores';
+import { AddonsSelectors, GetAddonsResourceReference, GetConfiguredLinkAddons } from '@shared/stores/addons';
import { CurrentResourceSelectors } from '@shared/stores/current-resource';
@Component({
diff --git a/src/app/features/project/overview/components/add-component-dialog/add-component-dialog.component.ts b/src/app/features/project/overview/components/add-component-dialog/add-component-dialog.component.ts
index 2e603a2d5..a996a4cfe 100644
--- a/src/app/features/project/overview/components/add-component-dialog/add-component-dialog.component.ts
+++ b/src/app/features/project/overview/components/add-component-dialog/add-component-dialog.component.ts
@@ -19,8 +19,8 @@ import { ComponentFormControls } from '@osf/shared/enums';
import { CustomValidators } from '@osf/shared/helpers';
import { ComponentForm, Institution } from '@osf/shared/models';
import { ToastService } from '@osf/shared/services';
-import { FetchRegions, RegionsSelectors } from '@osf/shared/stores';
import { FetchUserInstitutions, InstitutionsSelectors } from '@osf/shared/stores/institutions';
+import { FetchRegions, RegionsSelectors } from '@osf/shared/stores/regions';
import { CreateComponent, GetComponents, ProjectOverviewSelectors } from '../../store';
diff --git a/src/app/features/project/overview/components/citation-addon-card/citation-addon-card.component.ts b/src/app/features/project/overview/components/citation-addon-card/citation-addon-card.component.ts
index e0c9ac1bc..d8e56fdf6 100644
--- a/src/app/features/project/overview/components/citation-addon-card/citation-addon-card.component.ts
+++ b/src/app/features/project/overview/components/citation-addon-card/citation-addon-card.component.ts
@@ -25,8 +25,8 @@ import { OperationNames, StorageItemType } from '@shared/enums';
import { formatCitation, getItemUrl } from '@shared/helpers';
import { CitationStyle, ConfiguredAddonModel, CustomOption, StorageItem } from '@shared/models';
import { AddonOperationInvocationService, CslStyleManagerService } from '@shared/services';
-import { CitationsSelectors, GetCitationStyles } from '@shared/stores';
import { AddonsSelectors, CreateCitationAddonOperationInvocation } from '@shared/stores/addons';
+import { CitationsSelectors, GetCitationStyles } from '@shared/stores/citations';
import '@citation-js/plugin-csl';
diff --git a/src/app/features/project/overview/components/delete-component-dialog/delete-component-dialog.component.ts b/src/app/features/project/overview/components/delete-component-dialog/delete-component-dialog.component.ts
index 85a36db14..fcbd08125 100644
--- a/src/app/features/project/overview/components/delete-component-dialog/delete-component-dialog.component.ts
+++ b/src/app/features/project/overview/components/delete-component-dialog/delete-component-dialog.component.ts
@@ -16,7 +16,7 @@ import { RegistryOverviewSelectors } from '@osf/features/registry/store/registry
import { ScientistsNames } from '@osf/shared/constants';
import { ResourceType, UserPermissions } from '@osf/shared/enums';
import { ToastService } from '@osf/shared/services';
-import { CurrentResourceSelectors } from '@osf/shared/stores';
+import { CurrentResourceSelectors } from '@osf/shared/stores/current-resource';
import { GetComponents, ProjectOverviewSelectors } from '../../store';
diff --git a/src/app/features/project/overview/components/delete-node-link-dialog/delete-node-link-dialog.component.ts b/src/app/features/project/overview/components/delete-node-link-dialog/delete-node-link-dialog.component.ts
index 97d9f20f3..78311fe83 100644
--- a/src/app/features/project/overview/components/delete-node-link-dialog/delete-node-link-dialog.component.ts
+++ b/src/app/features/project/overview/components/delete-node-link-dialog/delete-node-link-dialog.component.ts
@@ -8,7 +8,7 @@ import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
import { ChangeDetectionStrategy, Component, DestroyRef, inject } from '@angular/core';
import { ToastService } from '@osf/shared/services';
-import { DeleteNodeLink, GetLinkedResources, NodeLinksSelectors } from '@osf/shared/stores';
+import { DeleteNodeLink, GetLinkedResources, NodeLinksSelectors } from '@osf/shared/stores/node-links';
import { ProjectOverviewSelectors } from '../../store';
diff --git a/src/app/features/project/overview/components/link-resource-dialog/link-resource-dialog.component.ts b/src/app/features/project/overview/components/link-resource-dialog/link-resource-dialog.component.ts
index 0f2058ec1..d0625131a 100644
--- a/src/app/features/project/overview/components/link-resource-dialog/link-resource-dialog.component.ts
+++ b/src/app/features/project/overview/components/link-resource-dialog/link-resource-dialog.component.ts
@@ -28,15 +28,8 @@ import { FormControl, FormsModule } from '@angular/forms';
import { SearchInputComponent } from '@osf/shared/components';
import { ResourceSearchMode, ResourceType } from '@osf/shared/enums';
import { MyResourcesItem, MyResourcesSearchFilters } from '@osf/shared/models';
-import {
- CreateNodeLink,
- DeleteNodeLink,
- GetLinkedResources,
- GetMyProjects,
- GetMyRegistrations,
- MyResourcesSelectors,
- NodeLinksSelectors,
-} from '@osf/shared/stores';
+import { GetMyProjects, GetMyRegistrations, MyResourcesSelectors } from '@osf/shared/stores/my-resources';
+import { CreateNodeLink, DeleteNodeLink, GetLinkedResources, NodeLinksSelectors } from '@osf/shared/stores/node-links';
import { ProjectOverviewSelectors } from '../../store';
diff --git a/src/app/features/project/overview/components/linked-resources/linked-resources.component.ts b/src/app/features/project/overview/components/linked-resources/linked-resources.component.ts
index 9a163e821..503e1f984 100644
--- a/src/app/features/project/overview/components/linked-resources/linked-resources.component.ts
+++ b/src/app/features/project/overview/components/linked-resources/linked-resources.component.ts
@@ -9,7 +9,7 @@ import { ChangeDetectionStrategy, Component, inject, input } from '@angular/core
import { ContributorsListComponent, IconComponent, TruncatedTextComponent } from '@osf/shared/components';
import { CustomDialogService } from '@osf/shared/services';
-import { NodeLinksSelectors } from '@osf/shared/stores';
+import { NodeLinksSelectors } from '@osf/shared/stores/node-links';
import { DeleteNodeLinkDialogComponent } from '../delete-node-link-dialog/delete-node-link-dialog.component';
import { LinkResourceDialogComponent } from '../link-resource-dialog/link-resource-dialog.component';
diff --git a/src/app/features/project/overview/components/overview-collections/overview-collections.component.ts b/src/app/features/project/overview/components/overview-collections/overview-collections.component.ts
index b85f48c30..addec8fa1 100644
--- a/src/app/features/project/overview/components/overview-collections/overview-collections.component.ts
+++ b/src/app/features/project/overview/components/overview-collections/overview-collections.component.ts
@@ -14,7 +14,7 @@ import { collectionFilterNames } from '@osf/features/collections/constants';
import { SubmissionReviewStatus } from '@osf/features/moderation/enums';
import { StopPropagationDirective } from '@osf/shared/directives';
import { CollectionSubmission, ResourceOverview } from '@osf/shared/models';
-import { CollectionsSelectors, GetProjectSubmissions } from '@osf/shared/stores';
+import { CollectionsSelectors, GetProjectSubmissions } from '@osf/shared/stores/collections';
@Component({
selector: 'osf-overview-collections',
diff --git a/src/app/features/project/overview/components/overview-components/overview-components.component.ts b/src/app/features/project/overview/components/overview-components/overview-components.component.ts
index e66832c11..3f08be57b 100644
--- a/src/app/features/project/overview/components/overview-components/overview-components.component.ts
+++ b/src/app/features/project/overview/components/overview-components/overview-components.component.ts
@@ -12,7 +12,7 @@ import { Router } from '@angular/router';
import { ContributorsListComponent, IconComponent, TruncatedTextComponent } from '@osf/shared/components';
import { ResourceType, UserPermissions } from '@osf/shared/enums';
import { CustomDialogService, LoaderService } from '@osf/shared/services';
-import { GetResourceWithChildren } from '@osf/shared/stores';
+import { GetResourceWithChildren } from '@osf/shared/stores/current-resource';
import { ComponentOverview } from '@shared/models';
import { LoadMoreComponents, ProjectOverviewSelectors } from '../../store';
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 876e1c046..907acd44f 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
@@ -22,13 +22,8 @@ import { ResourceType } from '@osf/shared/enums';
import { ToolbarResource } from '@osf/shared/models';
import { FileSizePipe } from '@osf/shared/pipes';
import { CustomDialogService, ToastService } from '@osf/shared/services';
-import {
- AddResourceToBookmarks,
- BookmarksSelectors,
- GetMyBookmarks,
- MyResourcesSelectors,
- RemoveResourceFromBookmarks,
-} from '@osf/shared/stores';
+import { AddResourceToBookmarks, BookmarksSelectors, RemoveResourceFromBookmarks } from '@osf/shared/stores/bookmarks';
+import { GetMyBookmarks, MyResourcesSelectors } from '@osf/shared/stores/my-resources';
import { hasViewOnlyParam } from '@shared/helpers';
import { DuplicateDialogComponent } from '../duplicate-dialog/duplicate-dialog.component';
diff --git a/src/app/features/project/overview/components/overview-wiki/overview-wiki.component.ts b/src/app/features/project/overview/components/overview-wiki/overview-wiki.component.ts
index 979f1992f..ee38257ec 100644
--- a/src/app/features/project/overview/components/overview-wiki/overview-wiki.component.ts
+++ b/src/app/features/project/overview/components/overview-wiki/overview-wiki.component.ts
@@ -9,7 +9,7 @@ import { ChangeDetectionStrategy, Component, computed, inject, input } from '@an
import { Router } from '@angular/router';
import { MarkdownComponent, TruncatedTextComponent } from '@osf/shared/components';
-import { WikiSelectors } from '@osf/shared/stores';
+import { WikiSelectors } from '@osf/shared/stores/wiki';
@Component({
selector: 'osf-overview-wiki',
diff --git a/src/app/features/project/overview/components/toggle-publicity-dialog/toggle-publicity-dialog.component.ts b/src/app/features/project/overview/components/toggle-publicity-dialog/toggle-publicity-dialog.component.ts
index ad83ae1d8..615be3309 100644
--- a/src/app/features/project/overview/components/toggle-publicity-dialog/toggle-publicity-dialog.component.ts
+++ b/src/app/features/project/overview/components/toggle-publicity-dialog/toggle-publicity-dialog.component.ts
@@ -21,7 +21,7 @@ import { ComponentsSelectionListComponent, LoadingSpinnerComponent } from '@osf/
import { UserPermissions } from '@osf/shared/enums';
import { ComponentCheckboxItemModel } from '@osf/shared/models';
import { ToastService } from '@osf/shared/services';
-import { CurrentResourceSelectors } from '@osf/shared/stores';
+import { CurrentResourceSelectors } from '@osf/shared/stores/current-resource';
import { TogglePublicityStep } from '../../enums';
import { PrivacyStatusModel } from '../../models';
diff --git a/src/app/features/project/overview/project-overview.component.html b/src/app/features/project/overview/project-overview.component.html
index f9d5a7850..f65b70d76 100644
--- a/src/app/features/project/overview/project-overview.component.html
+++ b/src/app/features/project/overview/project-overview.component.html
@@ -89,6 +89,10 @@
[isCollectionsRoute]="isCollectionsRoute()"
[canEdit]="hasAdminAccess()"
[showEditButton]="hasWriteAccess()"
+ [bibliographicContributors]="bibliographicContributors()"
+ [isBibliographicContributorsLoading]="isBibliographicContributorsLoading()"
+ [hasMoreBibliographicContributors]="hasMoreBibliographicContributors()"
+ (loadMoreContributors)="handleLoadMoreContributors()"
/>
diff --git a/src/app/features/project/overview/project-overview.component.ts b/src/app/features/project/overview/project-overview.component.ts
index 8c87fc8e0..291cb4254 100644
--- a/src/app/features/project/overview/project-overview.component.ts
+++ b/src/app/features/project/overview/project-overview.component.ts
@@ -33,25 +33,26 @@ import { Mode, ResourceType } from '@osf/shared/enums';
import { hasViewOnlyParam } from '@osf/shared/helpers';
import { MapProjectOverview } from '@osf/shared/mappers';
import { CustomDialogService, MetaTagsService, ToastService } from '@osf/shared/services';
+import { GetActivityLogs } from '@osf/shared/stores/activity-logs';
import {
AddonsSelectors,
- ClearCollections,
ClearConfiguredAddons,
- ClearWiki,
- CollectionsSelectors,
- CurrentResourceSelectors,
- FetchSelectedSubjects,
GetAddonsResourceReference,
- GetBookmarksCollectionId,
- GetCollectionProvider,
GetConfiguredCitationAddons,
GetConfiguredStorageAddons,
- GetHomeWiki,
- GetLinkedResources,
- GetResourceWithChildren,
- SubjectsSelectors,
-} from '@osf/shared/stores';
-import { GetActivityLogs } from '@osf/shared/stores/activity-logs';
+} from '@osf/shared/stores/addons';
+import { GetBookmarksCollectionId } from '@osf/shared/stores/bookmarks';
+import { ClearCollections, CollectionsSelectors, GetCollectionProvider } from '@osf/shared/stores/collections';
+import {
+ ContributorsSelectors,
+ GetBibliographicContributors,
+ LoadMoreBibliographicContributors,
+ ResetContributorsState,
+} from '@osf/shared/stores/contributors';
+import { CurrentResourceSelectors, GetResourceWithChildren } from '@osf/shared/stores/current-resource';
+import { GetLinkedResources } from '@osf/shared/stores/node-links';
+import { FetchSelectedSubjects, SubjectsSelectors } from '@osf/shared/stores/subjects';
+import { ClearWiki, GetHomeWiki } from '@osf/shared/stores/wiki';
import {
LoadingSpinnerComponent,
MakeDecisionDialogComponent,
@@ -139,6 +140,9 @@ export class ProjectOverviewComponent implements OnInit {
isWikiEnabled = select(ProjectOverviewSelectors.isWikiEnabled);
parentProject = select(ProjectOverviewSelectors.getParentProject);
isParentProjectLoading = select(ProjectOverviewSelectors.getParentProjectLoading);
+ bibliographicContributors = select(ContributorsSelectors.getBibliographicContributors);
+ isBibliographicContributorsLoading = select(ContributorsSelectors.isBibliographicContributorsLoading);
+ hasMoreBibliographicContributors = select(ContributorsSelectors.hasMoreBibliographicContributors);
addonsResourceReference = select(AddonsSelectors.getAddonsResourceReference);
configuredCitationAddons = select(AddonsSelectors.getConfiguredCitationAddons);
operationInvocation = select(AddonsSelectors.getOperationInvocation);
@@ -164,6 +168,9 @@ export class ProjectOverviewComponent implements OnInit {
getParentProject: GetParentProject,
getAddonsResourceReference: GetAddonsResourceReference,
getConfiguredCitationAddons: GetConfiguredCitationAddons,
+ getBibliographicContributors: GetBibliographicContributors,
+ loadMoreBibliographicContributors: LoadMoreBibliographicContributors,
+ resetContributorsState: ResetContributorsState,
});
readonly activityPageSize = 5;
@@ -193,8 +200,9 @@ export class ProjectOverviewComponent implements OnInit {
resourceOverview = computed(() => {
const project = this.currentProject();
const subjects = this.subjects();
+ const bibliographicContributors = this.bibliographicContributors();
if (project) {
- return MapProjectOverview(project, subjects, this.isAnonymous());
+ return MapProjectOverview(project, subjects, this.isAnonymous(), bibliographicContributors);
}
return null;
});
@@ -282,6 +290,10 @@ export class ProjectOverviewComponent implements OnInit {
this.actions.setProjectCustomCitation(citation);
}
+ handleLoadMoreContributors(): void {
+ this.actions.loadMoreBibliographicContributors(this.currentProject()?.id, ResourceType.Project);
+ }
+
ngOnInit(): void {
const projectId = this.route.snapshot.params['id'] || this.route.parent?.snapshot.params['id'];
@@ -291,6 +303,7 @@ export class ProjectOverviewComponent implements OnInit {
this.actions.getComponents(projectId);
this.actions.getLinkedProjects(projectId);
this.actions.getActivityLogs(projectId, this.activityDefaultPage, this.activityPageSize);
+ this.actions.getBibliographicContributors(projectId, ResourceType.Project);
}
this.dataciteService
@@ -385,6 +398,7 @@ export class ProjectOverviewComponent implements OnInit {
this.actions.getComponents(projectId);
this.actions.getLinkedProjects(projectId);
this.actions.getActivityLogs(projectId, this.activityDefaultPage, this.activityPageSize);
+ this.actions.getBibliographicContributors(projectId, ResourceType.Project);
}),
takeUntilDestroyed(this.destroyRef)
)
@@ -398,6 +412,7 @@ export class ProjectOverviewComponent implements OnInit {
this.actions.clearCollections();
this.actions.clearCollectionModeration();
this.actions.clearConfiguredAddons();
+ this.actions.resetContributorsState();
});
}
diff --git a/src/app/features/project/overview/services/project-overview.service.ts b/src/app/features/project/overview/services/project-overview.service.ts
index 4261b22a8..eb8bf543a 100644
--- a/src/app/features/project/overview/services/project-overview.service.ts
+++ b/src/app/features/project/overview/services/project-overview.service.ts
@@ -34,14 +34,7 @@ export class ProjectOverviewService {
getProjectById(projectId: string): Observable
{
const params: Record = {
- 'embed[]': [
- 'bibliographic_contributors',
- 'affiliated_institutions',
- 'identifiers',
- 'license',
- 'storage',
- 'preprints',
- ],
+ 'embed[]': ['affiliated_institutions', 'identifiers', 'license', 'storage', 'preprints'],
'fields[institutions]': 'assets,description,name',
'fields[preprints]': 'title,date_created',
'fields[users]': 'family_name,full_name,given_name,middle_name',
diff --git a/src/app/features/project/project-addons/components/configure-addon/configure-addon.component.spec.ts b/src/app/features/project/project-addons/components/configure-addon/configure-addon.component.spec.ts
index 0dae909d5..182e23e10 100644
--- a/src/app/features/project/project-addons/components/configure-addon/configure-addon.component.spec.ts
+++ b/src/app/features/project/project-addons/components/configure-addon/configure-addon.component.spec.ts
@@ -8,7 +8,7 @@ import { HttpTestingController } from '@angular/common/http/testing';
import { ComponentFixture, inject, TestBed } from '@angular/core/testing';
import { ActivatedRoute, Router } from '@angular/router';
-import { AddonsState } from '@osf/shared/stores';
+import { AddonsState } from '@osf/shared/stores/addons';
import { ConfigureAddonComponent } from './configure-addon.component';
diff --git a/src/app/features/project/project-addons/components/configure-addon/configure-addon.component.ts b/src/app/features/project/project-addons/components/configure-addon/configure-addon.component.ts
index bda7efa20..20a3b1cce 100644
--- a/src/app/features/project/project-addons/components/configure-addon/configure-addon.component.ts
+++ b/src/app/features/project/project-addons/components/configure-addon/configure-addon.component.ts
@@ -33,7 +33,7 @@ import {
CreateAddonOperationInvocation,
GetLinkAddons,
UpdateConfiguredAddon,
-} from '@osf/shared/stores';
+} from '@osf/shared/stores/addons';
import { AddonDialogService } from '../../services/addon-dialog.service';
diff --git a/src/app/features/project/project-addons/components/confirm-account-connection-modal/confirm-account-connection-modal.component.ts b/src/app/features/project/project-addons/components/confirm-account-connection-modal/confirm-account-connection-modal.component.ts
index 5c098c638..8ea7fda4b 100644
--- a/src/app/features/project/project-addons/components/confirm-account-connection-modal/confirm-account-connection-modal.component.ts
+++ b/src/app/features/project/project-addons/components/confirm-account-connection-modal/confirm-account-connection-modal.component.ts
@@ -9,7 +9,7 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { AddonOperationInvocationService } from '@osf/shared/services';
-import { AddonsSelectors, CreateAddonOperationInvocation } from '@osf/shared/stores';
+import { AddonsSelectors, CreateAddonOperationInvocation } from '@osf/shared/stores/addons';
import { OperationNames } from '@shared/enums';
@Component({
diff --git a/src/app/features/project/project-addons/components/connect-configured-addon/connect-configured-addon.component.ts b/src/app/features/project/project-addons/components/connect-configured-addon/connect-configured-addon.component.ts
index 1d9c77d8d..05d1575ee 100644
--- a/src/app/features/project/project-addons/components/connect-configured-addon/connect-configured-addon.component.ts
+++ b/src/app/features/project/project-addons/components/connect-configured-addon/connect-configured-addon.component.ts
@@ -39,7 +39,7 @@ import {
GetAuthorizedStorageAddons,
UpdateAuthorizedAddon,
UpdateConfiguredAddon,
-} from '@osf/shared/stores';
+} from '@osf/shared/stores/addons';
import { AddonConfigMap } from '../../models';
import { AddonDialogService } from '../../services';
diff --git a/src/app/features/project/project-addons/components/disconnect-addon-modal/disconnect-addon-modal.component.ts b/src/app/features/project/project-addons/components/disconnect-addon-modal/disconnect-addon-modal.component.ts
index 4991e4aea..56ed5e320 100644
--- a/src/app/features/project/project-addons/components/disconnect-addon-modal/disconnect-addon-modal.component.ts
+++ b/src/app/features/project/project-addons/components/disconnect-addon-modal/disconnect-addon-modal.component.ts
@@ -9,7 +9,7 @@ import { ChangeDetectionStrategy, Component, computed, inject } from '@angular/c
import { AddonType } from '@osf/shared/enums';
import { getAddonTypeString } from '@osf/shared/helpers';
-import { AddonsSelectors, DeleteConfiguredAddon } from '@osf/shared/stores';
+import { AddonsSelectors, DeleteConfiguredAddon } from '@osf/shared/stores/addons';
@Component({
selector: 'osf-disconnect-addon-modal',
@@ -31,9 +31,8 @@ export class DisconnectAddonModalComponent {
? 'settings.addons.configureAddon.linkedItem'
: 'settings.addons.configureAddon.selectedFolder';
});
- actions = createDispatchMap({
- deleteConfiguredAddon: DeleteConfiguredAddon,
- });
+
+ actions = createDispatchMap({ deleteConfiguredAddon: DeleteConfiguredAddon });
handleDisconnectAddonAccount(): void {
if (!this.addon) return;
diff --git a/src/app/features/project/project-addons/project-addons.component.spec.ts b/src/app/features/project/project-addons/project-addons.component.spec.ts
index c29ff66bf..e4f6b109b 100644
--- a/src/app/features/project/project-addons/project-addons.component.spec.ts
+++ b/src/app/features/project/project-addons/project-addons.component.spec.ts
@@ -4,7 +4,7 @@ import { signal } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { UserSelectors, UserState } from '@core/store/user';
-import { AddonsState } from '@osf/shared/stores';
+import { AddonsState } from '@osf/shared/stores/addons';
import { ProjectAddonsComponent } from './project-addons.component';
diff --git a/src/app/features/project/project.routes.ts b/src/app/features/project/project.routes.ts
index 5d7d3bae8..a6af0de78 100644
--- a/src/app/features/project/project.routes.ts
+++ b/src/app/features/project/project.routes.ts
@@ -5,16 +5,13 @@ import { Routes } from '@angular/router';
import { viewOnlyGuard } from '@osf/core/guards';
import { ResourceType } from '@osf/shared/enums';
import { LicensesService } from '@osf/shared/services';
-import {
- CitationsState,
- CollectionsState,
- ContributorsState,
- DuplicatesState,
- NodeLinksState,
- SubjectsState,
- ViewOnlyLinkState,
-} from '@osf/shared/stores';
import { ActivityLogsState } from '@osf/shared/stores/activity-logs';
+import { CitationsState } from '@osf/shared/stores/citations';
+import { CollectionsState } from '@osf/shared/stores/collections';
+import { DuplicatesState } from '@osf/shared/stores/duplicates';
+import { NodeLinksState } from '@osf/shared/stores/node-links';
+import { SubjectsState } from '@osf/shared/stores/subjects';
+import { ViewOnlyLinkState } from '@osf/shared/stores/view-only-links';
import { AnalyticsState } from '../analytics/store';
import { CollectionsModerationState } from '../moderation/store/collections-moderation';
@@ -53,7 +50,7 @@ export const projectRoutes: Routes = [
{
path: 'metadata',
loadChildren: () => import('@osf/features/metadata/metadata.routes').then((mod) => mod.metadataRoutes),
- providers: [provideStates([SubjectsState, ContributorsState])],
+ providers: [provideStates([SubjectsState])],
data: { resourceType: ResourceType.Project },
canActivate: [viewOnlyGuard],
},
@@ -87,7 +84,7 @@ export const projectRoutes: Routes = [
canActivate: [viewOnlyGuard],
loadComponent: () => import('../contributors/contributors.component').then((mod) => mod.ContributorsComponent),
data: { resourceType: ResourceType.Project },
- providers: [provideStates([ContributorsState, ViewOnlyLinkState])],
+ providers: [provideStates([ViewOnlyLinkState])],
},
{
path: 'analytics',
diff --git a/src/app/features/project/registrations/registrations.component.ts b/src/app/features/project/registrations/registrations.component.ts
index 8ac6abb1c..bf0102113 100644
--- a/src/app/features/project/registrations/registrations.component.ts
+++ b/src/app/features/project/registrations/registrations.component.ts
@@ -18,7 +18,7 @@ import {
RegistrationCardComponent,
SubHeaderComponent,
} from '@osf/shared/components';
-import { CurrentResourceSelectors } from '@shared/stores';
+import { CurrentResourceSelectors } from '@shared/stores/current-resource';
import { GetRegistrations, RegistrationsSelectors } from './store';
diff --git a/src/app/features/project/settings/components/delete-project-dialog/delete-project-dialog.component.ts b/src/app/features/project/settings/components/delete-project-dialog/delete-project-dialog.component.ts
index 1a6a7713c..39cdb738e 100644
--- a/src/app/features/project/settings/components/delete-project-dialog/delete-project-dialog.component.ts
+++ b/src/app/features/project/settings/components/delete-project-dialog/delete-project-dialog.component.ts
@@ -15,7 +15,7 @@ import { Router } from '@angular/router';
import { ScientistsNames } from '@osf/shared/constants';
import { UserPermissions } from '@osf/shared/enums';
import { ToastService } from '@osf/shared/services';
-import { CurrentResourceSelectors } from '@osf/shared/stores';
+import { CurrentResourceSelectors } from '@osf/shared/stores/current-resource';
import { DeleteProject, SettingsSelectors } from '../../store';
diff --git a/src/app/features/project/settings/components/settings-project-affiliation/settings-project-affiliation.component.spec.ts b/src/app/features/project/settings/components/settings-project-affiliation/settings-project-affiliation.component.spec.ts
index 74290b667..85f9371a3 100644
--- a/src/app/features/project/settings/components/settings-project-affiliation/settings-project-affiliation.component.spec.ts
+++ b/src/app/features/project/settings/components/settings-project-affiliation/settings-project-affiliation.component.spec.ts
@@ -1,7 +1,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Institution } from '@osf/shared/models';
-import { InstitutionsSelectors } from '@osf/shared/stores';
+import { InstitutionsSelectors } from '@osf/shared/stores/institutions';
import { SettingsProjectAffiliationComponent } from './settings-project-affiliation.component';
diff --git a/src/app/features/project/settings/components/settings-project-affiliation/settings-project-affiliation.component.ts b/src/app/features/project/settings/components/settings-project-affiliation/settings-project-affiliation.component.ts
index 6d55ef188..9e764d439 100644
--- a/src/app/features/project/settings/components/settings-project-affiliation/settings-project-affiliation.component.ts
+++ b/src/app/features/project/settings/components/settings-project-affiliation/settings-project-affiliation.component.ts
@@ -9,7 +9,7 @@ import { NgOptimizedImage } from '@angular/common';
import { ChangeDetectionStrategy, Component, computed, input, OnInit, output } from '@angular/core';
import { Institution } from '@osf/shared/models';
-import { FetchUserInstitutions, InstitutionsSelectors } from '@shared/stores';
+import { FetchUserInstitutions, InstitutionsSelectors } from '@shared/stores/institutions';
@Component({
selector: 'osf-settings-project-affiliation',
diff --git a/src/app/features/project/settings/settings.component.spec.ts b/src/app/features/project/settings/settings.component.spec.ts
index 18458936b..ed275c037 100644
--- a/src/app/features/project/settings/settings.component.spec.ts
+++ b/src/app/features/project/settings/settings.component.spec.ts
@@ -14,7 +14,7 @@ import {
} from '@osf/features/project/settings/components';
import { LoadingSpinnerComponent, SubHeaderComponent } from '@osf/shared/components';
import { CustomConfirmationService, LoaderService, ToastService } from '@osf/shared/services';
-import { ViewOnlyLinkSelectors } from '@osf/shared/stores';
+import { ViewOnlyLinkSelectors } from '@osf/shared/stores/view-only-links';
import { SettingsComponent } from './settings.component';
import { SettingsSelectors } from './store';
diff --git a/src/app/features/project/settings/settings.component.ts b/src/app/features/project/settings/settings.component.ts
index 3d00d1697..51cba11f5 100644
--- a/src/app/features/project/settings/settings.component.ts
+++ b/src/app/features/project/settings/settings.component.ts
@@ -14,13 +14,8 @@ import { LoadingSpinnerComponent, SubHeaderComponent } from '@osf/shared/compone
import { ResourceType, SubscriptionEvent, SubscriptionFrequency } from '@osf/shared/enums';
import { Institution, UpdateNodeRequestModel, ViewOnlyLinkModel } from '@osf/shared/models';
import { CustomConfirmationService, CustomDialogService, LoaderService, ToastService } from '@osf/shared/services';
-import {
- DeleteViewOnlyLink,
- FetchViewOnlyLinks,
- GetResource,
- GetResourceWithChildren,
- ViewOnlyLinkSelectors,
-} from '@osf/shared/stores';
+import { GetResource, GetResourceWithChildren } from '@osf/shared/stores/current-resource';
+import { DeleteViewOnlyLink, FetchViewOnlyLinks, ViewOnlyLinkSelectors } from '@osf/shared/stores/view-only-links';
import {
DeleteProjectDialogComponent,
diff --git a/src/app/features/project/wiki/legacy-wiki-redirect.component.ts b/src/app/features/project/wiki/legacy-wiki-redirect.component.ts
index 090ccb30e..fc4515624 100644
--- a/src/app/features/project/wiki/legacy-wiki-redirect.component.ts
+++ b/src/app/features/project/wiki/legacy-wiki-redirect.component.ts
@@ -8,7 +8,7 @@ import { ActivatedRoute, Router } from '@angular/router';
import { ResourceType } from '@osf/shared/enums';
import { LoaderService } from '@osf/shared/services';
-import { GetWikiList, WikiSelectors } from '@osf/shared/stores';
+import { GetWikiList, WikiSelectors } from '@osf/shared/stores/wiki';
@Component({
template: '',
diff --git a/src/app/features/project/wiki/wiki.component.spec.ts b/src/app/features/project/wiki/wiki.component.spec.ts
index a6a823274..a9f76c326 100644
--- a/src/app/features/project/wiki/wiki.component.spec.ts
+++ b/src/app/features/project/wiki/wiki.component.spec.ts
@@ -19,7 +19,7 @@ import {
ViewSectionComponent,
WikiListComponent,
} from '@shared/components/wiki';
-import { WikiState } from '@shared/stores';
+import { WikiState } from '@shared/stores/wiki';
describe('WikiComponent', () => {
let component: WikiComponent;
diff --git a/src/app/features/project/wiki/wiki.component.ts b/src/app/features/project/wiki/wiki.component.ts
index 7a324ed22..38eedf314 100644
--- a/src/app/features/project/wiki/wiki.component.ts
+++ b/src/app/features/project/wiki/wiki.component.ts
@@ -22,11 +22,11 @@ import { ResourceType } from '@osf/shared/enums';
import { hasViewOnlyParam } from '@osf/shared/helpers';
import { WikiModes } from '@osf/shared/models';
import { ToastService } from '@osf/shared/services';
+import { CurrentResourceSelectors } from '@osf/shared/stores/current-resource';
import {
ClearWiki,
CreateWiki,
CreateWikiVersion,
- CurrentResourceSelectors,
DeleteWiki,
GetCompareVersionContent,
GetComponentsWikiList,
@@ -39,7 +39,7 @@ import {
ToggleMode,
UpdateWikiPreviewContent,
WikiSelectors,
-} from '@osf/shared/stores';
+} from '@osf/shared/stores/wiki';
import { ViewOnlyLinkMessageComponent } from '@shared/components/view-only-link-message/view-only-link-message.component';
@Component({
diff --git a/src/app/features/registries/components/custom-step/custom-step.component.html b/src/app/features/registries/components/custom-step/custom-step.component.html
index a8cdbc290..d8e434a69 100644
--- a/src/app/features/registries/components/custom-step/custom-step.component.html
+++ b/src/app/features/registries/components/custom-step/custom-step.component.html
@@ -30,7 +30,7 @@ {{ section.title }}
@if (section.description) {
-
{{ section.description }}
+
{{ section.description }}
}
@@ -40,151 +40,150 @@
{{ section.title }}
@for (q of questions; track q.id) {
}
diff --git a/src/app/features/registries/components/drafts/drafts.component.spec.ts b/src/app/features/registries/components/drafts/drafts.component.spec.ts
index cda932900..a38d7d65c 100644
--- a/src/app/features/registries/components/drafts/drafts.component.spec.ts
+++ b/src/app/features/registries/components/drafts/drafts.component.spec.ts
@@ -7,7 +7,8 @@ import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
import { RegistriesSelectors } from '@osf/features/registries/store';
import { StepperComponent, SubHeaderComponent } from '@osf/shared/components';
-import { ContributorsSelectors, SubjectsSelectors } from '@shared/stores';
+import { ContributorsSelectors } from '@shared/stores/contributors';
+import { SubjectsSelectors } from '@shared/stores/subjects';
import { DraftsComponent } from './drafts.component';
diff --git a/src/app/features/registries/components/drafts/drafts.component.ts b/src/app/features/registries/components/drafts/drafts.component.ts
index 4c4c4c04f..ad9c6262c 100644
--- a/src/app/features/registries/components/drafts/drafts.component.ts
+++ b/src/app/features/registries/components/drafts/drafts.component.ts
@@ -22,12 +22,8 @@ import { StepperComponent, SubHeaderComponent } from '@osf/shared/components';
import { ResourceType } from '@osf/shared/enums';
import { PageSchema, Question, StepOption } from '@osf/shared/models';
import { LoaderService } from '@osf/shared/services';
-import {
- ContributorsSelectors,
- FetchSelectedSubjects,
- GetAllContributors,
- SubjectsSelectors,
-} from '@osf/shared/stores';
+import { ContributorsSelectors, GetAllContributors } from '@osf/shared/stores/contributors';
+import { FetchSelectedSubjects, SubjectsSelectors } from '@osf/shared/stores/subjects';
import { DEFAULT_STEPS } from '../../constants';
import { ClearState, FetchDraft, FetchSchemaBlocks, RegistriesSelectors, UpdateStepState } from '../../store';
diff --git a/src/app/features/registries/components/justification-review/justification-review.component.html b/src/app/features/registries/components/justification-review/justification-review.component.html
index 5f8c45cda..480466477 100644
--- a/src/app/features/registries/components/justification-review/justification-review.component.html
+++ b/src/app/features/registries/components/justification-review/justification-review.component.html
@@ -1,7 +1,8 @@
-
+
{{ 'registries.justification.step' | translate }}
{{ 'registries.justification.title' | translate }}
+
@if (schemaResponse()?.revisionJustification) {
{{ schemaResponse()?.revisionJustification }}
} @else {
@@ -12,7 +13,9 @@ {{ 'registries.justification.title' | translate }}
}
+
{{ 'registries.justification.updatedList' | translate }}
+
@if (changes().length) {
@for (item of changes(); track item) {
{{ item }}
@@ -21,6 +24,7 @@
{{ 'registries.justification.updatedList' | translate }}
{{ 'registries.justification.noUpdates' | translate }}
}
+
@for (page of pages(); track page.id) {
{{ page.title }}
@@ -28,6 +32,14 @@ {{ page.title }}
@if (page.description) {
{{ page.description }}
}
+
+ @if (page.questions?.length) {
+
+ }
+
@if (page.sections?.length) {
@for (section of page.sections; track section.id) {
@@ -43,51 +55,39 @@
{{ section.title }}
}
}
- } @else {
- @if (page.questions?.length) {
-
- }
}
}
-
-
- @if (inProgress) {
-
-
-
-
- } @else if (isUnapproved) {
-
-
- }
-
+
+
+ @if (inProgress) {
+
+
+
+
+
+
+ } @else if (isUnapproved) {
+
+
+
+ }
diff --git a/src/app/features/registries/components/registries-metadata-step/registries-affiliated-institution/registries-affiliated-institution.component.spec.ts b/src/app/features/registries/components/registries-metadata-step/registries-affiliated-institution/registries-affiliated-institution.component.spec.ts
index ef6845138..1ca5f56ba 100644
--- a/src/app/features/registries/components/registries-metadata-step/registries-affiliated-institution/registries-affiliated-institution.component.spec.ts
+++ b/src/app/features/registries/components/registries-metadata-step/registries-affiliated-institution/registries-affiliated-institution.component.spec.ts
@@ -1,7 +1,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ActivatedRoute } from '@angular/router';
-import { InstitutionsSelectors } from '@osf/shared/stores';
+import { InstitutionsSelectors } from '@osf/shared/stores/institutions';
import { RegistriesAffiliatedInstitutionComponent } from './registries-affiliated-institution.component';
diff --git a/src/app/features/registries/components/registries-metadata-step/registries-affiliated-institution/registries-affiliated-institution.component.ts b/src/app/features/registries/components/registries-metadata-step/registries-affiliated-institution/registries-affiliated-institution.component.ts
index 1a6ffdad8..261eceddb 100644
--- a/src/app/features/registries/components/registries-metadata-step/registries-affiliated-institution/registries-affiliated-institution.component.ts
+++ b/src/app/features/registries/components/registries-metadata-step/registries-affiliated-institution/registries-affiliated-institution.component.ts
@@ -15,7 +15,7 @@ import {
FetchUserInstitutions,
InstitutionsSelectors,
UpdateResourceInstitutions,
-} from '@osf/shared/stores';
+} from '@osf/shared/stores/institutions';
@Component({
selector: 'osf-registries-affiliated-institution',
diff --git a/src/app/features/registries/components/registries-metadata-step/registries-contributors/registries-contributors.component.html b/src/app/features/registries/components/registries-metadata-step/registries-contributors/registries-contributors.component.html
index 7726dae69..70164d25d 100644
--- a/src/app/features/registries/components/registries-metadata-step/registries-contributors/registries-contributors.component.html
+++ b/src/app/features/registries/components/registries-metadata-step/registries-contributors/registries-contributors.component.html
@@ -6,8 +6,9 @@
{{ 'project.overview.metadata.contributors' | translate }}
[(contributors)]="contributors"
[tableParams]="tableParams()"
[isLoading]="isContributorsLoading()"
+ [isLoadingMore]="isLoadingMore()"
(remove)="removeContributor($event)"
- (pageChanged)="pageChanged($event)"
+ (loadMore)="loadMoreContributors()"
/>
diff --git a/src/app/features/registries/components/registries-metadata-step/registries-contributors/registries-contributors.component.spec.ts b/src/app/features/registries/components/registries-metadata-step/registries-contributors/registries-contributors.component.spec.ts
index 35d007b66..bbef5fbd3 100644
--- a/src/app/features/registries/components/registries-metadata-step/registries-contributors/registries-contributors.component.spec.ts
+++ b/src/app/features/registries/components/registries-metadata-step/registries-contributors/registries-contributors.component.spec.ts
@@ -9,8 +9,8 @@ import { ActivatedRoute } from '@angular/router';
import { UserSelectors } from '@core/store/user';
import { ResourceType } from '@osf/shared/enums';
import { CustomConfirmationService, CustomDialogService, ToastService } from '@osf/shared/services';
-import { ContributorsSelectors } from '@osf/shared/stores';
-import { ContributorsTableComponent } from '@shared/components/contributors';
+import { ContributorsSelectors } from '@osf/shared/stores/contributors/contributors.selectors';
+import { ContributorsTableComponent } from '@shared/components/contributors/contributors-table/contributors-table.component';
import { RegistriesContributorsComponent } from './registries-contributors.component';
@@ -77,15 +77,12 @@ describe('RegistriesContributorsComponent', () => {
deleteContributor: jest.fn().mockReturnValue(of({})),
bulkUpdateContributors: jest.fn().mockReturnValue(of({})),
bulkAddContributors: jest.fn().mockReturnValue(of({})),
+ resetContributorsState: jest.fn().mockRejectedValue(of({})),
} as any;
Object.defineProperty(component, 'actions', { value: mockActions });
fixture.detectChanges();
});
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-
it('should request contributors on init', () => {
const actions = (component as any).actions;
expect(actions.getContributors).toHaveBeenCalledWith('draft-1', ResourceType.DraftRegistration);
diff --git a/src/app/features/registries/components/registries-metadata-step/registries-contributors/registries-contributors.component.ts b/src/app/features/registries/components/registries-metadata-step/registries-contributors/registries-contributors.component.ts
index bf07f0974..1a8a71f66 100644
--- a/src/app/features/registries/components/registries-metadata-step/registries-contributors/registries-contributors.component.ts
+++ b/src/app/features/registries/components/registries-metadata-step/registries-contributors/registries-contributors.component.ts
@@ -4,7 +4,7 @@ import { TranslatePipe } from '@ngx-translate/core';
import { Button } from 'primeng/button';
import { Card } from 'primeng/card';
-import { TableModule, TablePageEvent } from 'primeng/table';
+import { TableModule } from 'primeng/table';
import { filter, map, of } from 'rxjs';
@@ -16,6 +16,7 @@ import {
effect,
inject,
input,
+ OnDestroy,
OnInit,
signal,
} from '@angular/core';
@@ -40,7 +41,9 @@ import {
ContributorsSelectors,
DeleteContributor,
GetAllContributors,
-} from '@osf/shared/stores';
+ LoadMoreContributors,
+ ResetContributorsState,
+} from '@osf/shared/stores/contributors';
@Component({
selector: 'osf-registries-contributors',
@@ -49,7 +52,7 @@ import {
styleUrl: './registries-contributors.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class RegistriesContributorsComponent implements OnInit {
+export class RegistriesContributorsComponent implements OnInit, OnDestroy {
control = input.required
();
readonly destroyRef = inject(DestroyRef);
@@ -65,14 +68,15 @@ export class RegistriesContributorsComponent implements OnInit {
isContributorsLoading = select(ContributorsSelectors.isContributorsLoading);
contributorsTotalCount = select(ContributorsSelectors.getContributorsTotalCount);
- page = select(ContributorsSelectors.getContributorsPageNumber);
+ isLoadingMore = select(ContributorsSelectors.isContributorsLoadingMore);
pageSize = select(ContributorsSelectors.getContributorsPageSize);
readonly tableParams = computed(() => ({
...DEFAULT_TABLE_PARAMS,
totalRecords: this.contributorsTotalCount(),
- paginator: this.contributorsTotalCount() > DEFAULT_TABLE_PARAMS.rows,
- firstRowIndex: (this.page() - 1) * this.pageSize(),
+ paginator: false,
+ scrollable: true,
+ firstRowIndex: 0,
rows: this.pageSize(),
}));
@@ -82,6 +86,8 @@ export class RegistriesContributorsComponent implements OnInit {
bulkUpdateContributors: BulkUpdateContributors,
bulkAddContributors: BulkAddContributors,
addContributor: AddContributor,
+ loadMoreContributors: LoadMoreContributors,
+ resetContributorsState: ResetContributorsState,
});
get hasChanges(): boolean {
@@ -98,6 +104,10 @@ export class RegistriesContributorsComponent implements OnInit {
this.actions.getContributors(this.draftId(), ResourceType.DraftRegistration);
}
+ ngOnDestroy(): void {
+ this.actions.resetContributorsState();
+ }
+
onFocusOut() {
if (this.control()) {
this.control().markAsTouched();
@@ -122,13 +132,10 @@ export class RegistriesContributorsComponent implements OnInit {
}
openAddContributorDialog() {
- const addedContributorIds = this.initialContributors().map((x) => x.userId);
-
this.customDialogService
.open(AddContributorDialogComponent, {
header: 'project.contributors.addDialog.addRegisteredContributor',
width: '448px',
- data: addedContributorIds,
})
.onClose.pipe(
filter((res: ContributorDialogAddModel) => !!res),
@@ -192,10 +199,7 @@ export class RegistriesContributorsComponent implements OnInit {
});
}
- pageChanged(event: TablePageEvent) {
- const page = Math.floor(event.first / event.rows) + 1;
- const pageSize = event.rows;
-
- this.actions.getContributors(this.draftId(), ResourceType.DraftRegistration, page, pageSize);
+ loadMoreContributors(): void {
+ this.actions.loadMoreContributors(this.draftId(), ResourceType.DraftRegistration);
}
}
diff --git a/src/app/features/registries/components/registries-metadata-step/registries-metadata-step.component.spec.ts b/src/app/features/registries/components/registries-metadata-step/registries-metadata-step.component.spec.ts
index 02fc8dfe6..cac750345 100644
--- a/src/app/features/registries/components/registries-metadata-step/registries-metadata-step.component.spec.ts
+++ b/src/app/features/registries/components/registries-metadata-step/registries-metadata-step.component.spec.ts
@@ -3,15 +3,18 @@ import { MockComponents, MockProvider } from 'ng-mocks';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ActivatedRoute, Router } from '@angular/router';
-import { RegistriesContributorsComponent } from '@osf/features/registries/components/registries-metadata-step/registries-contributors/registries-contributors.component';
-import { RegistriesLicenseComponent } from '@osf/features/registries/components/registries-metadata-step/registries-license/registries-license.component';
-import { RegistriesSubjectsComponent } from '@osf/features/registries/components/registries-metadata-step/registries-subjects/registries-subjects.component';
-import { RegistriesTagsComponent } from '@osf/features/registries/components/registries-metadata-step/registries-tags/registries-tags.component';
-import { RegistriesSelectors } from '@osf/features/registries/store';
import { CustomConfirmationService } from '@osf/shared/services';
-import { ContributorsSelectors, InstitutionsSelectors, SubjectsSelectors } from '@osf/shared/stores';
+import { ContributorsSelectors } from '@osf/shared/stores/contributors';
+import { InstitutionsSelectors } from '@osf/shared/stores/institutions';
+import { SubjectsSelectors } from '@osf/shared/stores/subjects';
import { TextInputComponent } from '@shared/components';
+import { RegistriesSelectors } from '../../store';
+
+import { RegistriesContributorsComponent } from './registries-contributors/registries-contributors.component';
+import { RegistriesLicenseComponent } from './registries-license/registries-license.component';
+import { RegistriesSubjectsComponent } from './registries-subjects/registries-subjects.component';
+import { RegistriesTagsComponent } from './registries-tags/registries-tags.component';
import { RegistriesMetadataStepComponent } from './registries-metadata-step.component';
import { OSFTestingModule } from '@testing/osf.testing.module';
diff --git a/src/app/features/registries/components/registries-metadata-step/registries-metadata-step.component.ts b/src/app/features/registries/components/registries-metadata-step/registries-metadata-step.component.ts
index 977b252c9..2ca52b2ab 100644
--- a/src/app/features/registries/components/registries-metadata-step/registries-metadata-step.component.ts
+++ b/src/app/features/registries/components/registries-metadata-step/registries-metadata-step.component.ts
@@ -18,7 +18,8 @@ import { INPUT_VALIDATION_MESSAGES, InputLimits } from '@osf/shared/constants';
import { CustomValidators, findChangedFields } from '@osf/shared/helpers';
import { ContributorModel, DraftRegistrationModel, SubjectModel } from '@osf/shared/models';
import { CustomConfirmationService } from '@osf/shared/services';
-import { ContributorsSelectors, SubjectsSelectors } from '@osf/shared/stores';
+import { ContributorsSelectors } from '@osf/shared/stores/contributors';
+import { SubjectsSelectors } from '@osf/shared/stores/subjects';
import { UserPermissions } from '@shared/enums';
import { ClearState, DeleteDraft, RegistriesSelectors, UpdateDraft, UpdateStepState } from '../../store';
diff --git a/src/app/features/registries/components/registries-metadata-step/registries-subjects/registries-subjects.component.spec.ts b/src/app/features/registries/components/registries-metadata-step/registries-subjects/registries-subjects.component.spec.ts
index 4102b431a..822320ac4 100644
--- a/src/app/features/registries/components/registries-metadata-step/registries-subjects/registries-subjects.component.spec.ts
+++ b/src/app/features/registries/components/registries-metadata-step/registries-subjects/registries-subjects.component.spec.ts
@@ -8,7 +8,7 @@ import { ActivatedRoute } from '@angular/router';
import { RegistriesSelectors } from '@osf/features/registries/store';
import { ResourceType } from '@osf/shared/enums';
-import { SubjectsSelectors } from '@osf/shared/stores';
+import { SubjectsSelectors } from '@osf/shared/stores/subjects';
import { RegistriesSubjectsComponent } from './registries-subjects.component';
diff --git a/src/app/features/registries/components/registries-metadata-step/registries-subjects/registries-subjects.component.ts b/src/app/features/registries/components/registries-metadata-step/registries-subjects/registries-subjects.component.ts
index ea6db6907..e7ad85599 100644
--- a/src/app/features/registries/components/registries-metadata-step/registries-subjects/registries-subjects.component.ts
+++ b/src/app/features/registries/components/registries-metadata-step/registries-subjects/registries-subjects.component.ts
@@ -20,7 +20,7 @@ import {
FetchSubjects,
SubjectsSelectors,
UpdateResourceSubjects,
-} from '@osf/shared/stores';
+} from '@osf/shared/stores/subjects';
@Component({
selector: 'osf-registries-subjects',
diff --git a/src/app/features/registries/components/review/review.component.html b/src/app/features/registries/components/review/review.component.html
index 691afff6c..46cfb88b6 100644
--- a/src/app/features/registries/components/review/review.component.html
+++ b/src/app/features/registries/components/review/review.component.html
@@ -13,6 +13,7 @@ {{ 'common.labels.title' | translate }}
}
+
{{ 'common.labels.description' | translate }}
{{ draftRegistration()?.description }}
@@ -27,22 +28,19 @@
{{ 'common.labels.description' | translate }}
{{ '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 }}
@@ -50,6 +48,7 @@ {{ 'shared.license.title' | translate }}
}
+
{{ 'shared.subjects.title' | translate }}
@@ -57,6 +56,7 @@
{{ 'shared.subjects.title' | translate }}
}
+
@if (!subjects().length) {
{{ 'common.labels.noData' | translate }}
@@ -77,6 +77,7 @@ {{ 'shared.tags.title' | translate }}
}
+
@for (page of pages(); track page.id) {
{{ page.title }}
@@ -85,6 +86,14 @@ {{ page.title }}
{{ page.description }}
}
+ @if (page.questions?.length) {
+
+ }
+
@if (page.sections?.length) {
@for (section of page.sections; track section.id) {
@@ -101,14 +110,6 @@
{{ section.title }}
}
}
- } @else {
- @if (page.questions?.length) {
-
- }
}
}
diff --git a/src/app/features/registries/components/review/review.component.spec.ts b/src/app/features/registries/components/review/review.component.spec.ts
index 705ff9be9..2b9752afd 100644
--- a/src/app/features/registries/components/review/review.component.spec.ts
+++ b/src/app/features/registries/components/review/review.component.spec.ts
@@ -14,7 +14,8 @@ import {
} from '@osf/shared/components';
import { FieldType } from '@osf/shared/enums';
import { CustomConfirmationService, CustomDialogService, ToastService } from '@osf/shared/services';
-import { ContributorsSelectors, SubjectsSelectors } from '@osf/shared/stores';
+import { ContributorsSelectors } from '@osf/shared/stores/contributors';
+import { SubjectsSelectors } from '@osf/shared/stores/subjects';
import { ReviewComponent } from './review.component';
diff --git a/src/app/features/registries/components/review/review.component.ts b/src/app/features/registries/components/review/review.component.ts
index cc8b2e29a..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';
@@ -10,22 +9,26 @@ import { Tag } from 'primeng/tag';
import { map, of } from 'rxjs';
-import { ChangeDetectionStrategy, Component, computed, effect, inject } from '@angular/core';
+import { ChangeDetectionStrategy, Component, computed, effect, inject, OnDestroy } from '@angular/core';
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,
- FetchSelectedSubjects,
GetAllContributors,
- SubjectsSelectors,
-} from '@osf/shared/stores';
+ LoadMoreContributors,
+ ResetContributorsState,
+} from '@osf/shared/stores/contributors';
+import { FetchSelectedSubjects, SubjectsSelectors } from '@osf/shared/stores/subjects';
import {
ClearState,
@@ -46,19 +49,15 @@ 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',
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class ReviewComponent {
+export class ReviewComponent implements OnDestroy {
private readonly router = inject(Router);
private readonly route = inject(ActivatedRoute);
private readonly customConfirmationService = inject(CustomConfirmationService);
@@ -73,6 +72,8 @@ export class ReviewComponent {
readonly stepsData = select(RegistriesSelectors.getStepsData);
readonly INPUT_VALIDATION_MESSAGES = INPUT_VALIDATION_MESSAGES;
readonly contributors = select(ContributorsSelectors.getContributors);
+ readonly areContributorsLoading = select(ContributorsSelectors.isContributorsLoading);
+ readonly hasMoreContributors = select(ContributorsSelectors.hasMoreContributors);
readonly subjects = select(SubjectsSelectors.getSelectedSubjects);
readonly components = select(RegistriesSelectors.getRegistrationComponents);
readonly license = select(RegistriesSelectors.getRegistrationLicense);
@@ -88,6 +89,8 @@ export class ReviewComponent {
getProjectsComponents: FetchProjectChildren,
fetchLicenses: FetchLicenses,
updateStepState: UpdateStepState,
+ loadMoreContributors: LoadMoreContributors,
+ resetContributorsState: ResetContributorsState,
});
private readonly draftId = toSignal(this.route.params.pipe(map((params) => params['id'])) ?? of(undefined));
@@ -134,6 +137,10 @@ export class ReviewComponent {
});
}
+ ngOnDestroy(): void {
+ this.actions.resetContributorsState();
+ }
+
goBack(): void {
const previousStep = this.pages().length;
this.router.navigate(['../', previousStep], { relativeTo: this.route });
@@ -206,4 +213,8 @@ export class ReviewComponent {
}
});
}
+
+ loadMoreContributors(): void {
+ this.actions.loadMoreContributors(this.draftId(), ResourceType.DraftRegistration);
+ }
}
diff --git a/src/app/features/registries/registries.routes.ts b/src/app/features/registries/registries.routes.ts
index 9e5e74892..ecff049e7 100644
--- a/src/app/features/registries/registries.routes.ts
+++ b/src/app/features/registries/registries.routes.ts
@@ -6,8 +6,10 @@ import { registrationModerationGuard } from '@core/guards/registration-moderatio
import { authGuard } from '@osf/core/guards';
import { RegistriesComponent } from '@osf/features/registries/registries.component';
import { RegistriesState } from '@osf/features/registries/store';
-import { CitationsState, ContributorsState, SubjectsState } from '@osf/shared/stores';
+import { CitationsState } from '@osf/shared/stores/citations';
+import { ContributorsState } from '@osf/shared/stores/contributors';
import { RegistrationProviderState } from '@osf/shared/stores/registration-provider';
+import { SubjectsState } from '@osf/shared/stores/subjects';
import { LicensesHandlers, ProjectsHandlers, ProvidersHandlers } from './store/handlers';
import { FilesHandlers } from './store/handlers/files.handlers';
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 387f1f2a6..0a0151a42 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
@@ -93,7 +93,18 @@
{{ page.title }}
@if (page.description) {
-
{{ page.description }}
+
{{ page.description }}
+ }
+
+ @if (page.questions?.length) {
+
}
@if (page.sections?.length) {
@@ -101,7 +112,7 @@
{{ page.title }}
{{ section.title }}
@if (section.description) {
-
{{ section.description }}
+
{{ section.description }}
}
@if (section.questions?.length) {
{{ section.title }}
}
}
- } @else {
- @if (page.questions?.length) {
-
- }
}
}
@@ -136,6 +136,10 @@
{{ section.title }}
(customCitationUpdated)="onCustomCitationUpdated($event)"
[canEdit]="hasWriteAccess()"
[showEditButton]="hasWriteAccess()"
+ [bibliographicContributors]="bibliographicContributors()"
+ [isBibliographicContributorsLoading]="isBibliographicContributorsLoading()"
+ [hasMoreBibliographicContributors]="hasMoreBibliographicContributors()"
+ (loadMoreContributors)="handleLoadMoreContributors()"
/>
diff --git a/src/app/features/registry/pages/registry-overview/registry-overview.component.ts b/src/app/features/registry/pages/registry-overview/registry-overview.component.ts
index af1b77fb6..dddd713b2 100644
--- a/src/app/features/registry/pages/registry-overview/registry-overview.component.ts
+++ b/src/app/features/registry/pages/registry-overview/registry-overview.component.ts
@@ -35,7 +35,13 @@ import { hasViewOnlyParam, toCamelCase } from '@osf/shared/helpers';
import { MapRegistryOverview } from '@osf/shared/mappers';
import { SchemaResponse, ToolbarResource } from '@osf/shared/models';
import { CustomDialogService, ToastService } from '@osf/shared/services';
-import { FetchSelectedSubjects, GetBookmarksCollectionId, SubjectsSelectors } from '@osf/shared/stores';
+import { GetBookmarksCollectionId } from '@osf/shared/stores/bookmarks';
+import {
+ ContributorsSelectors,
+ GetBibliographicContributors,
+ LoadMoreBibliographicContributors,
+} from '@osf/shared/stores/contributors';
+import { FetchSelectedSubjects, SubjectsSelectors } from '@osf/shared/stores/subjects';
import { ArchivingMessageComponent, RegistryRevisionsComponent, RegistryStatusesComponent } from '../../components';
import { RegistryMakeDecisionComponent } from '../../components/registry-make-decision/registry-make-decision.component';
@@ -90,6 +96,9 @@ export class RegistryOverviewComponent {
readonly areReviewActionsLoading = select(RegistryOverviewSelectors.areReviewActionsLoading);
readonly currentRevision = select(RegistriesSelectors.getSchemaResponse);
readonly isSchemaResponseLoading = select(RegistriesSelectors.getSchemaResponseLoading);
+ bibliographicContributors = select(ContributorsSelectors.getBibliographicContributors);
+ isBibliographicContributorsLoading = select(ContributorsSelectors.isBibliographicContributorsLoading);
+ hasMoreBibliographicContributors = select(ContributorsSelectors.hasMoreBibliographicContributors);
readonly hasWriteAccess = select(RegistryOverviewSelectors.hasWriteAccess);
readonly hasAdminAccess = select(RegistryOverviewSelectors.hasAdminAccess);
@@ -180,6 +189,8 @@ export class RegistryOverviewComponent {
getRegistryReviewActions: GetRegistryReviewActions,
getSchemaResponse: FetchAllSchemaResponses,
createSchemaResponse: CreateSchemaResponse,
+ getBibliographicContributors: GetBibliographicContributors,
+ loadMoreBibliographicContributors: LoadMoreBibliographicContributors,
});
revisionId: string | null = null;
@@ -213,6 +224,7 @@ export class RegistryOverviewComponent {
effect(() => {
if (this.registryId()) {
this.actions.getRegistryById(this.registryId());
+ this.actions.getBibliographicContributors(this.registryId(), ResourceType.Registration);
}
});
@@ -272,6 +284,10 @@ export class RegistryOverviewComponent {
.subscribe();
}
+ handleLoadMoreContributors(): void {
+ this.actions.loadMoreBibliographicContributors(this.registry()?.id, ResourceType.Registration);
+ }
+
private navigateToJustificationPage(): void {
const revisionId = this.revisionId || this.revisionInProgress?.id;
this.router.navigate([`/registries/revisions/${revisionId}/justification`]);
diff --git a/src/app/features/registry/pages/registry-wiki/registry-wiki.component.spec.ts b/src/app/features/registry/pages/registry-wiki/registry-wiki.component.spec.ts
index 8dc9d21a5..7259a5d1f 100644
--- a/src/app/features/registry/pages/registry-wiki/registry-wiki.component.spec.ts
+++ b/src/app/features/registry/pages/registry-wiki/registry-wiki.component.spec.ts
@@ -7,7 +7,7 @@ import { ActivatedRoute, Router } from '@angular/router';
import { WikiModes } from '@osf/shared/models';
import { SubHeaderComponent, ViewOnlyLinkMessageComponent } from '@shared/components';
import { CompareSectionComponent, ViewSectionComponent, WikiListComponent } from '@shared/components/wiki';
-import { WikiSelectors } from '@shared/stores';
+import { WikiSelectors } from '@shared/stores/wiki';
import { RegistryWikiComponent } from './registry-wiki.component';
diff --git a/src/app/features/registry/pages/registry-wiki/registry-wiki.component.ts b/src/app/features/registry/pages/registry-wiki/registry-wiki.component.ts
index f9c35ff7f..f8493272a 100644
--- a/src/app/features/registry/pages/registry-wiki/registry-wiki.component.ts
+++ b/src/app/features/registry/pages/registry-wiki/registry-wiki.component.ts
@@ -27,7 +27,7 @@ import {
SetCurrentWiki,
ToggleMode,
WikiSelectors,
-} from '@osf/shared/stores';
+} from '@osf/shared/stores/wiki';
@Component({
selector: 'osf-registry-wiki',
diff --git a/src/app/features/registry/registry.routes.ts b/src/app/features/registry/registry.routes.ts
index f26006bbe..31815be3e 100644
--- a/src/app/features/registry/registry.routes.ts
+++ b/src/app/features/registry/registry.routes.ts
@@ -5,13 +5,10 @@ import { Routes } from '@angular/router';
import { viewOnlyGuard } from '@osf/core/guards';
import { ResourceType } from '@osf/shared/enums';
import { LicensesService } from '@osf/shared/services';
-import {
- CitationsState,
- ContributorsState,
- DuplicatesState,
- SubjectsState,
- ViewOnlyLinkState,
-} from '@osf/shared/stores';
+import { CitationsState } from '@osf/shared/stores/citations';
+import { DuplicatesState } from '@osf/shared/stores/duplicates';
+import { SubjectsState } from '@osf/shared/stores/subjects';
+import { ViewOnlyLinkState } from '@osf/shared/stores/view-only-links';
import { ActivityLogsState } from '@shared/stores/activity-logs';
import { AnalyticsState } from '../analytics/store';
@@ -52,7 +49,7 @@ export const registryRoutes: Routes = [
{
path: 'metadata',
loadChildren: () => import('@osf/features/metadata/metadata.routes').then((mod) => mod.metadataRoutes),
- providers: [provideStates([SubjectsState, ContributorsState])],
+ providers: [provideStates([SubjectsState])],
data: { resourceType: ResourceType.Registration },
canActivate: [viewOnlyGuard],
},
@@ -68,7 +65,7 @@ export const registryRoutes: Routes = [
canActivate: [viewOnlyGuard],
loadComponent: () => import('../contributors/contributors.component').then((mod) => mod.ContributorsComponent),
data: { resourceType: ResourceType.Registration },
- providers: [provideStates([ContributorsState, ViewOnlyLinkState])],
+ providers: [provideStates([ViewOnlyLinkState])],
},
{
path: 'analytics',
diff --git a/src/app/features/settings/account-settings/account-settings.component.spec.ts b/src/app/features/settings/account-settings/account-settings.component.spec.ts
index 6afd881fd..6a8cfd3c9 100644
--- a/src/app/features/settings/account-settings/account-settings.component.spec.ts
+++ b/src/app/features/settings/account-settings/account-settings.component.spec.ts
@@ -9,6 +9,11 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { provideNoopAnimations } from '@angular/platform-browser/animations';
import { UserSelectors } from '@osf/core/store/user';
+import { SubHeaderComponent } from '@osf/shared/components';
+import { ToastService } from '@osf/shared/services';
+import { RegionsSelectors } from '@osf/shared/stores/regions';
+
+import { AccountSettingsComponent } from './account-settings.component';
import {
AffiliatedInstitutionsComponent,
ChangePasswordComponent,
@@ -18,13 +23,8 @@ import {
DefaultStorageLocationComponent,
ShareIndexingComponent,
TwoFactorAuthComponent,
-} from '@osf/features/settings/account-settings/components';
-import { AccountSettingsSelectors } from '@osf/features/settings/account-settings/store';
-import { SubHeaderComponent } from '@osf/shared/components';
-import { RegionsSelectors } from '@osf/shared/stores';
-import { ToastService } from '@shared/services';
-
-import { AccountSettingsComponent } from './account-settings.component';
+} from './components';
+import { AccountSettingsSelectors } from './store';
import { MOCK_STORE, MOCK_USER, MockCustomConfirmationServiceProvider, TranslateServiceMock } from '@testing/mocks';
diff --git a/src/app/features/settings/account-settings/account-settings.component.ts b/src/app/features/settings/account-settings/account-settings.component.ts
index 2a0187d38..36882cb61 100644
--- a/src/app/features/settings/account-settings/account-settings.component.ts
+++ b/src/app/features/settings/account-settings/account-settings.component.ts
@@ -8,7 +8,7 @@ import { ReactiveFormsModule } from '@angular/forms';
import { GetEmails } from '@core/store/user-emails';
import { UserSelectors } from '@osf/core/store/user';
import { SubHeaderComponent } from '@osf/shared/components';
-import { FetchRegions } from '@osf/shared/stores';
+import { FetchRegions } from '@osf/shared/stores/regions';
import {
AffiliatedInstitutionsComponent,
diff --git a/src/app/features/settings/account-settings/components/default-storage-location/default-storage-location.component.spec.ts b/src/app/features/settings/account-settings/components/default-storage-location/default-storage-location.component.spec.ts
index a5a9e723a..072034d2b 100644
--- a/src/app/features/settings/account-settings/components/default-storage-location/default-storage-location.component.spec.ts
+++ b/src/app/features/settings/account-settings/components/default-storage-location/default-storage-location.component.spec.ts
@@ -9,7 +9,7 @@ import { signal } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { UserSelectors, UserState } from '@osf/core/store/user';
-import { RegionsSelectors, RegionsState } from '@osf/shared/stores';
+import { RegionsSelectors, RegionsState } from '@osf/shared/stores/regions';
import { LoaderService, ToastService } from '@shared/services';
import { AccountSettingsState } from '../../store';
diff --git a/src/app/shared/components/add-project-form/add-project-form.component.spec.ts b/src/app/shared/components/add-project-form/add-project-form.component.spec.ts
index 9dfc2e78e..a066932db 100644
--- a/src/app/shared/components/add-project-form/add-project-form.component.spec.ts
+++ b/src/app/shared/components/add-project-form/add-project-form.component.spec.ts
@@ -11,7 +11,9 @@ import { ProjectFormControls } from '@osf/shared/enums';
import { CustomValidators } from '@osf/shared/helpers';
import { ProjectForm } from '@osf/shared/models';
import { ProjectModel } from '@osf/shared/models/projects';
-import { InstitutionsSelectors, ProjectsSelectors, RegionsSelectors } from '@osf/shared/stores';
+import { InstitutionsSelectors } from '@osf/shared/stores/institutions';
+import { ProjectsSelectors } from '@osf/shared/stores/projects';
+import { RegionsSelectors } from '@osf/shared/stores/regions';
import { AffiliatedInstitutionSelectComponent, ProjectSelectorComponent } from '@shared/components';
import { AddProjectFormComponent } from './add-project-form.component';
diff --git a/src/app/shared/components/add-project-form/add-project-form.component.ts b/src/app/shared/components/add-project-form/add-project-form.component.ts
index c7c40f02b..876e92273 100644
--- a/src/app/shared/components/add-project-form/add-project-form.component.ts
+++ b/src/app/shared/components/add-project-form/add-project-form.component.ts
@@ -16,7 +16,8 @@ import { FormGroup, ReactiveFormsModule } from '@angular/forms';
import { UserSelectors } from '@core/store/user';
import { ProjectFormControls } from '@osf/shared/enums';
import { Institution, ProjectForm, ProjectModel } from '@osf/shared/models';
-import { FetchRegions, FetchUserInstitutions, InstitutionsSelectors, RegionsSelectors } from '@osf/shared/stores';
+import { FetchUserInstitutions, InstitutionsSelectors } from '@osf/shared/stores/institutions';
+import { FetchRegions, RegionsSelectors } from '@osf/shared/stores/regions';
import { AffiliatedInstitutionSelectComponent } from '../affiliated-institution-select/affiliated-institution-select.component';
import { ProjectSelectorComponent } from '../project-selector/project-selector.component';
diff --git a/src/app/shared/components/addons/storage-item-selector/storage-item-selector.component.spec.ts b/src/app/shared/components/addons/storage-item-selector/storage-item-selector.component.spec.ts
index 170ab3e57..18b50ae71 100644
--- a/src/app/shared/components/addons/storage-item-selector/storage-item-selector.component.spec.ts
+++ b/src/app/shared/components/addons/storage-item-selector/storage-item-selector.component.spec.ts
@@ -7,7 +7,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { GoogleFilePickerComponent, SelectComponent } from '@shared/components';
import { StorageItemSelectorComponent } from '@shared/components/addons';
import { OperationNames } from '@shared/enums';
-import { AddonsSelectors } from '@shared/stores';
+import { AddonsSelectors } from '@shared/stores/addons';
import { OSFTestingModule } from '@testing/osf.testing.module';
import { DialogServiceMockBuilder } from '@testing/providers/dialog-provider.mock';
diff --git a/src/app/shared/components/addons/storage-item-selector/storage-item-selector.component.ts b/src/app/shared/components/addons/storage-item-selector/storage-item-selector.component.ts
index 6d36a4dfb..c965e907a 100644
--- a/src/app/shared/components/addons/storage-item-selector/storage-item-selector.component.ts
+++ b/src/app/shared/components/addons/storage-item-selector/storage-item-selector.component.ts
@@ -30,7 +30,7 @@ import { AddonType, OperationNames, StorageItemType } from '@osf/shared/enums';
import { convertCamelCaseToNormal, IS_XSMALL } from '@osf/shared/helpers';
import { OperationInvokeData, StorageItem } from '@osf/shared/models';
import { CustomDialogService } from '@osf/shared/services';
-import { AddonsSelectors, ClearOperationInvocations } from '@osf/shared/stores';
+import { AddonsSelectors, ClearOperationInvocations } from '@osf/shared/stores/addons';
import { GoogleFilePickerComponent } from '../../google-file-picker/google-file-picker.component';
import { SelectComponent } from '../../select/select.component';
diff --git a/src/app/shared/components/contributors-list/contributors-list.component.html b/src/app/shared/components/contributors-list/contributors-list.component.html
index 7c379d360..814ad1dac 100644
--- a/src/app/shared/components/contributors-list/contributors-list.component.html
+++ b/src/app/shared/components/contributors-list/contributors-list.component.html
@@ -1,17 +1,34 @@
-
+
@if (anonymous()) {
{{ 'project.overview.metadata.anonymousContributors' | translate }}
} @else {
- @for (contributor of contributors(); track contributor.id) {
-
- @if (readonly() || contributor.isUnregisteredContributor || !contributor.id || contributor.deactivated) {
-
{{ contributor.fullName }}{{ $last ? '' : ',' }}
- } @else {
-
- {{ contributor.fullName }}{{ $last ? '' : ',' }}
-
- }
+ @if (isLoading()) {
+
+ } @else {
+ @for (contributor of contributors(); track contributor.id) {
+
+ }
}
}
+
+@if (hasLoadMore()) {
+
+}
diff --git a/src/app/shared/components/contributors-list/contributors-list.component.scss b/src/app/shared/components/contributors-list/contributors-list.component.scss
index e69de29bb..b9bc65ea4 100644
--- a/src/app/shared/components/contributors-list/contributors-list.component.scss
+++ b/src/app/shared/components/contributors-list/contributors-list.component.scss
@@ -0,0 +1,3 @@
+:host {
+ width: 100%;
+}
diff --git a/src/app/shared/components/contributors-list/contributors-list.component.ts b/src/app/shared/components/contributors-list/contributors-list.component.ts
index 89cc6ab67..b84335abc 100644
--- a/src/app/shared/components/contributors-list/contributors-list.component.ts
+++ b/src/app/shared/components/contributors-list/contributors-list.component.ts
@@ -1,19 +1,26 @@
import { TranslatePipe } from '@ngx-translate/core';
-import { ChangeDetectionStrategy, Component, input } from '@angular/core';
+import { Button } from 'primeng/button';
+import { Skeleton } from 'primeng/skeleton';
+
+import { ChangeDetectionStrategy, Component, input, output } from '@angular/core';
import { RouterLink } from '@angular/router';
import { ContributorModel } from '@shared/models';
@Component({
selector: 'osf-contributors-list',
- imports: [RouterLink, TranslatePipe],
+ imports: [RouterLink, TranslatePipe, Skeleton, Button],
templateUrl: './contributors-list.component.html',
styleUrl: './contributors-list.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ContributorsListComponent {
contributors = input.required
[]>();
+ isLoading = input(false);
+ hasLoadMore = input(false);
readonly = input(false);
anonymous = input(false);
+
+ loadMoreContributors = output();
}
diff --git a/src/app/shared/components/contributors/add-contributor-dialog/add-contributor-dialog.component.html b/src/app/shared/components/contributors/add-contributor-dialog/add-contributor-dialog.component.html
index 933bf2479..b235be0d0 100644
--- a/src/app/shared/components/contributors/add-contributor-dialog/add-contributor-dialog.component.html
+++ b/src/app/shared/components/contributors/add-contributor-dialog/add-contributor-dialog.component.html
@@ -11,7 +11,12 @@
} @else {
@for (item of users(); track $index) {
}
@@ -65,6 +70,22 @@
}
+ @if (allowAddingContributorsFromParentProject() && this.isSearchState()) {
+
+ }
+
([]);
readonly components = signal([]);
readonly resourceName = signal('');
+ readonly parentResourceName = signal('');
+ readonly allowAddingContributorsFromParentProject = signal(false);
readonly contributorNames = computed(() =>
this.selectedUsers()
@@ -82,6 +85,10 @@ export class AddContributorDialogComponent implements OnInit, OnDestroy {
readonly hasComponents = computed(() => this.components().length > 0);
readonly buttonLabel = computed(() => (this.isComponentsState() ? 'common.buttons.done' : 'common.buttons.next'));
+ constructor() {
+ this.setupEffects();
+ }
+
ngOnInit(): void {
this.initializeDialogData();
this.setSearchSubscription();
@@ -113,6 +120,10 @@ export class AddContributorDialogComponent implements OnInit, OnDestroy {
}
}
+ addSourceProjectContributors(): void {
+ this.closeDialogWithData(AddContributorType.ParentProject);
+ }
+
addUnregistered(): void {
this.dialogRef.close({
data: [],
@@ -129,7 +140,8 @@ export class AddContributorDialogComponent implements OnInit, OnDestroy {
private initializeDialogData(): void {
this.selectedUsers.set([]);
- const { components, resourceName } = this.config.data || {};
+ const { components, resourceName, parentResourceName, allowAddingContributorsFromParentProject } =
+ this.config.data || {};
if (components) {
this.components.set(components);
@@ -138,16 +150,26 @@ export class AddContributorDialogComponent implements OnInit, OnDestroy {
if (resourceName) {
this.resourceName.set(resourceName);
}
+
+ if (allowAddingContributorsFromParentProject) {
+ this.allowAddingContributorsFromParentProject.set(allowAddingContributorsFromParentProject);
+ }
+
+ if (parentResourceName) {
+ this.parentResourceName.set(parentResourceName);
+ }
}
- private closeDialogWithData(): void {
+ private closeDialogWithData(AddContributorTypeValue = AddContributorType.Registered): void {
const childNodeIds = this.components()
.filter((c) => c.checked && !c.isCurrent)
.map((c) => c.id);
+ const filteredUsers = this.selectedUsers().filter((user) => !user.disabled);
+
this.dialogRef.close({
- data: this.selectedUsers(),
- type: AddContributorType.Registered,
+ data: filteredUsers,
+ type: AddContributorTypeValue,
childNodeIds: childNodeIds.length > 0 ? childNodeIds : undefined,
} as ContributorDialogAddModel);
}
@@ -174,4 +196,18 @@ export class AddContributorDialogComponent implements OnInit, OnDestroy {
this.currentPage.set(1);
this.first.set(0);
}
+
+ private setupEffects(): void {
+ effect(() => {
+ const usersList = this.users();
+
+ if (usersList.length > 0) {
+ const checkedUsers = usersList.filter((user) => user.checked);
+
+ if (checkedUsers.length > 0) {
+ this.selectedUsers.set(checkedUsers);
+ }
+ }
+ });
+ }
}
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/components/resource-metadata/resource-metadata.component.ts b/src/app/shared/components/resource-metadata/resource-metadata.component.ts
index f1ef25b3a..b83423090 100644
--- a/src/app/shared/components/resource-metadata/resource-metadata.component.ts
+++ b/src/app/shared/components/resource-metadata/resource-metadata.component.ts
@@ -10,7 +10,7 @@ import { Router, RouterLink } from '@angular/router';
import { ENVIRONMENT } from '@core/provider/environment.provider';
import { OverviewCollectionsComponent } from '@osf/features/project/overview/components/overview-collections/overview-collections.component';
import { CurrentResourceType } from '@osf/shared/enums';
-import { ResourceOverview } from '@shared/models';
+import { ContributorModel, ResourceOverview } from '@shared/models';
import { AffiliatedInstitutionsViewComponent } from '../affiliated-institutions-view/affiliated-institutions-view.component';
import { ContributorsListComponent } from '../contributors-list/contributors-list.component';
@@ -44,6 +44,10 @@ export class ResourceMetadataComponent {
isCollectionsRoute = input
(false);
canEdit = input.required();
showEditButton = input();
+ bibliographicContributors = input([]);
+ isBibliographicContributorsLoading = input(false);
+ hasMoreBibliographicContributors = input(false);
+ loadMoreContributors = output();
readonly resourceTypes = CurrentResourceType;
readonly dateFormat = 'MMM d, y, h:mm a';
diff --git a/src/app/shared/components/subjects/subjects.component.spec.ts b/src/app/shared/components/subjects/subjects.component.spec.ts
index 257ca3d16..54b62ea68 100644
--- a/src/app/shared/components/subjects/subjects.component.spec.ts
+++ b/src/app/shared/components/subjects/subjects.component.spec.ts
@@ -1,6 +1,6 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { SubjectsSelectors } from '@osf/shared/stores';
+import { SubjectsSelectors } from '@osf/shared/stores/subjects';
import { SubjectsComponent } from './subjects.component';
diff --git a/src/app/shared/components/subjects/subjects.component.ts b/src/app/shared/components/subjects/subjects.component.ts
index 2b986b7dc..63f86297b 100644
--- a/src/app/shared/components/subjects/subjects.component.ts
+++ b/src/app/shared/components/subjects/subjects.component.ts
@@ -15,7 +15,7 @@ import { ChangeDetectionStrategy, Component, computed, input, output } from '@an
import { FormControl, FormsModule } from '@angular/forms';
import { SubjectModel } from '@osf/shared/models';
-import { SubjectsSelectors } from '@osf/shared/stores';
+import { SubjectsSelectors } from '@osf/shared/stores/subjects';
import { SearchInputComponent } from '../search-input/search-input.component';
diff --git a/src/app/shared/components/truncated-text/truncated-text.component.scss b/src/app/shared/components/truncated-text/truncated-text.component.scss
index bb473e7e9..8c983db9c 100644
--- a/src/app/shared/components/truncated-text/truncated-text.component.scss
+++ b/src/app/shared/components/truncated-text/truncated-text.component.scss
@@ -4,7 +4,6 @@
text-overflow: ellipsis;
display: -webkit-box;
line-clamp: var(--line-clamp);
- line-height: 1.7;
-webkit-line-clamp: var(--line-clamp);
-webkit-box-orient: vertical;
white-space: pre-line;
diff --git a/src/app/shared/components/wiki/add-wiki-dialog/add-wiki-dialog.component.spec.ts b/src/app/shared/components/wiki/add-wiki-dialog/add-wiki-dialog.component.spec.ts
index c1b8b9399..06ea0bf4d 100644
--- a/src/app/shared/components/wiki/add-wiki-dialog/add-wiki-dialog.component.spec.ts
+++ b/src/app/shared/components/wiki/add-wiki-dialog/add-wiki-dialog.component.spec.ts
@@ -7,7 +7,7 @@ import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';
-import { WikiSelectors } from '@osf/shared/stores';
+import { WikiSelectors } from '@osf/shared/stores/wiki';
import { ToastService } from '@shared/services';
import { AddWikiDialogComponent } from './add-wiki-dialog.component';
diff --git a/src/app/shared/components/wiki/add-wiki-dialog/add-wiki-dialog.component.ts b/src/app/shared/components/wiki/add-wiki-dialog/add-wiki-dialog.component.ts
index 40e062cbe..4e80244de 100644
--- a/src/app/shared/components/wiki/add-wiki-dialog/add-wiki-dialog.component.ts
+++ b/src/app/shared/components/wiki/add-wiki-dialog/add-wiki-dialog.component.ts
@@ -12,7 +12,7 @@ import { InputLimits } from '@osf/shared/constants';
import { ResourceType } from '@osf/shared/enums';
import { CustomValidators } from '@osf/shared/helpers';
import { ToastService } from '@osf/shared/services';
-import { CreateWiki, WikiSelectors } from '@osf/shared/stores';
+import { CreateWiki, WikiSelectors } from '@osf/shared/stores/wiki';
import { TextInputComponent } from '../../text-input/text-input.component';
diff --git a/src/app/shared/constants/social-links.const.ts b/src/app/shared/constants/social-links.const.ts
index a64cec645..56e3352f6 100644
--- a/src/app/shared/constants/social-links.const.ts
+++ b/src/app/shared/constants/social-links.const.ts
@@ -79,7 +79,7 @@ export const SOCIAL_LINKS: SocialLinksModel[] = [
address: '',
placeholder: 'https://yourwebsite.com',
key: 'profileWebsites',
- icon: '',
+ icon: 'globe.svg',
},
{
id: 11,
diff --git a/src/app/shared/enums/contributors/add-contributor-type.enum.ts b/src/app/shared/enums/contributors/add-contributor-type.enum.ts
index 17ce058fc..cd0e360e4 100644
--- a/src/app/shared/enums/contributors/add-contributor-type.enum.ts
+++ b/src/app/shared/enums/contributors/add-contributor-type.enum.ts
@@ -1,4 +1,5 @@
export enum AddContributorType {
Registered = 1,
Unregistered,
+ ParentProject,
}
diff --git a/src/app/shared/mappers/collections/collections.mapper.ts b/src/app/shared/mappers/collections/collections.mapper.ts
index 7a741e2d0..22969da39 100644
--- a/src/app/shared/mappers/collections/collections.mapper.ts
+++ b/src/app/shared/mappers/collections/collections.mapper.ts
@@ -37,12 +37,14 @@ export class CollectionsMapper {
facebookAppId: response.attributes.facebook_app_id,
allowSubmissions: response.attributes.allow_submissions,
allowCommenting: response.attributes.allow_commenting,
- assets: {
- style: response.attributes.assets.style,
- squareColorTransparent: response.attributes.assets.square_color_transparent,
- squareColorNoTransparent: response.attributes.assets.square_color_no_transparent,
- favicon: response.attributes.assets.favicon,
- },
+ assets: response.attributes.assets
+ ? {
+ style: response.attributes.assets.style,
+ squareColorTransparent: response.attributes.assets.square_color_transparent,
+ squareColorNoTransparent: response.attributes.assets.square_color_no_transparent,
+ favicon: response.attributes.assets.favicon,
+ }
+ : {},
shareSource: response.attributes.share_source,
sharePublishType: response.attributes.share_publish_type,
permissions: response.attributes.permissions,
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/app/shared/mappers/nodes/base-node.mapper.ts b/src/app/shared/mappers/nodes/base-node.mapper.ts
index 75f0a79b7..771005e2b 100644
--- a/src/app/shared/mappers/nodes/base-node.mapper.ts
+++ b/src/app/shared/mappers/nodes/base-node.mapper.ts
@@ -60,6 +60,7 @@ export class BaseNodeMapper {
wikiEnabled: data.attributes.wiki_enabled,
customCitation: data.attributes.custom_citation || undefined,
rootParentId: data.relationships.root?.data?.id,
+ parent: data.embeds?.parent?.data ? this.getNodeData(data.embeds?.parent.data) : undefined,
};
}
diff --git a/src/app/shared/mappers/registration/page-schema.mapper.ts b/src/app/shared/mappers/registration/page-schema.mapper.ts
index 01fc419f3..e5207c49a 100644
--- a/src/app/shared/mappers/registration/page-schema.mapper.ts
+++ b/src/app/shared/mappers/registration/page-schema.mapper.ts
@@ -41,12 +41,18 @@ export class PageSchemaMapper {
case BlockType.Paragraph:
if (currentQuestion) {
- currentQuestion.paragraphText = item.attributes.display_text;
+ currentQuestion.paragraphText = currentQuestion.paragraphText
+ ? currentQuestion.paragraphText + '\n \n' + item.attributes.display_text
+ : item.attributes.display_text;
currentQuestion.fieldType = FieldType.Paragraph;
} else if (currentSection) {
- currentSection.description = item.attributes.display_text;
+ currentSection.description = currentSection.description
+ ? currentSection.description + '\n \n' + item.attributes.display_text
+ : item.attributes.display_text;
} else {
- currentPage.description = item.attributes.display_text;
+ currentPage.description = currentPage.description
+ ? currentPage.description + '\n \n' + item.attributes.display_text
+ : item.attributes.display_text;
}
break;
diff --git a/src/app/shared/mappers/resource-overview.mappers.ts b/src/app/shared/mappers/resource-overview.mappers.ts
index 419e515fa..cb4a69622 100644
--- a/src/app/shared/mappers/resource-overview.mappers.ts
+++ b/src/app/shared/mappers/resource-overview.mappers.ts
@@ -1,12 +1,13 @@
import { ProjectOverview } from '@osf/features/project/overview/models';
import { RegistryOverview } from '@osf/features/registry/models';
-import { Institution, ResourceOverview, SubjectModel } from '../models';
+import { ContributorModel, Institution, ResourceOverview, SubjectModel } from '../models';
export function MapProjectOverview(
project: ProjectOverview,
subjects: SubjectModel[],
- isAnonymous = false
+ isAnonymous = false,
+ bibliographicContributors: ContributorModel[] = []
): ResourceOverview {
return {
id: project.id,
@@ -35,7 +36,7 @@ export function MapProjectOverview(
currentUserIsContributorOrGroupMember: project.currentUserIsContributorOrGroupMember,
wikiEnabled: project.wikiEnabled,
subjects: subjects,
- contributors: project.contributors?.filter(Boolean) || [],
+ contributors: bibliographicContributors?.filter(Boolean) || [],
customCitation: project.customCitation || null,
region: project.region || undefined,
affiliatedInstitutions: project.affiliatedInstitutions?.filter(Boolean) || undefined,
diff --git a/src/app/shared/mappers/view-only-links.mapper.ts b/src/app/shared/mappers/view-only-links.mapper.ts
index a168611b2..3d56a1c4d 100644
--- a/src/app/shared/mappers/view-only-links.mapper.ts
+++ b/src/app/shared/mappers/view-only-links.mapper.ts
@@ -24,7 +24,7 @@ export class ViewOnlyLinksMapper {
id: creator?.id || '',
fullName: creator?.fullName || '',
},
- nodes: item.embeds.nodes.data.map(
+ nodes: item.embeds?.nodes?.data?.map(
(node) =>
({
id: node.id,
@@ -59,7 +59,7 @@ export class ViewOnlyLinksMapper {
id: creator?.id || '',
fullName: creator?.fullName || '',
},
- nodes: item.embeds.nodes.data.map(
+ nodes: item.embeds?.nodes?.data?.map(
(node) =>
({
id: node.id,
diff --git a/src/app/shared/models/contributors/contributor-add.model.ts b/src/app/shared/models/contributors/contributor-add.model.ts
index 31bc3bf35..9e6a6f6c6 100644
--- a/src/app/shared/models/contributors/contributor-add.model.ts
+++ b/src/app/shared/models/contributors/contributor-add.model.ts
@@ -5,4 +5,6 @@ export interface ContributorAddModel {
fullName?: string;
email?: string;
index?: number;
+ checked?: boolean;
+ disabled?: boolean;
}
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/app/shared/models/nodes/base-node-embeds-json-api.model.ts b/src/app/shared/models/nodes/base-node-embeds-json-api.model.ts
index 5f7ace796..ae0c580be 100644
--- a/src/app/shared/models/nodes/base-node-embeds-json-api.model.ts
+++ b/src/app/shared/models/nodes/base-node-embeds-json-api.model.ts
@@ -1,4 +1,5 @@
import {
+ BaseNodeDataJsonApi,
ContributorDataJsonApi,
IdentifierAttributes,
IdentifiersJsonApiData,
@@ -23,6 +24,9 @@ export interface BaseNodeEmbedsJsonApi {
region?: {
data: RegionDataJsonApi;
};
+ parent?: {
+ data: BaseNodeDataJsonApi;
+ };
}
export interface JsonApiResource {
diff --git a/src/app/shared/models/nodes/base-node.model.ts b/src/app/shared/models/nodes/base-node.model.ts
index 4187db43e..0ef9ccb0d 100644
--- a/src/app/shared/models/nodes/base-node.model.ts
+++ b/src/app/shared/models/nodes/base-node.model.ts
@@ -23,6 +23,7 @@ export interface BaseNodeModel {
wikiEnabled: boolean;
rootParentId?: string;
type: string;
+ parent?: BaseNodeModel;
}
export interface NodeModel extends BaseNodeModel {
diff --git a/src/app/shared/services/contributors.service.ts b/src/app/shared/services/contributors.service.ts
index 16d525413..7735c7f85 100644
--- a/src/app/shared/services/contributors.service.ts
+++ b/src/app/shared/services/contributors.service.ts
@@ -164,6 +164,12 @@ export class ContributorsService {
.pipe(map((contributor) => ContributorsMapper.getContributor(contributor.data)));
}
+ addContributorsFromProject(resourceType: ResourceType, resourceId: string): Observable {
+ const baseUrl = `${this.getBaseUrl(resourceType, resourceId)}/?copy_contributors_from_parent_project=true`;
+ const contributorData = { data: { type: AddContributorType.ParentProject } };
+ return this.jsonApiService.patch(baseUrl, contributorData);
+ }
+
deleteContributor(resourceType: ResourceType, resourceId: string, userId: string): Observable {
const baseUrl = `${this.getBaseUrl(resourceType, resourceId)}/${userId}/`;
diff --git a/src/app/shared/services/resource.service.ts b/src/app/shared/services/resource.service.ts
index fd227e040..839f2194f 100644
--- a/src/app/shared/services/resource.service.ts
+++ b/src/app/shared/services/resource.service.ts
@@ -66,9 +66,11 @@ export class ResourceGuidService {
getResourceDetails(resourceId: string, resourceType: ResourceType): Observable {
const resourcePath = this.urlMap.get(resourceType);
-
+ const params: Record = {
+ embed: 'parent',
+ };
return this.jsonApiService
- .get>(`${this.apiUrl}/${resourcePath}/${resourceId}/`)
+ .get>(`${this.apiUrl}/${resourcePath}/${resourceId}/`, params)
.pipe(map((response) => BaseNodeMapper.getNodeData(response.data)));
}
diff --git a/src/app/shared/stores/contributors/contributors.actions.ts b/src/app/shared/stores/contributors/contributors.actions.ts
index 4eaaead98..e2654c6c9 100644
--- a/src/app/shared/stores/contributors/contributors.actions.ts
+++ b/src/app/shared/stores/contributors/contributors.actions.ts
@@ -62,6 +62,15 @@ export class BulkAddContributors {
) {}
}
+export class BulkAddContributorsFromParentProject {
+ static readonly type = '[Contributors] Bulk Add Contributors From Parent Project';
+
+ constructor(
+ public resourceId: string | undefined | null,
+ public resourceType: ResourceType | undefined
+ ) {}
+}
+
export class DeleteContributor {
static readonly type = '[Contributors] Delete Contributor';
@@ -119,3 +128,32 @@ export class RejectRequestAccess {
public resourceType: ResourceType | undefined
) {}
}
+
+export class GetBibliographicContributors {
+ static readonly type = '[Contributors] Get Bibliographic Contributors';
+
+ constructor(
+ public resourceId: string | undefined | null,
+ public resourceType: ResourceType | undefined,
+ public page = 1,
+ public pageSize = DEFAULT_TABLE_PARAMS.rows
+ ) {}
+}
+
+export class LoadMoreBibliographicContributors {
+ static readonly type = '[Contributors] Load More Bibliographic Contributors';
+
+ constructor(
+ public resourceId: string | undefined | null,
+ public resourceType: ResourceType | undefined
+ ) {}
+}
+
+export class LoadMoreContributors {
+ static readonly type = '[Contributors] Load More Contributors';
+
+ constructor(
+ public resourceId: string | undefined | null,
+ public resourceType: ResourceType | undefined
+ ) {}
+}
diff --git a/src/app/shared/stores/contributors/contributors.model.ts b/src/app/shared/stores/contributors/contributors.model.ts
index 5f7c92b61..89d34a7fd 100644
--- a/src/app/shared/stores/contributors/contributors.model.ts
+++ b/src/app/shared/stores/contributors/contributors.model.ts
@@ -2,16 +2,21 @@ import { DEFAULT_TABLE_PARAMS } from '@osf/shared/constants';
import { ContributorAddModel, ContributorModel, RequestAccessModel } from '@osf/shared/models';
import { AsyncStateModel, AsyncStateWithTotalCount } from '@osf/shared/models/store';
-export interface ContributorsListModel extends AsyncStateWithTotalCount {
+export interface ContributorsList extends AsyncStateWithTotalCount {
+ page: number;
+ pageSize: number;
+}
+
+export interface ContributorsListWithFiltersModel extends ContributorsList {
searchValue: string | null;
permissionFilter: string | null;
bibliographyFilter: boolean | null;
- page: number;
- pageSize: number;
+ isLoadingMore: boolean;
}
export interface ContributorsStateModel {
- contributorsList: ContributorsListModel;
+ contributorsList: ContributorsListWithFiltersModel;
+ bibliographicContributorsList: ContributorsList;
requestAccessList: AsyncStateModel;
users: AsyncStateWithTotalCount;
}
@@ -27,6 +32,16 @@ export const CONTRIBUTORS_STATE_DEFAULTS: ContributorsStateModel = {
totalCount: 0,
page: 1,
pageSize: DEFAULT_TABLE_PARAMS.rows,
+ isLoadingMore: false,
+ },
+ bibliographicContributorsList: {
+ data: [],
+ isLoading: false,
+ isSubmitting: false,
+ error: null,
+ totalCount: 0,
+ page: 0,
+ pageSize: DEFAULT_TABLE_PARAMS.rows,
},
requestAccessList: {
data: [],
diff --git a/src/app/shared/stores/contributors/contributors.selectors.ts b/src/app/shared/stores/contributors/contributors.selectors.ts
index f3816a2de..57f026482 100644
--- a/src/app/shared/stores/contributors/contributors.selectors.ts
+++ b/src/app/shared/stores/contributors/contributors.selectors.ts
@@ -30,11 +30,29 @@ export class ContributorsSelectors {
@Selector([ContributorsState])
static getBibliographicContributors(state: ContributorsStateModel) {
- if (!state?.contributorsList?.data) {
+ if (!state?.bibliographicContributorsList?.data) {
return [];
}
- return state.contributorsList.data.filter((contributor) => contributor.isBibliographic);
+ return state.bibliographicContributorsList.data;
+ }
+
+ @Selector([ContributorsState])
+ static isBibliographicContributorsLoading(state: ContributorsStateModel) {
+ return state?.bibliographicContributorsList?.isLoading || false;
+ }
+
+ @Selector([ContributorsState])
+ static getBibliographicContributorsTotalCount(state: ContributorsStateModel) {
+ return state?.bibliographicContributorsList?.totalCount || 0;
+ }
+
+ @Selector([ContributorsState])
+ static hasMoreBibliographicContributors(state: ContributorsStateModel) {
+ return (
+ state?.bibliographicContributorsList?.data?.length < state?.bibliographicContributorsList?.totalCount &&
+ !state?.bibliographicContributorsList?.isLoading
+ );
}
@Selector([ContributorsState])
@@ -43,8 +61,8 @@ export class ContributorsSelectors {
}
@Selector([ContributorsState])
- static getContributorsPageNumber(state: ContributorsStateModel) {
- return state.contributorsList.page;
+ static isContributorsLoadingMore(state: ContributorsStateModel) {
+ return state?.contributorsList?.isLoadingMore || false;
}
@Selector([ContributorsState])
@@ -57,6 +75,13 @@ export class ContributorsSelectors {
return state.contributorsList.totalCount;
}
+ @Selector([ContributorsState])
+ static hasMoreContributors(state: ContributorsStateModel) {
+ return (
+ state?.contributorsList?.data?.length < state?.contributorsList?.totalCount && !state?.contributorsList?.isLoading
+ );
+ }
+
@Selector([ContributorsState])
static getUsers(state: ContributorsStateModel) {
return state?.users?.data || [];
diff --git a/src/app/shared/stores/contributors/contributors.state.ts b/src/app/shared/stores/contributors/contributors.state.ts
index be0259ff5..cd894866a 100644
--- a/src/app/shared/stores/contributors/contributors.state.ts
+++ b/src/app/shared/stores/contributors/contributors.state.ts
@@ -11,11 +11,15 @@ import {
AcceptRequestAccess,
AddContributor,
BulkAddContributors,
+ BulkAddContributorsFromParentProject,
BulkUpdateContributors,
ClearUsers,
DeleteContributor,
GetAllContributors,
+ GetBibliographicContributors,
GetRequestAccessContributors,
+ LoadMoreBibliographicContributors,
+ LoadMoreContributors,
RejectRequestAccess,
ResetContributorsState,
SearchUsers,
@@ -48,19 +52,23 @@ export class ContributorsState {
ctx.patchState({
contributorsList: {
...state.contributorsList,
- data: [],
- isLoading: true,
+ data: page === 1 ? [] : state.contributorsList.data,
+ isLoading: page === 1,
+ isLoadingMore: page > 1,
error: null,
},
});
return this.contributorsService.getAllContributors(action.resourceType, action.resourceId, page, pageSize).pipe(
tap((res) => {
+ const data = page === 1 ? res.data : [...state.contributorsList.data, ...res.data];
+
ctx.patchState({
contributorsList: {
...state.contributorsList,
- data: res.data,
+ data,
isLoading: false,
+ isLoadingMore: false,
totalCount: res.totalCount,
page,
pageSize,
@@ -206,6 +214,29 @@ export class ContributorsState {
);
}
+ @Action(BulkAddContributorsFromParentProject)
+ bulkAddContributorsFromParentProject(
+ ctx: StateContext,
+ action: BulkAddContributorsFromParentProject
+ ) {
+ const state = ctx.getState();
+
+ if (!action.resourceId || !action.resourceType) {
+ return;
+ }
+
+ ctx.patchState({
+ contributorsList: { ...state.contributorsList, isLoading: true, error: null },
+ });
+
+ return this.contributorsService.addContributorsFromProject(action.resourceType, action.resourceId).pipe(
+ tap(() => {
+ ctx.dispatch(new GetAllContributors(action.resourceId, action.resourceType));
+ }),
+ catchError((error) => handleSectionError(ctx, 'contributorsList', error))
+ );
+ }
+
@Action(DeleteContributor)
deleteContributor(ctx: StateContext, action: DeleteContributor) {
const state = ctx.getState();
@@ -259,17 +290,21 @@ export class ContributorsState {
users: { ...state.users, isLoading: true, error: null },
});
- const addedContributorsIds = state.contributorsList.data.map((contributor) => contributor.userId);
-
if (!action.searchValue) {
return of([]);
}
return this.contributorsService.searchUsers(action.searchValue, action.page).pipe(
tap((users) => {
+ const addedContributorsIds = state.contributorsList.data.map((contributor) => contributor.userId);
+
ctx.patchState({
users: {
- data: users.data.filter((user) => !addedContributorsIds.includes(user.id!)),
+ data: users.data.map((user) => ({
+ ...user,
+ checked: addedContributorsIds.includes(user.id!),
+ disabled: addedContributorsIds.includes(user.id!),
+ })),
isLoading: false,
error: '',
totalCount: users.totalCount,
@@ -285,6 +320,67 @@ export class ContributorsState {
ctx.patchState({ users: { data: [], isLoading: false, error: null, totalCount: 0 } });
}
+ @Action(GetBibliographicContributors)
+ getBibliographicContributors(ctx: StateContext, action: GetBibliographicContributors) {
+ const state = ctx.getState();
+
+ if (!action.resourceId || !action.resourceType) {
+ return;
+ }
+
+ ctx.patchState({
+ bibliographicContributorsList: {
+ ...state.bibliographicContributorsList,
+ data: action.page === 1 ? [] : state.bibliographicContributorsList.data,
+ isLoading: true,
+ error: null,
+ },
+ });
+
+ return this.contributorsService
+ .getBibliographicContributors(action.resourceType, action.resourceId, action.page, action.pageSize)
+ .pipe(
+ tap((res) => {
+ const data = action.page === 1 ? res.data : [...state.bibliographicContributorsList.data, ...res.data];
+
+ ctx.patchState({
+ bibliographicContributorsList: {
+ data,
+ isLoading: false,
+ error: null,
+ page: action.page,
+ pageSize: res.pageSize,
+ totalCount: res.totalCount,
+ },
+ });
+ }),
+ catchError((error) => handleSectionError(ctx, 'bibliographicContributorsList', error))
+ );
+ }
+
+ @Action(LoadMoreBibliographicContributors)
+ loadMoreBibliographicContributors(
+ ctx: StateContext,
+ action: LoadMoreBibliographicContributors
+ ) {
+ const state = ctx.getState();
+ const nextPage = state.bibliographicContributorsList.page + 1;
+ const nextPageSize = state.bibliographicContributorsList.pageSize;
+
+ return ctx.dispatch(
+ new GetBibliographicContributors(action.resourceId, action.resourceType, nextPage, nextPageSize)
+ );
+ }
+
+ @Action(LoadMoreContributors)
+ loadMoreContributors(ctx: StateContext, action: LoadMoreContributors) {
+ const state = ctx.getState();
+ const nextPage = state.contributorsList.page + 1;
+ const nextPageSize = state.contributorsList.pageSize;
+
+ return ctx.dispatch(new GetAllContributors(action.resourceId, action.resourceType, nextPage, nextPageSize));
+ }
+
@Action(ResetContributorsState)
resetState(ctx: StateContext) {
ctx.setState({ ...CONTRIBUTORS_STATE_DEFAULTS });
diff --git a/src/app/shared/stores/index.ts b/src/app/shared/stores/index.ts
deleted file mode 100644
index bc94b95fa..000000000
--- a/src/app/shared/stores/index.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-export * from './addons';
-export * from './banners';
-export * from './bookmarks';
-export * from './citations';
-export * from './collections';
-export * from './contributors';
-export * from './current-resource';
-export * from './duplicates';
-export * from './institutions';
-export * from './licenses';
-export * from './my-resources';
-export * from './node-links';
-export * from './projects';
-export * from './regions';
-export * from './subjects';
-export * from './view-only-links';
-export * from './wiki';
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/i18n/en.json b/src/assets/i18n/en.json
index 58c0b767d..11e832513 100644
--- a/src/assets/i18n/en.json
+++ b/src/assets/i18n/en.json
@@ -57,7 +57,8 @@
"removeAll": "Remove All",
"accept": "Accept",
"reject": "Reject",
- "loadMore": "Load more"
+ "loadMore": "Load more",
+ "seeMore": "See more"
},
"accessibility": {
"help": "Help",
@@ -135,7 +136,8 @@
"contributors": "Contributors",
"updated": "Updated",
"dateUpdated": "Date Updated",
- "dateCreated": "Date Created"
+ "dateCreated": "Date Created",
+ "license": "License"
},
"deleteConfirmation": {
"header": "Delete",
@@ -606,7 +608,8 @@
"bibliographicContributor": "Bibliographic Contributor",
"addUnregisteredContributor": "Add Unregistered Contributor",
"addRegisteredContributor": "Add Registered Contributor",
- "unregisteredContributorNotification": "We will notify the user that they have been added to your project"
+ "unregisteredContributorNotification": "We will notify the user that they have been added to your project",
+ "addingContributorsFromParentProject": "Import contributors from {{projectName}}"
},
"removeDialog": {
"title": "Remove contributor",
@@ -718,7 +721,6 @@
"supplementsText2": "on OSF Preprints",
"dateCreated": "Date Created",
"dateUpdated": "Date Updated",
- "license": "License",
"projectDOI": "Project DOI",
"publicationDOI": "Publication DOI",
"registrationDOI": "Registration DOI",
@@ -1157,7 +1159,9 @@
"storage": "OSF Storage",
"pathError": "Path is not specified!",
"success": "Successfully moved.",
- "noMovePermission": "Cannot move or copy to this file provider"
+ "noMovePermission": "Cannot move or copy to this file provider",
+ "movingHeader": "Moving...",
+ "copingHeader": "Coping..."
},
"copyFile": {
"success": "File successfully copied."
@@ -1382,7 +1386,6 @@
"title": "Title",
"description": "Description",
"chooseLicense": "Choose License",
- "license": "License",
"licensePlaceholder": "Select license",
"tags": "Tags",
"tagsPlaceholder": "Add tags here",
@@ -2216,7 +2219,6 @@
"contributors": "Contributors",
"authors": "Authors",
"affiliatedInstitutions": "Affiliated Institutions",
- "license": "License",
"publicationDoi": "Publication DOI",
"subjects": "Subjects",
"tags": "Tags",
@@ -2737,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/assets/icons/socials/globe.svg b/src/assets/icons/socials/globe.svg
new file mode 100644
index 000000000..938451a18
--- /dev/null
+++ b/src/assets/icons/socials/globe.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
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/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/styles/overrides/table.scss b/src/styles/overrides/table.scss
index a27d956b4..416cee613 100644
--- a/src/styles/overrides/table.scss
+++ b/src/styles/overrides/table.scss
@@ -85,10 +85,13 @@ p-table {
td,
th {
- background-color: transparent;
border-bottom: 1px solid var(--grey-2);
}
+ td {
+ background-color: transparent;
+ }
+
tr {
&:hover {
background: transparent;
diff --git a/src/styles/styles.scss b/src/styles/styles.scss
index f192f1d5d..a51b536b8 100644
--- a/src/styles/styles.scss
+++ b/src/styles/styles.scss
@@ -7,6 +7,11 @@
@use "base";
@use "icons";
+@use "@fortawesome/fontawesome-free/scss/fontawesome.scss";
+@use "@fortawesome/fontawesome-free/scss/solid.scss";
+@use "@fortawesome/fontawesome-free/scss/brands.scss";
+@use "@fortawesome/fontawesome-free/scss/regular.scss";
+
@use "./components/md-editor";
@use "./components/preprints";
@use "./components/collections";
diff --git a/src/testing/mocks/environment.token.mock.ts b/src/testing/mocks/environment.token.mock.ts
index 1c8c64216..02105aed2 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: '',
+ newRelicLoaderConfigAgentID: '',
+ 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..afec7c6c7
--- /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',
+ newRelicLoaderConfigAgentID: 'test-agent-id',
+ newRelicLoaderConfigLicenseKey: 'test-loader-license-key',
+ newRelicLoaderConfigApplicationID: 'test-loader-app-id',
+};