From 26f117ea3a64b6c16e8ef8d59d7ffc73945b0e40 Mon Sep 17 00:00:00 2001
From: nsemets
Date: Fri, 30 Jan 2026 18:24:46 +0200
Subject: [PATCH 01/10] [ENG-10048] Add Registry Name to Registration Metadata
(#858)
- Ticket: [ENG-10048]
- Feature flag: n/a
## Summary of Changes
1. Added registry info to overview and metadata pages.
---
.../metadata-date-info.component.html | 4 +-
.../metadata-registry-info.component.html | 13 ++
.../metadata-registry-info.component.scss | 0
.../metadata-registry-info.component.spec.ts | 130 ++++++++++++++++++
.../metadata-registry-info.component.ts | 19 +++
.../metadata/mappers/metadata.mapper.ts | 1 +
.../features/metadata/metadata.component.html | 4 +
.../metadata/metadata.component.spec.ts | 2 +
.../features/metadata/metadata.component.ts | 4 +
.../models/metadata-json-api.model.ts | 1 +
.../metadata/models/metadata.model.ts | 1 +
.../registry-overview-metadata.component.html | 5 +
...gistry-overview-metadata.component.spec.ts | 2 +
.../registry-overview-metadata.component.ts | 2 +
src/assets/i18n/en.json | 1 +
15 files changed, 187 insertions(+), 2 deletions(-)
create mode 100644 src/app/features/metadata/components/metadata-registry-info/metadata-registry-info.component.html
create mode 100644 src/app/features/metadata/components/metadata-registry-info/metadata-registry-info.component.scss
create mode 100644 src/app/features/metadata/components/metadata-registry-info/metadata-registry-info.component.spec.ts
create mode 100644 src/app/features/metadata/components/metadata-registry-info/metadata-registry-info.component.ts
diff --git a/src/app/features/metadata/components/metadata-date-info/metadata-date-info.component.html b/src/app/features/metadata/components/metadata-date-info/metadata-date-info.component.html
index b4c1d5fa2..b83f434ad 100644
--- a/src/app/features/metadata/components/metadata-date-info/metadata-date-info.component.html
+++ b/src/app/features/metadata/components/metadata-date-info/metadata-date-info.component.html
@@ -5,7 +5,7 @@
{{ 'project.overview.metadata.dateCreated' | translate }}
-
+
{{ dateCreated() | date: dateFormat }}
@@ -15,7 +15,7 @@
{{ 'project.overview.metadata.dateUpdated' | translate }}
-
+
{{ dateModified() | date: dateFormat }}
diff --git a/src/app/features/metadata/components/metadata-registry-info/metadata-registry-info.component.html b/src/app/features/metadata/components/metadata-registry-info/metadata-registry-info.component.html
new file mode 100644
index 000000000..3f1465c0d
--- /dev/null
+++ b/src/app/features/metadata/components/metadata-registry-info/metadata-registry-info.component.html
@@ -0,0 +1,13 @@
+
+
+
+
{{ 'registry.overview.metadata.type' | translate }}
+
{{ type() }}
+
+
+
+
{{ 'registry.overview.metadata.registry' | translate }}
+
{{ provider()?.name }}
+
+
+
diff --git a/src/app/features/metadata/components/metadata-registry-info/metadata-registry-info.component.scss b/src/app/features/metadata/components/metadata-registry-info/metadata-registry-info.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/app/features/metadata/components/metadata-registry-info/metadata-registry-info.component.spec.ts b/src/app/features/metadata/components/metadata-registry-info/metadata-registry-info.component.spec.ts
new file mode 100644
index 000000000..8dacb5d07
--- /dev/null
+++ b/src/app/features/metadata/components/metadata-registry-info/metadata-registry-info.component.spec.ts
@@ -0,0 +1,130 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { RegistryProviderDetails } from '@osf/shared/models/provider/registry-provider.model';
+
+import { MetadataRegistryInfoComponent } from './metadata-registry-info.component';
+
+import { OSFTestingModule } from '@testing/osf.testing.module';
+
+describe('MetadataRegistryInfoComponent', () => {
+ let component: MetadataRegistryInfoComponent;
+ let fixture: ComponentFixture;
+
+ const mockProvider: RegistryProviderDetails = {
+ id: 'test-provider-id',
+ name: 'Test Registry Provider',
+ descriptionHtml: 'Test description
',
+ permissions: [],
+ brand: null,
+ iri: 'https://example.com/registry',
+ reviewsWorkflow: 'standard',
+ };
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [MetadataRegistryInfoComponent, OSFTestingModule],
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(MetadataRegistryInfoComponent);
+ component = fixture.componentInstance;
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('should initialize with default values', () => {
+ expect(component.type()).toBe('');
+ expect(component.provider()).toBeUndefined();
+ });
+
+ it('should set type input', () => {
+ const mockType = 'Clinical Trial';
+ fixture.componentRef.setInput('type', mockType);
+ fixture.detectChanges();
+
+ expect(component.type()).toBe(mockType);
+ });
+
+ it('should set provider input', () => {
+ fixture.componentRef.setInput('provider', mockProvider);
+ fixture.detectChanges();
+
+ expect(component.provider()).toEqual(mockProvider);
+ });
+
+ it('should handle undefined type input', () => {
+ fixture.componentRef.setInput('type', undefined);
+ fixture.detectChanges();
+
+ expect(component.type()).toBeUndefined();
+ });
+
+ it('should handle null provider input', () => {
+ fixture.componentRef.setInput('provider', null);
+ fixture.detectChanges();
+
+ expect(component.provider()).toBeNull();
+ });
+
+ it('should render type in template', () => {
+ const mockType = 'Preprint';
+ fixture.componentRef.setInput('type', mockType);
+ fixture.detectChanges();
+
+ const compiled = fixture.nativeElement;
+ const typeElement = compiled.querySelector('[data-test-display-registry-type]');
+ expect(typeElement).toBeTruthy();
+ expect(typeElement.textContent.trim()).toBe(mockType);
+ });
+
+ it('should render provider name in template', () => {
+ fixture.componentRef.setInput('provider', mockProvider);
+ fixture.detectChanges();
+
+ const compiled = fixture.nativeElement;
+ const providerElement = compiled.querySelector('[data-test-display-registry-provider]');
+ expect(providerElement).toBeTruthy();
+ expect(providerElement.textContent.trim()).toBe(mockProvider.name);
+ });
+
+ it('should display empty string when type is empty', () => {
+ fixture.componentRef.setInput('type', '');
+ fixture.detectChanges();
+
+ const compiled = fixture.nativeElement;
+ const typeElement = compiled.querySelector('[data-test-display-registry-type]');
+ expect(typeElement.textContent.trim()).toBe('');
+ });
+
+ it('should display empty string when provider is null', () => {
+ fixture.componentRef.setInput('provider', null);
+ fixture.detectChanges();
+
+ const compiled = fixture.nativeElement;
+ const providerElement = compiled.querySelector('[data-test-display-registry-provider]');
+ expect(providerElement.textContent.trim()).toBe('');
+ });
+
+ it('should display both type and provider when both are set', () => {
+ const mockType = 'Registered Report';
+ fixture.componentRef.setInput('type', mockType);
+ fixture.componentRef.setInput('provider', mockProvider);
+ fixture.detectChanges();
+
+ const compiled = fixture.nativeElement;
+ const typeElement = compiled.querySelector('[data-test-display-registry-type]');
+ const providerElement = compiled.querySelector('[data-test-display-registry-provider]');
+
+ expect(typeElement.textContent.trim()).toBe(mockType);
+ expect(providerElement.textContent.trim()).toBe(mockProvider.name);
+ });
+
+ it('should display translated labels', () => {
+ fixture.detectChanges();
+
+ const compiled = fixture.nativeElement;
+ const headings = compiled.querySelectorAll('h2');
+ expect(headings.length).toBe(2);
+ });
+});
diff --git a/src/app/features/metadata/components/metadata-registry-info/metadata-registry-info.component.ts b/src/app/features/metadata/components/metadata-registry-info/metadata-registry-info.component.ts
new file mode 100644
index 000000000..25c83a0f0
--- /dev/null
+++ b/src/app/features/metadata/components/metadata-registry-info/metadata-registry-info.component.ts
@@ -0,0 +1,19 @@
+import { TranslatePipe } from '@ngx-translate/core';
+
+import { Card } from 'primeng/card';
+
+import { ChangeDetectionStrategy, Component, input } from '@angular/core';
+
+import { RegistryProviderDetails } from '@osf/shared/models/provider/registry-provider.model';
+
+@Component({
+ selector: 'osf-metadata-registry-info',
+ imports: [Card, TranslatePipe],
+ templateUrl: './metadata-registry-info.component.html',
+ styleUrl: './metadata-registry-info.component.scss',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class MetadataRegistryInfoComponent {
+ type = input('');
+ provider = input();
+}
diff --git a/src/app/features/metadata/mappers/metadata.mapper.ts b/src/app/features/metadata/mappers/metadata.mapper.ts
index 5c166574d..eb6044cb3 100644
--- a/src/app/features/metadata/mappers/metadata.mapper.ts
+++ b/src/app/features/metadata/mappers/metadata.mapper.ts
@@ -25,6 +25,7 @@ export class MetadataMapper {
provider: response.embeds?.provider?.data.id,
public: response.attributes.public,
currentUserPermissions: response.attributes.current_user_permissions,
+ registrationSupplement: response.attributes.registration_supplement,
};
}
diff --git a/src/app/features/metadata/metadata.component.html b/src/app/features/metadata/metadata.component.html
index 491ad60dd..f49bd7ff0 100644
--- a/src/app/features/metadata/metadata.component.html
+++ b/src/app/features/metadata/metadata.component.html
@@ -33,6 +33,10 @@
[readonly]="!hasWriteAccess()"
/>
+ @if (isRegistrationType()) {
+
+ }
+
{
{ selector: MetadataSelectors.getSubmitting, value: false },
{ selector: MetadataSelectors.getCedarRecords, value: [] },
{ selector: MetadataSelectors.getCedarTemplates, value: null },
+ { selector: RegistrationProviderSelectors.getBrandedProvider, value: null },
],
}),
],
diff --git a/src/app/features/metadata/metadata.component.ts b/src/app/features/metadata/metadata.component.ts
index e20e097f7..85958743d 100644
--- a/src/app/features/metadata/metadata.component.ts
+++ b/src/app/features/metadata/metadata.component.ts
@@ -37,6 +37,7 @@ import {
InstitutionsSelectors,
UpdateResourceInstitutions,
} from '@osf/shared/stores/institutions';
+import { RegistrationProviderSelectors } from '@osf/shared/stores/registration-provider';
import {
FetchChildrenSubjects,
FetchSelectedSubjects,
@@ -48,6 +49,7 @@ import { MetadataTabsModel } from '@shared/models/metadata-tabs.model';
import { SubjectModel } from '@shared/models/subject/subject.model';
import { MetadataCollectionsComponent } from './components/metadata-collections/metadata-collections.component';
+import { MetadataRegistryInfoComponent } from './components/metadata-registry-info/metadata-registry-info.component';
import { EditTitleDialogComponent } from './dialogs/edit-title-dialog/edit-title-dialog.component';
import {
MetadataAffiliatedInstitutionsComponent,
@@ -112,6 +114,7 @@ import {
MetadataTitleComponent,
MetadataRegistrationDoiComponent,
MetadataCollectionsComponent,
+ MetadataRegistryInfoComponent,
],
templateUrl: './metadata.component.html',
styleUrl: './metadata.component.scss',
@@ -150,6 +153,7 @@ export class MetadataComponent implements OnInit {
affiliatedInstitutions = select(InstitutionsSelectors.getResourceInstitutions);
areInstitutionsLoading = select(InstitutionsSelectors.areResourceInstitutionsLoading);
areResourceInstitutionsSubmitting = select(InstitutionsSelectors.areResourceInstitutionsSubmitting);
+ registryProvider = select(RegistrationProviderSelectors.getBrandedProvider);
projectSubmissions = select(CollectionsSelectors.getCurrentProjectSubmissions);
isProjectSubmissionsLoading = select(CollectionsSelectors.getCurrentProjectSubmissionsLoading);
diff --git a/src/app/features/metadata/models/metadata-json-api.model.ts b/src/app/features/metadata/models/metadata-json-api.model.ts
index 11dfbd342..802777461 100644
--- a/src/app/features/metadata/models/metadata-json-api.model.ts
+++ b/src/app/features/metadata/models/metadata-json-api.model.ts
@@ -21,6 +21,7 @@ export interface MetadataAttributesJsonApi {
category?: string;
node_license?: LicenseRecordJsonApi;
public?: boolean;
+ registration_supplement?: string;
current_user_permissions: UserPermissions[];
}
diff --git a/src/app/features/metadata/models/metadata.model.ts b/src/app/features/metadata/models/metadata.model.ts
index 43c44bf84..b8ce8f5cb 100644
--- a/src/app/features/metadata/models/metadata.model.ts
+++ b/src/app/features/metadata/models/metadata.model.ts
@@ -24,6 +24,7 @@ export interface MetadataModel {
};
public?: boolean;
currentUserPermissions: UserPermissions[];
+ registrationSupplement?: string;
}
export interface CustomItemMetadataRecord {
diff --git a/src/app/features/registry/components/registry-overview-metadata/registry-overview-metadata.component.html b/src/app/features/registry/components/registry-overview-metadata/registry-overview-metadata.component.html
index 2637d5b9b..c8f736206 100644
--- a/src/app/features/registry/components/registry-overview-metadata/registry-overview-metadata.component.html
+++ b/src/app/features/registry/components/registry-overview-metadata/registry-overview-metadata.component.html
@@ -42,6 +42,11 @@ {{ 'registry.overview.metadata.type' | translate }}
{{ resource.registrationSupplement }}
+
+
{{ 'registry.overview.metadata.registry' | translate }}
+
{{ registryProvider()?.name }}
+
+
@if (resource.associatedProjectId) {
{{ 'registry.overview.metadata.associatedProject' | translate }}
diff --git a/src/app/features/registry/components/registry-overview-metadata/registry-overview-metadata.component.spec.ts b/src/app/features/registry/components/registry-overview-metadata/registry-overview-metadata.component.spec.ts
index 697e0a1de..72dca0a1a 100644
--- a/src/app/features/registry/components/registry-overview-metadata/registry-overview-metadata.component.spec.ts
+++ b/src/app/features/registry/components/registry-overview-metadata/registry-overview-metadata.component.spec.ts
@@ -17,6 +17,7 @@ import { SubjectsListComponent } from '@osf/shared/components/subjects-list/subj
import { TagsListComponent } from '@osf/shared/components/tags-list/tags-list.component';
import { CurrentResourceType, ResourceType } from '@osf/shared/enums/resource-type.enum';
import { ContributorsSelectors, LoadMoreBibliographicContributors } from '@osf/shared/stores/contributors';
+import { RegistrationProviderSelectors } from '@osf/shared/stores/registration-provider';
import { FetchSelectedSubjects, SubjectsSelectors } from '@osf/shared/stores/subjects';
import {
@@ -79,6 +80,7 @@ describe('RegistryOverviewMetadataComponent', () => {
{ selector: RegistrySelectors.isIdentifiersLoading, value: false },
{ selector: RegistrySelectors.getInstitutions, value: [] },
{ selector: RegistrySelectors.isInstitutionsLoading, value: false },
+ { selector: RegistrationProviderSelectors.getBrandedProvider, value: null },
{ selector: SubjectsSelectors.getSubjects, value: [] },
{ selector: SubjectsSelectors.getSubjectsLoading, value: false },
{ selector: ContributorsSelectors.getBibliographicContributors, value: [] },
diff --git a/src/app/features/registry/components/registry-overview-metadata/registry-overview-metadata.component.ts b/src/app/features/registry/components/registry-overview-metadata/registry-overview-metadata.component.ts
index c2c3b4686..693e8eebd 100644
--- a/src/app/features/registry/components/registry-overview-metadata/registry-overview-metadata.component.ts
+++ b/src/app/features/registry/components/registry-overview-metadata/registry-overview-metadata.component.ts
@@ -19,6 +19,7 @@ import { TagsListComponent } from '@osf/shared/components/tags-list/tags-list.co
import { TruncatedTextComponent } from '@osf/shared/components/truncated-text/truncated-text.component';
import { CurrentResourceType, ResourceType } from '@osf/shared/enums/resource-type.enum';
import { ContributorsSelectors, LoadMoreBibliographicContributors } from '@osf/shared/stores/contributors';
+import { RegistrationProviderSelectors } from '@osf/shared/stores/registration-provider';
import { FetchSelectedSubjects, SubjectsSelectors } from '@osf/shared/stores/subjects';
import {
@@ -54,6 +55,7 @@ export class RegistryOverviewMetadataComponent {
private readonly router = inject(Router);
readonly registry = select(RegistrySelectors.getRegistry);
+ readonly registryProvider = select(RegistrationProviderSelectors.getBrandedProvider);
readonly isAnonymous = select(RegistrySelectors.isRegistryAnonymous);
canEdit = select(RegistrySelectors.hasWriteAccess);
diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json
index 9ee6ffb5c..5a12c0109 100644
--- a/src/assets/i18n/en.json
+++ b/src/assets/i18n/en.json
@@ -2645,6 +2645,7 @@
},
"metadata": {
"type": "Registration Type",
+ "registry": "Registry",
"registeredDate": "Date registered",
"doi": "Registration DOI",
"associatedProject": "Associated project"
From bf56c47025fcaeede0ccf7e3b696d6af56942b79 Mon Sep 17 00:00:00 2001
From: mkovalua
Date: Fri, 30 Jan 2026 18:31:21 +0200
Subject: [PATCH 02/10] [ENG-10047] Display Affiliated Institution(s) on User
Profile Page (#859)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Ticket: https://openscience.atlassian.net/browse/ENG-10047
- Feature flag: n/a
## Purpose
User profile pages do not currently display a user’s affiliated institution(s), even when the user has active institutional affiliations set in OSF. This makes it difficult for others to understand a user’s institutional context and reduces the visibility of institutional participation on the platform.
## Summary of Changes
Implement affiliated Institution(s) on User Profile Page showing
---
.../profile-information.component.html | 19 +++++++++++++
.../profile-information.component.spec.ts | 27 +++++++++++++++++++
.../profile-information.component.ts | 5 ++++
.../features/profile/profile.component.html | 7 ++++-
src/app/features/profile/profile.component.ts | 5 ++++
.../store/account-settings.actions.ts | 2 ++
.../store/account-settings.state.ts | 4 +--
.../add-project-form.component.spec.ts | 2 +-
.../shared/services/institutions.service.ts | 4 +--
.../institutions/institutions.actions.ts | 1 +
.../stores/institutions/institutions.state.ts | 4 +--
11 files changed, 72 insertions(+), 8 deletions(-)
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 b4636126f..80ae98a4a 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
@@ -35,6 +35,25 @@ {{ currentUser()?.fullName }}
}
+
+ @for (institution of currentUserInstitutions(); track $index) {
+
+
+
+ }
+
+
@if (!isMedium() && showEdit()) {
{
it('should initialize with default inputs', () => {
expect(component.currentUser()).toBeUndefined();
expect(component.showEdit()).toBe(false);
+ expect(component.currentUserInstitutions()).toBeUndefined();
});
it('should accept user input', () => {
@@ -172,4 +175,28 @@ describe('ProfileInformationComponent', () => {
component.toProfileSettings();
expect(component.editProfile.emit).toHaveBeenCalled();
});
+
+ it('should accept currentUserInstitutions input', () => {
+ const mockInstitutions: Institution[] = [MOCK_INSTITUTION];
+ fixture.componentRef.setInput('currentUserInstitutions', mockInstitutions);
+ fixture.detectChanges();
+ expect(component.currentUserInstitutions()).toEqual(mockInstitutions);
+ });
+
+ it('should not render institution logos when currentUserInstitutions is undefined', () => {
+ fixture.componentRef.setInput('currentUserInstitutions', undefined);
+ fixture.detectChanges();
+ const logos = fixture.nativeElement.querySelectorAll('img.fit-contain');
+ expect(logos.length).toBe(0);
+ });
+
+ it('should render institution logos when currentUserInstitutions is provided', () => {
+ const institutions: Institution[] = [MOCK_INSTITUTION];
+ fixture.componentRef.setInput('currentUserInstitutions', institutions);
+ fixture.detectChanges();
+
+ const logos = fixture.nativeElement.querySelectorAll('img.fit-contain');
+ expect(logos.length).toBe(institutions.length);
+ expect(logos[0].alt).toBe(institutions[0].name);
+ });
});
diff --git a/src/app/features/profile/components/profile-information/profile-information.component.ts b/src/app/features/profile/components/profile-information/profile-information.component.ts
index 3304a8ce2..0740068b0 100644
--- a/src/app/features/profile/components/profile-information/profile-information.component.ts
+++ b/src/app/features/profile/components/profile-information/profile-information.component.ts
@@ -5,6 +5,7 @@ import { Button } from 'primeng/button';
import { DatePipe, NgOptimizedImage } from '@angular/common';
import { ChangeDetectionStrategy, Component, computed, inject, input, output } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
+import { RouterLink } from '@angular/router';
import { EducationHistoryComponent } from '@osf/shared/components/education-history/education-history.component';
import { EmploymentHistoryComponent } from '@osf/shared/components/employment-history/employment-history.component';
@@ -12,6 +13,7 @@ import { SOCIAL_LINKS } from '@osf/shared/constants/social-links.const';
import { IS_MEDIUM } from '@osf/shared/helpers/breakpoints.tokens';
import { UserModel } from '@osf/shared/models/user/user.models';
import { SortByDatePipe } from '@osf/shared/pipes/sort-by-date.pipe';
+import { Institution } from '@shared/models/institutions/institutions.models';
import { mapUserSocials } from '../../helpers';
@@ -25,6 +27,7 @@ import { mapUserSocials } from '../../helpers';
DatePipe,
NgOptimizedImage,
SortByDatePipe,
+ RouterLink,
],
templateUrl: './profile-information.component.html',
styleUrl: './profile-information.component.scss',
@@ -32,6 +35,8 @@ import { mapUserSocials } from '../../helpers';
})
export class ProfileInformationComponent {
currentUser = input();
+
+ currentUserInstitutions = input();
showEdit = input(false);
editProfile = output();
diff --git a/src/app/features/profile/profile.component.html b/src/app/features/profile/profile.component.html
index 4176e33fc..958d22e05 100644
--- a/src/app/features/profile/profile.component.html
+++ b/src/app/features/profile/profile.component.html
@@ -13,7 +13,12 @@
}
-
+
@if (defaultSearchFiltersInitialized()) {
diff --git a/src/app/features/profile/profile.component.ts b/src/app/features/profile/profile.component.ts
index fb7c186a8..86e1afa43 100644
--- a/src/app/features/profile/profile.component.ts
+++ b/src/app/features/profile/profile.component.ts
@@ -25,6 +25,7 @@ import { SEARCH_TAB_OPTIONS } from '@osf/shared/constants/search-tab-options.con
import { ResourceType } from '@osf/shared/enums/resource-type.enum';
import { UserModel } from '@osf/shared/models/user/user.models';
import { SetDefaultFilterValue } from '@osf/shared/stores/global-search';
+import { FetchUserInstitutions, InstitutionsSelectors } from '@shared/stores/institutions';
import { ProfileInformationComponent } from './components';
import { FetchUserProfile, ProfileSelectors, SetUserProfile } from './store';
@@ -46,11 +47,13 @@ export class ProfileComponent implements OnInit, OnDestroy {
fetchUserProfile: FetchUserProfile,
setDefaultFilterValue: SetDefaultFilterValue,
setUserProfile: SetUserProfile,
+ fetchUserInstitutions: FetchUserInstitutions,
});
loggedInUser = select(UserSelectors.getCurrentUser);
userProfile = select(ProfileSelectors.getUserProfile);
isUserLoading = select(ProfileSelectors.isUserProfileLoading);
+ institutions = select(InstitutionsSelectors.getUserInstitutions);
resourceTabOptions = SEARCH_TAB_OPTIONS.filter((x) => x.value !== ResourceType.Agent);
@@ -67,6 +70,8 @@ export class ProfileComponent implements OnInit, OnDestroy {
} else if (currentUser) {
this.setupMyProfile(currentUser);
}
+
+ this.actions.fetchUserInstitutions(userId || currentUser?.id);
}
ngOnDestroy(): void {
diff --git a/src/app/features/settings/account-settings/store/account-settings.actions.ts b/src/app/features/settings/account-settings/store/account-settings.actions.ts
index db53fa534..e4ba12de7 100644
--- a/src/app/features/settings/account-settings/store/account-settings.actions.ts
+++ b/src/app/features/settings/account-settings/store/account-settings.actions.ts
@@ -24,6 +24,8 @@ export class DeleteExternalIdentity {
export class GetUserInstitutions {
static readonly type = '[AccountSettings] Get User Institutions';
+
+ constructor(public userId = 'me') {}
}
export class DeleteUserInstitution {
diff --git a/src/app/features/settings/account-settings/store/account-settings.state.ts b/src/app/features/settings/account-settings/store/account-settings.state.ts
index eee615405..9382c7ee0 100644
--- a/src/app/features/settings/account-settings/store/account-settings.state.ts
+++ b/src/app/features/settings/account-settings/store/account-settings.state.ts
@@ -84,8 +84,8 @@ export class AccountSettingsState {
}
@Action(GetUserInstitutions)
- getUserInstitutions(ctx: StateContext) {
- return this.institutionsService.getUserInstitutions().pipe(
+ getUserInstitutions(ctx: StateContext, action: GetUserInstitutions) {
+ return this.institutionsService.getUserInstitutions(action.userId).pipe(
tap((userInstitutions) => ctx.patchState({ userInstitutions })),
catchError((error) => throwError(() => error))
);
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 54336feae..ee325c8f8 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
@@ -9,11 +9,11 @@ import { FormControl, FormGroup, Validators } from '@angular/forms';
import { UserSelectors } from '@core/store/user';
import { ProjectFormControls } from '@osf/shared/enums/create-project-form-controls.enum';
import { CustomValidators } from '@osf/shared/helpers/custom-form-validators.helper';
-import { ProjectModel } from '@osf/shared/models/projects';
import { InstitutionsSelectors } from '@osf/shared/stores/institutions';
import { ProjectsSelectors } from '@osf/shared/stores/projects';
import { RegionsSelectors } from '@osf/shared/stores/regions';
import { ProjectForm } from '@shared/models/projects/create-project-form.model';
+import { ProjectModel } from '@shared/models/projects/projects.models';
import { AffiliatedInstitutionSelectComponent } from '../affiliated-institution-select/affiliated-institution-select.component';
import { ProjectSelectorComponent } from '../project-selector/project-selector.component';
diff --git a/src/app/shared/services/institutions.service.ts b/src/app/shared/services/institutions.service.ts
index 9858f02f2..b90fd89ea 100644
--- a/src/app/shared/services/institutions.service.ts
+++ b/src/app/shared/services/institutions.service.ts
@@ -47,8 +47,8 @@ export class InstitutionsService {
.pipe(map((response) => InstitutionsMapper.fromResponseWithMeta(response)));
}
- getUserInstitutions(): Observable {
- const url = `${this.apiUrl}/users/me/institutions/`;
+ getUserInstitutions(userId: string): Observable {
+ const url = `${this.apiUrl}/users/${userId}/institutions/`;
return this.jsonApiService
.get(url)
diff --git a/src/app/shared/stores/institutions/institutions.actions.ts b/src/app/shared/stores/institutions/institutions.actions.ts
index 7645e7d6b..4e7790f79 100644
--- a/src/app/shared/stores/institutions/institutions.actions.ts
+++ b/src/app/shared/stores/institutions/institutions.actions.ts
@@ -3,6 +3,7 @@ import { Institution } from '@shared/models/institutions/institutions.models';
export class FetchUserInstitutions {
static readonly type = '[Institutions] Fetch User Institutions';
+ constructor(public userId = 'me') {}
}
export class FetchInstitutions {
diff --git a/src/app/shared/stores/institutions/institutions.state.ts b/src/app/shared/stores/institutions/institutions.state.ts
index 631f9ec56..757012656 100644
--- a/src/app/shared/stores/institutions/institutions.state.ts
+++ b/src/app/shared/stores/institutions/institutions.state.ts
@@ -25,10 +25,10 @@ export class InstitutionsState {
private readonly institutionsService = inject(InstitutionsService);
@Action(FetchUserInstitutions)
- getUserInstitutions(ctx: StateContext) {
+ getUserInstitutions(ctx: StateContext, action: FetchUserInstitutions) {
ctx.setState(patch({ userInstitutions: patch({ isLoading: true }) }));
- return this.institutionsService.getUserInstitutions().pipe(
+ return this.institutionsService.getUserInstitutions(action.userId).pipe(
tap((institutions) => {
ctx.setState(
patch({
From cd3155997d62a4e21cd1b581bcf523e608e33ce5 Mon Sep 17 00:00:00 2001
From: sh-andriy <105591819+sh-andriy@users.noreply.github.com>
Date: Fri, 30 Jan 2026 18:33:36 +0200
Subject: [PATCH 03/10] ENG-9720 | fix(addons): Fix GitLab pagination Load More
button not showing (#847)
---
.../storage-item-selector.component.spec.ts | 105 ++++++++++++++++++
.../storage-item-selector.component.ts | 4 +-
2 files changed, 107 insertions(+), 2 deletions(-)
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 2e3797ea6..f3961bd7d 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
@@ -2,9 +2,11 @@ import { MockComponents, MockProvider } from 'ng-mocks';
import { DialogService } from 'primeng/dynamicdialog';
+import { signal, WritableSignal } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { OperationNames } from '@shared/enums/operation-names.enum';
+import { OperationInvocation } from '@shared/models/addons/operation-invocation.model';
import { AddonsSelectors } from '@shared/stores/addons';
import { GoogleFilePickerComponent } from '../../google-file-picker/google-file-picker.component';
@@ -20,9 +22,11 @@ describe('StorageItemSelectorComponent', () => {
let component: StorageItemSelectorComponent;
let fixture: ComponentFixture;
let mockDialogService: ReturnType;
+ let mockOperationInvocation: WritableSignal;
beforeEach(async () => {
mockDialogService = DialogServiceMockBuilder.create().withOpenMock().build();
+ mockOperationInvocation = signal(null);
await TestBed.configureTestingModule({
imports: [
@@ -45,6 +49,10 @@ describe('StorageItemSelectorComponent', () => {
selector: AddonsSelectors.getCreatedOrUpdatedConfiguredAddonSubmitting,
value: false,
},
+ {
+ selector: AddonsSelectors.getOperationInvocation,
+ value: mockOperationInvocation,
+ },
],
}),
MockProvider(DialogService, mockDialogService),
@@ -53,6 +61,10 @@ describe('StorageItemSelectorComponent', () => {
fixture = TestBed.createComponent(StorageItemSelectorComponent);
component = fixture.componentInstance;
+ fixture.componentRef.setInput('isGoogleFilePicker', false);
+ fixture.componentRef.setInput('accountName', 'test-account');
+ fixture.componentRef.setInput('accountId', 'test-id');
+ fixture.componentRef.setInput('operationInvocationResult', []);
});
it('should create', () => {
@@ -115,4 +127,97 @@ describe('StorageItemSelectorComponent', () => {
expect(breadcrumbs[0].id).toBe(itemId);
expect(breadcrumbs[0].label).toBe(itemName);
});
+
+ describe('showLoadMoreButton', () => {
+ it('should return false when operationInvocation is null', () => {
+ mockOperationInvocation.set(null);
+ fixture.detectChanges();
+
+ expect(component.showLoadMoreButton()).toBe(false);
+ });
+
+ it('should return false when nextSampleCursor is not present', () => {
+ mockOperationInvocation.set({
+ id: 'test-id',
+ type: 'operation-invocation',
+ invocationStatus: 'success',
+ operationName: 'list_root_items',
+ operationKwargs: {},
+ operationResult: [],
+ itemCount: 10,
+ thisSampleCursor: 'cursor-1',
+ });
+ fixture.detectChanges();
+
+ expect(component.showLoadMoreButton()).toBe(false);
+ });
+
+ it('should return true when nextSampleCursor differs from thisSampleCursor', () => {
+ mockOperationInvocation.set({
+ id: 'test-id',
+ type: 'operation-invocation',
+ invocationStatus: 'success',
+ operationName: 'list_root_items',
+ operationKwargs: {},
+ operationResult: [],
+ itemCount: 20,
+ thisSampleCursor: 'cursor-1',
+ nextSampleCursor: 'cursor-2',
+ });
+ fixture.detectChanges();
+
+ expect(component.showLoadMoreButton()).toBe(true);
+ });
+
+ it('should return true for opaque/base64 cursors like GitLab uses', () => {
+ // GitLab uses base64-encoded cursors where lexicographic comparison doesn't work
+ mockOperationInvocation.set({
+ id: 'test-id',
+ type: 'operation-invocation',
+ invocationStatus: 'success',
+ operationName: 'list_root_items',
+ operationKwargs: {},
+ operationResult: [],
+ itemCount: 20,
+ thisSampleCursor: 'eyJpZCI6MTIzfQ==',
+ nextSampleCursor: 'eyJpZCI6MTQ1fQ==',
+ });
+ fixture.detectChanges();
+
+ expect(component.showLoadMoreButton()).toBe(true);
+ });
+
+ it('should return false when nextSampleCursor equals thisSampleCursor', () => {
+ mockOperationInvocation.set({
+ id: 'test-id',
+ type: 'operation-invocation',
+ invocationStatus: 'success',
+ operationName: 'list_root_items',
+ operationKwargs: {},
+ operationResult: [],
+ itemCount: 10,
+ thisSampleCursor: 'cursor-1',
+ nextSampleCursor: 'cursor-1',
+ });
+ fixture.detectChanges();
+
+ expect(component.showLoadMoreButton()).toBe(false);
+ });
+
+ it('should return true when nextSampleCursor exists but thisSampleCursor is undefined', () => {
+ mockOperationInvocation.set({
+ id: 'test-id',
+ type: 'operation-invocation',
+ invocationStatus: 'success',
+ operationName: 'list_root_items',
+ operationKwargs: {},
+ operationResult: [],
+ itemCount: 20,
+ nextSampleCursor: 'cursor-2',
+ });
+ fixture.detectChanges();
+
+ expect(component.showLoadMoreButton()).toBe(true);
+ });
+ });
});
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 fc29d0ae8..3f8588d07 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
@@ -199,10 +199,10 @@ export class StorageItemSelectorComponent implements OnInit {
readonly showLoadMoreButton = computed(() => {
const invocation = this.operationInvocation();
- if (!invocation?.nextSampleCursor || !invocation?.thisSampleCursor) {
+ if (!invocation?.nextSampleCursor) {
return false;
}
- return invocation.nextSampleCursor > invocation.thisSampleCursor;
+ return invocation.nextSampleCursor !== invocation.thisSampleCursor;
});
handleCreateOperationInvocation(
From 0113de1f113389f98149ad2c2f086da4f8cb43d2 Mon Sep 17 00:00:00 2001
From: mkovalua
Date: Fri, 30 Jan 2026 18:37:57 +0200
Subject: [PATCH 04/10] [ENG-9042] Each registries, preprints, and collections
provider sets a default license in admin. (#796)
- Ticket: https://openscience.atlassian.net/browse/ENG-9042
- Feature flag: n/a
## Purpose
Each registries, preprints, and collections provider sets a default license in admin.
## Summary of Changes
These should be preselected on all registration drafts on that provider, and the user can change them from there.
All provider types need a serialized default license.
---
.../preprints-metadata-step.component.html | 2 +-
.../preprints-metadata-step.component.ts | 20 ++++++++++++-
.../preprints/mappers/preprints.mapper.ts | 1 +
.../models/preprint-json-api.models.ts | 1 +
.../preprints/models/preprint.models.ts | 1 +
.../preprint-stepper.state.ts | 1 -
.../registries-license.component.html | 2 +-
.../registries-license.component.ts | 29 +++++++++++++------
.../registration/registration.mapper.ts | 1 +
.../registration/draft-registration.model.ts | 1 +
.../registration-json-api.model.ts | 1 +
11 files changed, 47 insertions(+), 13 deletions(-)
diff --git a/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-metadata-step.component.html b/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-metadata-step.component.html
index 77e60fe11..3d20423c3 100644
--- a/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-metadata-step.component.html
+++ b/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-metadata-step.component.html
@@ -16,7 +16,7 @@ {{ 'shared.license.title' | translate }}
();
nextClicked = output();
backClicked = output();
+ defaultLicense = signal(undefined);
+
+ constructor() {
+ effect(() => {
+ const licenses = this.licenses();
+ const preprint = this.createdPreprint();
+
+ if (licenses.length && preprint && !preprint.licenseId && preprint.defaultLicenseId) {
+ const defaultLicense = licenses.find((license) => license.id === preprint?.defaultLicenseId);
+ if (defaultLicense) {
+ this.defaultLicense.set(defaultLicense.id);
+ if (!defaultLicense.requiredFields.length) {
+ this.actions.saveLicense(defaultLicense.id);
+ }
+ }
+ }
+ });
+ }
ngOnInit() {
this.actions.fetchLicenses();
diff --git a/src/app/features/preprints/mappers/preprints.mapper.ts b/src/app/features/preprints/mappers/preprints.mapper.ts
index 61dd10329..23f72d0be 100644
--- a/src/app/features/preprints/mappers/preprints.mapper.ts
+++ b/src/app/features/preprints/mappers/preprints.mapper.ts
@@ -83,6 +83,7 @@ export class PreprintsMapper {
articleDoiLink: response.links.doi,
embeddedLicense: null,
providerId: response.relationships?.provider?.data?.id,
+ defaultLicenseId: response.attributes.default_license_id,
};
}
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 9761ef0fa..3fa417f48 100644
--- a/src/app/features/preprints/models/preprint-json-api.models.ts
+++ b/src/app/features/preprints/models/preprint-json-api.models.ts
@@ -37,6 +37,7 @@ export interface PreprintAttributesJsonApi {
why_no_prereg: StringOrNull;
prereg_links: string[];
prereg_link_info: PreregLinkInfo | null;
+ default_license_id: string;
}
export interface PreprintRelationshipsJsonApi {
diff --git a/src/app/features/preprints/models/preprint.models.ts b/src/app/features/preprints/models/preprint.models.ts
index 527c1a76d..e966e60ce 100644
--- a/src/app/features/preprints/models/preprint.models.ts
+++ b/src/app/features/preprints/models/preprint.models.ts
@@ -47,6 +47,7 @@ export interface PreprintModel {
articleDoiLink?: string;
identifiers?: IdentifierModel[];
providerId: string;
+ defaultLicenseId?: string;
}
export interface PreprintFilesLinks {
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 d915e5782..1e1eb3bd1 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
@@ -87,7 +87,6 @@ export class PreprintStepperState {
if (action.payload.isPublished) {
ctx.setState(patch({ hasBeenSubmitted: true }));
}
-
ctx.setState(patch({ preprint: patch({ isSubmitting: false, data: preprint }) }));
}),
catchError((error) => handleSectionError(ctx, 'preprint', error))
diff --git a/src/app/features/registries/components/registries-metadata-step/registries-license/registries-license.component.html b/src/app/features/registries/components/registries-metadata-step/registries-license/registries-license.component.html
index da782b1e3..0366f69b3 100644
--- a/src/app/features/registries/components/registries-metadata-step/registries-license/registries-license.component.html
+++ b/src/app/features/registries/components/registries-metadata-step/registries-license/registries-license.component.html
@@ -11,7 +11,7 @@ {{ 'shared.license.title' | translate }}
{
const licenses = this.licenses();
- const selectedLicense = untracked(() => this.selectedLicense());
+ const selectedLicense = this.selectedLicense();
+ const defaultLicenseId = this.draftRegistration()?.defaultLicenseId;
- if (!licenses.length || !selectedLicense) {
+ if (!licenses.length) {
return;
}
- if (!licenses.find((license) => license.id === selectedLicense.id)) {
- this.control().patchValue({
- id: null,
- });
- this.control().markAsTouched();
- this.control().updateValueAndValidity();
+ if (
+ defaultLicenseId &&
+ (!selectedLicense?.id || !licenses.find((license) => license.id === selectedLicense?.id))
+ ) {
+ const defaultLicense = licenses.find((license) => license.id === defaultLicenseId);
+ if (defaultLicense) {
+ this.control().patchValue({
+ id: defaultLicense.id,
+ });
+ this.control().markAsTouched();
+ this.control().updateValueAndValidity();
+
+ if (!defaultLicense.requiredFields.length) {
+ this.actions.saveLicense(this.draftId, defaultLicense.id);
+ }
+ }
}
});
}
diff --git a/src/app/shared/mappers/registration/registration.mapper.ts b/src/app/shared/mappers/registration/registration.mapper.ts
index 43040ec85..41fbe6ef4 100644
--- a/src/app/shared/mappers/registration/registration.mapper.ts
+++ b/src/app/shared/mappers/registration/registration.mapper.ts
@@ -47,6 +47,7 @@ export class RegistrationMapper {
},
providerId: response.relationships.provider?.data?.id || '',
hasProject: !!response.attributes.has_project,
+ defaultLicenseId: response.attributes?.default_license_id,
components: [],
currentUserPermissions: response.attributes.current_user_permissions,
};
diff --git a/src/app/shared/models/registration/draft-registration.model.ts b/src/app/shared/models/registration/draft-registration.model.ts
index 4d0230e0d..4a18222ac 100644
--- a/src/app/shared/models/registration/draft-registration.model.ts
+++ b/src/app/shared/models/registration/draft-registration.model.ts
@@ -18,6 +18,7 @@ export interface DraftRegistrationModel {
branchedFrom?: Partial;
providerId: string;
hasProject: boolean;
+ defaultLicenseId?: string;
components: Partial[];
currentUserPermissions: UserPermissions[];
}
diff --git a/src/app/shared/models/registration/registration-json-api.model.ts b/src/app/shared/models/registration/registration-json-api.model.ts
index 1a6d64e12..1e38892af 100644
--- a/src/app/shared/models/registration/registration-json-api.model.ts
+++ b/src/app/shared/models/registration/registration-json-api.model.ts
@@ -39,6 +39,7 @@ export interface DraftRegistrationAttributesJsonApi {
datetime_updated: string;
description: string;
has_project: boolean;
+ default_license_id?: string;
node_license: LicenseRecordJsonApi;
registration_metadata: Record;
registration_responses: Record;
From 4d85d516616778edf3ebba8faec4e2a77017c7fd Mon Sep 17 00:00:00 2001
From: mkovalua
Date: Fri, 30 Jan 2026 18:40:56 +0200
Subject: [PATCH 05/10] [ENG-6719] Show Funder and Grant ID information on
registries moderation cards (#855)
- Ticket: https://openscience.atlassian.net/browse/ENG-6719
- Feature flag: n/a
## Purpose
Some registries members use funder information to determine priority or relevance of submissions. They currently must drill down several layers within their moderation workflow to find this information.
---
src/app/features/metadata/services/index.ts | 1 -
.../features/metadata/store/metadata.state.ts | 2 +-
...egistry-pending-submissions.component.html | 2 +-
.../registry-pending-submissions.component.ts | 7 ++
.../registry-submission-item.component.html | 9 ++-
.../registry-submission-item.component.ts | 7 +-
.../registry-submissions.component.html | 2 +-
.../registry-submissions.component.ts | 7 ++
.../models/registry-moderation.model.ts | 3 +
.../registry-moderation.actions.ts | 6 ++
.../registry-moderation.state.ts | 59 ++++++++++++++++-
.../funder-awards-list.component.html | 24 +++++++
.../funder-awards-list.component.scss | 0
.../funder-awards-list.component.spec.ts | 66 +++++++++++++++++++
.../funder-awards-list.component.ts | 21 ++++++
.../services/metadata.service.ts | 19 +++---
src/assets/i18n/en.json | 1 +
17 files changed, 218 insertions(+), 18 deletions(-)
delete mode 100644 src/app/features/metadata/services/index.ts
create mode 100644 src/app/shared/funder-awards-list/funder-awards-list.component.html
create mode 100644 src/app/shared/funder-awards-list/funder-awards-list.component.scss
create mode 100644 src/app/shared/funder-awards-list/funder-awards-list.component.spec.ts
create mode 100644 src/app/shared/funder-awards-list/funder-awards-list.component.ts
rename src/app/{features/metadata => shared}/services/metadata.service.ts (97%)
diff --git a/src/app/features/metadata/services/index.ts b/src/app/features/metadata/services/index.ts
deleted file mode 100644
index 92c69e450..000000000
--- a/src/app/features/metadata/services/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './metadata.service';
diff --git a/src/app/features/metadata/store/metadata.state.ts b/src/app/features/metadata/store/metadata.state.ts
index 245895fd9..af839233a 100644
--- a/src/app/features/metadata/store/metadata.state.ts
+++ b/src/app/features/metadata/store/metadata.state.ts
@@ -5,9 +5,9 @@ import { catchError, finalize, tap } from 'rxjs';
import { inject, Injectable } from '@angular/core';
import { handleSectionError } from '@osf/shared/helpers/state-error.handler';
+import { MetadataService } from '@osf/shared/services/metadata.service';
import { CedarMetadataRecord, CedarMetadataRecordJsonApi, MetadataModel } from '../models';
-import { MetadataService } from '../services';
import {
AddCedarMetadataRecordToState,
diff --git a/src/app/features/moderation/components/registry-pending-submissions/registry-pending-submissions.component.html b/src/app/features/moderation/components/registry-pending-submissions/registry-pending-submissions.component.html
index 77f120b97..7275ce5ce 100644
--- a/src/app/features/moderation/components/registry-pending-submissions/registry-pending-submissions.component.html
+++ b/src/app/features/moderation/components/registry-pending-submissions/registry-pending-submissions.component.html
@@ -39,7 +39,7 @@
[submission]="item"
[status]="selectedReviewOption()"
(selected)="navigateToRegistration(item)"
- (loadContributors)="loadContributors(item)"
+ (loadAdditionalData)="loadAdditionalData(item)"
(loadMoreContributors)="loadMoreContributors(item)"
>
diff --git a/src/app/features/moderation/components/registry-pending-submissions/registry-pending-submissions.component.ts b/src/app/features/moderation/components/registry-pending-submissions/registry-pending-submissions.component.ts
index ac0f00e76..26b08f29c 100644
--- a/src/app/features/moderation/components/registry-pending-submissions/registry-pending-submissions.component.ts
+++ b/src/app/features/moderation/components/registry-pending-submissions/registry-pending-submissions.component.ts
@@ -26,6 +26,7 @@ import { RegistrySort, SubmissionReviewStatus } from '../../enums';
import { RegistryModeration } from '../../models';
import {
GetRegistrySubmissionContributors,
+ GetRegistrySubmissionFunders,
GetRegistrySubmissions,
LoadMoreRegistrySubmissionContributors,
RegistryModerationSelectors,
@@ -63,6 +64,7 @@ export class RegistryPendingSubmissionsComponent implements OnInit {
getRegistrySubmissions: GetRegistrySubmissions,
getRegistrySubmissionContributors: GetRegistrySubmissionContributors,
loadMoreRegistrySubmissionContributors: LoadMoreRegistrySubmissionContributors,
+ getRegistrySubmissionFunders: GetRegistrySubmissionFunders,
});
readonly submissions = select(RegistryModerationSelectors.getRegistrySubmissions);
@@ -129,6 +131,11 @@ export class RegistryPendingSubmissionsComponent implements OnInit {
this.actions.loadMoreRegistrySubmissionContributors(item.id);
}
+ loadAdditionalData(item: RegistryModeration) {
+ this.actions.getRegistrySubmissionContributors(item.id);
+ this.actions.getRegistrySubmissionFunders(item.id);
+ }
+
private getStatusFromQueryParams() {
const queryParams = this.route.snapshot.queryParams;
const statusValues = Object.values(SubmissionReviewStatus);
diff --git a/src/app/features/moderation/components/registry-submission-item/registry-submission-item.component.html b/src/app/features/moderation/components/registry-submission-item/registry-submission-item.component.html
index 006ce75fc..8bc4d4a6e 100644
--- a/src/app/features/moderation/components/registry-submission-item/registry-submission-item.component.html
+++ b/src/app/features/moderation/components/registry-submission-item/registry-submission-item.component.html
@@ -64,12 +64,19 @@ {{ submission().title }}
+
+
+
diff --git a/src/app/features/moderation/components/registry-submission-item/registry-submission-item.component.ts b/src/app/features/moderation/components/registry-submission-item/registry-submission-item.component.ts
index 770db64d4..93b331b09 100644
--- a/src/app/features/moderation/components/registry-submission-item/registry-submission-item.component.ts
+++ b/src/app/features/moderation/components/registry-submission-item/registry-submission-item.component.ts
@@ -10,6 +10,7 @@ import { ContributorsListComponent } from '@osf/shared/components/contributors-l
import { IconComponent } from '@osf/shared/components/icon/icon.component';
import { TruncatedTextComponent } from '@osf/shared/components/truncated-text/truncated-text.component';
import { DateAgoPipe } from '@osf/shared/pipes/date-ago.pipe';
+import { FunderAwardsListComponent } from '@shared/funder-awards-list/funder-awards-list.component';
import { REGISTRY_ACTION_LABEL, ReviewStatusIcon } from '../../constants';
import { ActionStatus, SubmissionReviewStatus } from '../../enums';
@@ -29,6 +30,7 @@ import { RegistryModeration } from '../../models';
AccordionHeader,
AccordionContent,
ContributorsListComponent,
+ FunderAwardsListComponent,
],
templateUrl: './registry-submission-item.component.html',
styleUrl: './registry-submission-item.component.scss',
@@ -37,9 +39,8 @@ import { RegistryModeration } from '../../models';
export class RegistrySubmissionItemComponent {
status = input.required();
submission = input.required();
- loadContributors = output();
loadMoreContributors = output();
-
+ loadAdditionalData = output();
selected = output();
readonly reviewStatusIcon = ReviewStatusIcon;
@@ -67,6 +68,6 @@ export class RegistrySubmissionItemComponent {
});
handleOpen() {
- this.loadContributors.emit();
+ this.loadAdditionalData.emit();
}
}
diff --git a/src/app/features/moderation/components/registry-submissions/registry-submissions.component.html b/src/app/features/moderation/components/registry-submissions/registry-submissions.component.html
index 5066d15f7..73386a8f1 100644
--- a/src/app/features/moderation/components/registry-submissions/registry-submissions.component.html
+++ b/src/app/features/moderation/components/registry-submissions/registry-submissions.component.html
@@ -39,7 +39,7 @@
[submission]="item"
[status]="selectedReviewOption()"
(selected)="navigateToRegistration(item)"
- (loadContributors)="loadContributors(item)"
+ (loadAdditionalData)="loadAdditionalData(item)"
(loadMoreContributors)="loadMoreContributors(item)"
>
diff --git a/src/app/features/moderation/components/registry-submissions/registry-submissions.component.ts b/src/app/features/moderation/components/registry-submissions/registry-submissions.component.ts
index e664daacc..3271d565f 100644
--- a/src/app/features/moderation/components/registry-submissions/registry-submissions.component.ts
+++ b/src/app/features/moderation/components/registry-submissions/registry-submissions.component.ts
@@ -26,6 +26,7 @@ import { RegistrySort, SubmissionReviewStatus } from '../../enums';
import { RegistryModeration } from '../../models';
import {
GetRegistrySubmissionContributors,
+ GetRegistrySubmissionFunders,
GetRegistrySubmissions,
LoadMoreRegistrySubmissionContributors,
RegistryModerationSelectors,
@@ -63,6 +64,7 @@ export class RegistrySubmissionsComponent implements OnInit {
getRegistrySubmissions: GetRegistrySubmissions,
getRegistrySubmissionContributors: GetRegistrySubmissionContributors,
loadMoreRegistrySubmissionContributors: LoadMoreRegistrySubmissionContributors,
+ getRegistrySubmissionFunders: GetRegistrySubmissionFunders,
});
readonly submissions = select(RegistryModerationSelectors.getRegistrySubmissions);
@@ -129,6 +131,11 @@ export class RegistrySubmissionsComponent implements OnInit {
this.actions.loadMoreRegistrySubmissionContributors(item.id);
}
+ loadAdditionalData(item: RegistryModeration) {
+ this.actions.getRegistrySubmissionContributors(item.id);
+ this.actions.getRegistrySubmissionFunders(item.id);
+ }
+
private getStatusFromQueryParams() {
const queryParams = this.route.snapshot.queryParams;
const statusValues = Object.values(SubmissionReviewStatus);
diff --git a/src/app/features/moderation/models/registry-moderation.model.ts b/src/app/features/moderation/models/registry-moderation.model.ts
index 2d59b2681..31da4f051 100644
--- a/src/app/features/moderation/models/registry-moderation.model.ts
+++ b/src/app/features/moderation/models/registry-moderation.model.ts
@@ -1,3 +1,4 @@
+import { Funder } from '@osf/features/metadata/models';
import { RegistrationReviewStates } from '@osf/shared/enums/registration-review-states.enum';
import { RevisionReviewStates } from '@osf/shared/enums/revision-review-states.enum';
import { ContributorModel } from '@shared/models/contributors/contributor.model';
@@ -18,4 +19,6 @@ export interface RegistryModeration {
contributors?: ContributorModel[];
totalContributors?: number;
contributorsPage?: number;
+ funders?: Funder[];
+ fundersLoading?: boolean;
}
diff --git a/src/app/features/moderation/store/registry-moderation/registry-moderation.actions.ts b/src/app/features/moderation/store/registry-moderation/registry-moderation.actions.ts
index 3e350142c..eafd2f856 100644
--- a/src/app/features/moderation/store/registry-moderation/registry-moderation.actions.ts
+++ b/src/app/features/moderation/store/registry-moderation/registry-moderation.actions.ts
@@ -27,3 +27,9 @@ export class LoadMoreRegistrySubmissionContributors {
constructor(public registryId: string) {}
}
+
+export class GetRegistrySubmissionFunders {
+ static readonly type = `${ACTION_SCOPE} Get Registry Submission Funders`;
+
+ constructor(public registryId: string) {}
+}
diff --git a/src/app/features/moderation/store/registry-moderation/registry-moderation.state.ts b/src/app/features/moderation/store/registry-moderation/registry-moderation.state.ts
index 52b068e89..5c4715b76 100644
--- a/src/app/features/moderation/store/registry-moderation/registry-moderation.state.ts
+++ b/src/app/features/moderation/store/registry-moderation/registry-moderation.state.ts
@@ -7,6 +7,7 @@ import { inject, Injectable } from '@angular/core';
import { handleSectionError } from '@osf/shared/helpers/state-error.handler';
import { PaginatedData } from '@osf/shared/models/paginated-data.model';
+import { MetadataService } from '@osf/shared/services/metadata.service';
import { DEFAULT_TABLE_PARAMS } from '@shared/constants/default-table-params.constants';
import { ResourceType } from '@shared/enums/resource-type.enum';
import { ContributorsService } from '@shared/services/contributors.service';
@@ -16,6 +17,7 @@ import { RegistryModerationService } from '../../services';
import {
GetRegistrySubmissionContributors,
+ GetRegistrySubmissionFunders,
GetRegistrySubmissions,
LoadMoreRegistrySubmissionContributors,
} from './registry-moderation.actions';
@@ -29,7 +31,7 @@ import { REGISTRY_MODERATION_STATE_DEFAULTS, RegistryModerationStateModel } from
export class RegistryModerationState {
private readonly registryModerationService = inject(RegistryModerationService);
private readonly contributorsService = inject(ContributorsService);
-
+ private readonly metadataService = inject(MetadataService);
@Action(GetRegistrySubmissionContributors)
getRegistrySubmissionContributors(
ctx: StateContext,
@@ -151,4 +153,59 @@ export class RegistryModerationState {
catchError((error) => handleSectionError(ctx, 'submissions', error))
);
}
+
+ @Action(GetRegistrySubmissionFunders)
+ getRegistrySubmissionFunders(
+ ctx: StateContext,
+ { registryId }: GetRegistrySubmissionFunders
+ ) {
+ const state = ctx.getState();
+ const submission = state.submissions.data.find((s) => s.id === registryId);
+
+ if (submission?.funders && submission.funders.length > 0) {
+ return;
+ }
+
+ ctx.setState(
+ patch({
+ submissions: patch({
+ data: updateItem(
+ (submission) => submission.id === registryId,
+ patch({ fundersLoading: true })
+ ),
+ }),
+ })
+ );
+
+ return this.metadataService.getCustomItemMetadata(registryId).pipe(
+ tap((res) => {
+ ctx.setState(
+ patch({
+ submissions: patch({
+ data: updateItem(
+ (submission) => submission.id === registryId,
+ patch({
+ funders: res.funders,
+ fundersLoading: false,
+ })
+ ),
+ }),
+ })
+ );
+ }),
+ catchError((error) => {
+ ctx.setState(
+ patch({
+ submissions: patch({
+ data: updateItem(
+ (submission) => submission.id === registryId,
+ patch({ fundersLoading: false })
+ ),
+ }),
+ })
+ );
+ return handleSectionError(ctx, 'submissions', error);
+ })
+ );
+ }
}
diff --git a/src/app/shared/funder-awards-list/funder-awards-list.component.html b/src/app/shared/funder-awards-list/funder-awards-list.component.html
new file mode 100644
index 000000000..95fcdad0b
--- /dev/null
+++ b/src/app/shared/funder-awards-list/funder-awards-list.component.html
@@ -0,0 +1,24 @@
+
+ @if (isLoading()) {
+
+ } @else {
+ @if (funders().length) {
+
{{ 'resourceCard.labels.funderAwards' | translate }}
+
+ }
+ }
+
diff --git a/src/app/shared/funder-awards-list/funder-awards-list.component.scss b/src/app/shared/funder-awards-list/funder-awards-list.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/app/shared/funder-awards-list/funder-awards-list.component.spec.ts b/src/app/shared/funder-awards-list/funder-awards-list.component.spec.ts
new file mode 100644
index 000000000..d066f8152
--- /dev/null
+++ b/src/app/shared/funder-awards-list/funder-awards-list.component.spec.ts
@@ -0,0 +1,66 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { provideRouter } from '@angular/router';
+
+import { FunderAwardsListComponent } from './funder-awards-list.component';
+
+import { MOCK_FUNDERS } from '@testing/mocks/funder.mock';
+import { OSFTestingModule } from '@testing/osf.testing.module';
+
+describe('FunderAwardsListComponent', () => {
+ let component: FunderAwardsListComponent;
+ let fixture: ComponentFixture;
+
+ const MOCK_REGISTRY_ID = 'test-registry-123';
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [FunderAwardsListComponent, OSFTestingModule],
+ providers: [provideRouter([])],
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(FunderAwardsListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('should not render the list or label if funders array is empty', () => {
+ fixture.componentRef.setInput('funders', []);
+ fixture.detectChanges();
+ const label = fixture.debugElement.query(By.css('p'));
+ const links = fixture.debugElement.queryAll(By.css('a'));
+ expect(label).toBeNull();
+ expect(links.length).toBe(0);
+ });
+
+ it('should render a list of funders when data is provided', () => {
+ fixture.componentRef.setInput('funders', MOCK_FUNDERS);
+ fixture.componentRef.setInput('registryId', MOCK_REGISTRY_ID);
+ fixture.detectChanges();
+ const links = fixture.debugElement.queryAll(By.css('a'));
+ expect(links.length).toBe(2);
+ const firstItemText = links[0].nativeElement.textContent;
+ expect(firstItemText).toContain('National Science Foundation');
+ expect(firstItemText).toContain('NSF-1234567');
+ });
+
+ it('should generate the correct router link', () => {
+ fixture.componentRef.setInput('funders', MOCK_FUNDERS);
+ fixture.componentRef.setInput('registryId', MOCK_REGISTRY_ID);
+ fixture.detectChanges();
+ const linkDebugEl = fixture.debugElement.query(By.css('a'));
+ const href = linkDebugEl.nativeElement.getAttribute('href');
+ expect(href).toContain(`/${MOCK_REGISTRY_ID}/metadata/osf`);
+ });
+
+ it('should open links in a new tab', () => {
+ fixture.componentRef.setInput('funders', MOCK_FUNDERS);
+ fixture.detectChanges();
+ const linkDebugEl = fixture.debugElement.query(By.css('a'));
+ expect(linkDebugEl.attributes['target']).toBe('_blank');
+ });
+});
diff --git a/src/app/shared/funder-awards-list/funder-awards-list.component.ts b/src/app/shared/funder-awards-list/funder-awards-list.component.ts
new file mode 100644
index 000000000..969bf1053
--- /dev/null
+++ b/src/app/shared/funder-awards-list/funder-awards-list.component.ts
@@ -0,0 +1,21 @@
+import { TranslatePipe } from '@ngx-translate/core';
+
+import { Skeleton } from 'primeng/skeleton';
+
+import { ChangeDetectionStrategy, Component, input } from '@angular/core';
+import { RouterLink } from '@angular/router';
+
+import { Funder } from '@osf/features/metadata/models';
+
+@Component({
+ selector: 'osf-funder-awards-list',
+ imports: [RouterLink, TranslatePipe, Skeleton],
+ templateUrl: './funder-awards-list.component.html',
+ styleUrl: './funder-awards-list.component.scss',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class FunderAwardsListComponent {
+ funders = input([]);
+ registryId = input(null);
+ isLoading = input(false);
+}
diff --git a/src/app/features/metadata/services/metadata.service.ts b/src/app/shared/services/metadata.service.ts
similarity index 97%
rename from src/app/features/metadata/services/metadata.service.ts
rename to src/app/shared/services/metadata.service.ts
index 75ae0c86b..b74d82b64 100644
--- a/src/app/features/metadata/services/metadata.service.ts
+++ b/src/app/shared/services/metadata.service.ts
@@ -4,24 +4,25 @@ import { map } from 'rxjs/operators';
import { inject, Injectable } from '@angular/core';
import { ENVIRONMENT } from '@core/provider/environment.provider';
-import { ResourceType } from '@osf/shared/enums/resource-type.enum';
-import { IdentifierModel } from '@osf/shared/models/identifiers/identifier.model';
-import { LicenseOptions } from '@osf/shared/models/license/license.model';
-import { BaseNodeAttributesJsonApi } from '@osf/shared/models/nodes/base-node-attributes-json-api.model';
-import { JsonApiService } from '@osf/shared/services/json-api.service';
-
-import { CedarRecordsMapper, MetadataMapper } from '../mappers';
+import { CedarRecordsMapper, MetadataMapper } from '@osf/features/metadata/mappers';
import {
CedarMetadataRecord,
CedarMetadataRecordJsonApi,
CedarMetadataTemplateJsonApi,
CedarRecordDataBinding,
+ CrossRefFundersResponse,
+ CustomItemMetadataRecord,
CustomMetadataJsonApi,
CustomMetadataJsonApiResponse,
MetadataJsonApi,
MetadataJsonApiResponse,
-} from '../models';
-import { CrossRefFundersResponse, CustomItemMetadataRecord, MetadataModel } from '../models/metadata.model';
+ MetadataModel,
+} from '@osf/features/metadata/models';
+import { ResourceType } from '@osf/shared/enums/resource-type.enum';
+import { IdentifierModel } from '@osf/shared/models/identifiers/identifier.model';
+import { LicenseOptions } from '@osf/shared/models/license/license.model';
+import { BaseNodeAttributesJsonApi } from '@osf/shared/models/nodes/base-node-attributes-json-api.model';
+import { JsonApiService } from '@osf/shared/services/json-api.service';
@Injectable({
providedIn: 'root',
diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json
index 5a12c0109..a2fde1dab 100644
--- a/src/assets/i18n/en.json
+++ b/src/assets/i18n/en.json
@@ -2780,6 +2780,7 @@
"withdrawn": "Withdrawn",
"from": "From:",
"funder": "Funder:",
+ "funderAwards": "Funder awards:",
"resourceNature": "Resource type:",
"dateCreated": "Date created",
"dateModified": "Date modified",
From 90213a3d30c0c55e36356d3dc4347f877cf7d536 Mon Sep 17 00:00:00 2001
From: nsemets
Date: Fri, 30 Jan 2026 18:47:38 +0200
Subject: [PATCH 06/10] fix(ssr-routes): removed home route due to auth (#866)
## Purpose
The logged-in user was not redirected from the home page to the dashboard because the required authentication data was missing.
## Summary of Changes
1. Removed home route from `app.server.route.ts`.
---
src/app/app.routes.server.ts | 4 ----
1 file changed, 4 deletions(-)
diff --git a/src/app/app.routes.server.ts b/src/app/app.routes.server.ts
index 0b3e928f9..77c193b59 100644
--- a/src/app/app.routes.server.ts
+++ b/src/app/app.routes.server.ts
@@ -21,10 +21,6 @@ export const serverRoutes: ServerRoute[] = [
path: 'forgotpassword',
renderMode: RenderMode.Prerender,
},
- {
- path: '',
- renderMode: RenderMode.Prerender,
- },
{
path: 'dashboard',
renderMode: RenderMode.Client,
From a2a9f3525daddd05aca50a592edd0ca7512e5176 Mon Sep 17 00:00:00 2001
From: nsemets
Date: Mon, 2 Feb 2026 16:45:25 +0200
Subject: [PATCH 07/10] [ENG-10148] Fix frontend state-management bug causing
stale facet results #870
- Ticket: [ENG-10148]
- Feature flag: n/a
## Summary of Changes
1. Added loading message.
---
.../components/generic-filter/generic-filter.component.html | 1 +
.../components/generic-filter/generic-filter.component.ts | 6 +++++-
src/assets/i18n/en.json | 1 +
3 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/src/app/shared/components/generic-filter/generic-filter.component.html b/src/app/shared/components/generic-filter/generic-filter.component.html
index 25b7844ad..71a47aaa9 100644
--- a/src/app/shared/components/generic-filter/generic-filter.component.html
+++ b/src/app/shared/components/generic-filter/generic-filter.component.html
@@ -20,6 +20,7 @@
[virtualScrollItemSize]="40"
scrollHeight="200px"
[autoOptionFocus]="false"
+ [emptyFilterMessage]="filterMessage() | translate"
[loading]="isPaginationLoading() || isSearchLoading()"
(onFilter)="onFilterChange($event)"
(onChange)="onMultiChange($event)"
diff --git a/src/app/shared/components/generic-filter/generic-filter.component.ts b/src/app/shared/components/generic-filter/generic-filter.component.ts
index 1a5391a0c..041160d70 100644
--- a/src/app/shared/components/generic-filter/generic-filter.component.ts
+++ b/src/app/shared/components/generic-filter/generic-filter.component.ts
@@ -1,3 +1,5 @@
+import { TranslatePipe } from '@ngx-translate/core';
+
import { MultiSelect, MultiSelectChangeEvent } from 'primeng/multiselect';
import { SelectLazyLoadEvent } from 'primeng/select';
@@ -23,7 +25,7 @@ import { LoadingSpinnerComponent } from '../loading-spinner/loading-spinner.comp
@Component({
selector: 'osf-generic-filter',
- imports: [MultiSelect, FormsModule, LoadingSpinnerComponent],
+ imports: [MultiSelect, FormsModule, LoadingSpinnerComponent, TranslatePipe],
templateUrl: './generic-filter.component.html',
styleUrls: ['./generic-filter.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
@@ -73,6 +75,8 @@ export class GenericFilterComponent {
selectedOptionValues = computed(() => this.selectedOptions().map((option) => option.value));
+ filterMessage = computed(() => (this.isSearchLoading() ? 'common.search.loading' : 'common.search.noResultsFound'));
+
constructor() {
effect(() => {
const searchResults = this.searchResults();
diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json
index a2fde1dab..01ef03921 100644
--- a/src/assets/i18n/en.json
+++ b/src/assets/i18n/en.json
@@ -84,6 +84,7 @@
"search": {
"title": "Search",
"noResultsFound": "No results found.",
+ "loading": "Loading results",
"tabs": {
"all": "All",
"preprints": "Preprints",
From 91f8e0dccca35aa4ebcd941ff8ab50a85a7a1741 Mon Sep 17 00:00:00 2001
From: nsemets
Date: Fri, 6 Feb 2026 18:55:12 +0200
Subject: [PATCH 08/10] [ENG-10251] Standardize model file naming convention
(#874)
- Ticket: [ENG-10251]
- Feature flag: n/a
## Summary of Changes
1. Renamed all instances of `.models.ts` to `.model.ts`.
2. Updated all internal references and import paths in components, services, and pipes to reflect the new file names.
3. Updated Jest unit tests to ensure imports within `.spec.ts` files are pointing to the corrected model paths.
4. Verified that no duplicate model definitions exist under the old naming scheme.
5. Remove animations.
---
.../core/animations/fade.in-out.animation.ts | 39 -------------------
.../cookie-consent-banner.component.spec.ts | 2 +-
.../cookie-consent-banner.component.ts | 3 --
.../maintenance-banner.component.html | 5 +--
.../maintenance-banner.component.spec.ts | 7 ++--
.../maintenance-banner.component.ts | 5 ---
src/app/core/services/user.service.ts | 2 +-
src/app/core/store/user/user.actions.ts | 2 +-
src/app/core/store/user/user.model.ts | 2 +-
src/app/core/store/user/user.selectors.ts | 2 +-
src/app/core/store/user/user.state.ts | 2 +-
.../institutions-summary.component.ts | 2 +-
.../store/institutions-admin.model.ts | 2 +-
.../store/institutions-admin.selectors.ts | 2 +-
.../store/institutions-admin.state.ts | 2 +-
.../features/analytics/analytics.component.ts | 2 +-
.../collection-metadata-step.component.ts | 2 +-
.../project-metadata-step.component.ts | 2 +-
.../select-project-step.component.ts | 2 +-
...tions-search-result-card.component.spec.ts | 2 +-
...ollections-search-result-card.component.ts | 2 +-
...s => collection-license-json-api.model.ts} | 0
.../services/project-metadata-form.service.ts | 2 +-
.../add-to-collection.model.ts | 2 +-
src/app/features/contributors/models/index.ts | 2 +-
...odels.ts => view-only-components.model.ts} | 0
.../pages/dashboard/dashboard.component.ts | 4 +-
src/app/features/meetings/models/index.ts | 2 +-
.../{meetings.models.ts => meetings.model.ts} | 0
...adata-affiliated-institutions.component.ts | 2 +-
...metadata-collection-item.component.spec.ts | 2 +-
.../metadata-collection-item.component.ts | 2 +-
.../metadata-collections.component.ts | 2 +-
...ated-institutions-dialog.component.spec.ts | 2 +-
...ffiliated-institutions-dialog.component.ts | 2 +-
.../metadata/models/metadata.model.ts | 2 +-
...llection-submission-item.component.spec.ts | 2 +-
.../collection-submission-item.component.ts | 2 +-
.../collection-submissions-list.component.ts | 2 +-
...ubmission-review-action-json-api.model.ts} | 0
src/app/features/moderation/models/index.ts | 2 +-
.../moderation/services/moderators.service.ts | 2 +-
.../collections-moderation.model.ts | 2 +-
.../my-projects/my-projects.component.ts | 4 +-
...-affiliated-institutions.component.spec.ts | 2 +-
...rints-affiliated-institutions.component.ts | 2 +-
src/app/features/preprints/models/index.ts | 20 +++++-----
...i.models.ts => preprint-json-api.model.ts} | 0
...ts => preprint-licenses-json-api.model.ts} | 0
...ts => preprint-provider-json-api.model.ts} | 2 +-
...r.models.ts => preprint-provider.model.ts} | 0
...preprint-request-action-json-api.model.ts} | 0
...ls.ts => preprint-request-action.model.ts} | 0
....ts => preprint-request-json-api.model.ts} | 0
...st.models.ts => preprint-request.model.ts} | 0
.../{preprint.models.ts => preprint.model.ts} | 0
...odels.ts => submit-preprint-form.model.ts} | 0
.../preprints/services/preprints.service.ts | 2 +-
.../profile-information.component.spec.ts | 4 +-
.../profile-information.component.ts | 4 +-
src/app/features/profile/profile.component.ts | 2 +-
.../features/profile/store/profile.actions.ts | 2 +-
.../features/profile/store/profile.model.ts | 2 +-
.../profile/store/profile.selectors.ts | 2 +-
.../add-component-dialog.component.ts | 2 +-
.../files-widget/files-widget.component.ts | 2 +-
.../link-resource-dialog.component.spec.ts | 2 +-
.../link-resource-dialog.component.ts | 4 +-
.../overview-collections.component.ts | 2 +-
.../features/project/overview/models/index.ts | 2 +-
...ew.models.ts => project-overview.model.ts} | 0
.../services/project-overview.service.ts | 2 +-
.../overview/store/project-overview.model.ts | 2 +-
.../connect-configured-addon.component.ts | 4 +-
...ings-project-affiliation.component.spec.ts | 2 +-
.../settings-project-affiliation.component.ts | 2 +-
.../settings/models/node-details.model.ts | 2 +-
.../project/settings/settings.component.ts | 2 +-
...stries-affiliated-institution.component.ts | 2 +-
.../services/registry-overview.service.ts | 2 +-
.../registry/store/registry/registry.model.ts | 2 +-
.../store/registry/registry.selectors.ts | 2 +-
.../affiliated-institutions.component.ts | 2 +-
.../services/account-settings.service.ts | 2 +-
.../store/account-settings.model.ts | 2 +-
.../store/account-settings.selectors.ts | 2 +-
.../citation-preview.component.ts | 2 +-
.../components/name/name.component.ts | 2 +-
.../helpers/name-comparison.helper.ts | 2 +-
.../connect-addon/connect-addon.component.ts | 4 +-
.../add-project-form.component.ts | 4 +-
.../addon-setup-account-form.component.ts | 4 +-
.../addon-terms/addon-terms.component.spec.ts | 2 +-
.../addon-terms/addon-terms.component.ts | 2 +-
.../storage-item-selector.component.ts | 2 +-
...iated-institution-select.component.spec.ts | 2 +-
...affiliated-institution-select.component.ts | 2 +-
...liated-institutions-view.component.spec.ts | 2 +-
.../affiliated-institutions-view.component.ts | 2 +-
.../bar-chart/bar-chart.component.ts | 2 +-
.../doughnut-chart.component.ts | 2 +-
.../components/license/license.component.ts | 2 +-
.../line-chart/line-chart.component.spec.ts | 2 +-
.../line-chart/line-chart.component.ts | 2 +-
.../my-projects-table.component.spec.ts | 2 +-
.../my-projects-table.component.ts | 2 +-
.../pie-chart/pie-chart.component.spec.ts | 2 +-
.../pie-chart/pie-chart.component.ts | 2 +-
.../project-selector.component.ts | 2 +-
src/app/shared/constants/addon-terms.const.ts | 2 +-
.../helpers/search-total-count.helper.ts | 2 +-
src/app/shared/mappers/addon.mapper.ts | 4 +-
.../mappers/collections/collections.mapper.ts | 4 +-
.../mappers/filters/filter-option.mapper.ts | 4 +-
.../shared/mappers/filters/filters.mapper.ts | 2 +-
.../institutions/institutions.mapper.ts | 2 +-
src/app/shared/mappers/my-resources.mapper.ts | 2 +-
.../mappers/projects/projects.mapper.ts | 4 +-
.../shared/mappers/search/search.mapper.ts | 6 +--
src/app/shared/mappers/user/user.mapper.ts | 2 +-
...-api.models.ts => addon-json-api.model.ts} | 0
....ts => addon-operations-json-api.model.ts} | 0
...n-utils.models.ts => addon-utils.model.ts} | 0
...ataset-input.ts => dataset-input.model.ts} | 0
...odels.ts => collections-json-api.model.ts} | 0
...ections.models.ts => collections.model.ts} | 2 +-
.../institution-json-api.model.ts | 2 +-
...utions.models.ts => institutions.model.ts} | 0
...e-form.models.ts => license-form.model.ts} | 0
...s => my-resources-search-filters.model.ts} | 0
...ources.models.ts => my-resources.model.ts} | 0
.../models/profile-settings-update.model.ts | 2 +-
.../{projects.models.ts => projects.model.ts} | 0
.../registration/draft-registration.model.ts | 2 +-
.../request-access/request-access.model.ts | 2 +-
...ls.ts => filter-options-json-api.model.ts} | 2 +-
...ts => index-card-search-json-api.model.ts} | 0
.../user/{user.models.ts => user.model.ts} | 0
src/app/shared/pipes/citation-format.pipe.ts | 2 +-
.../services/addons/addon-form.service.ts | 6 +--
.../services/addons/addon-oauth.service.ts | 2 +-
.../addon-operation-invocation.service.ts | 2 +-
.../shared/services/addons/addons.service.ts | 4 +-
src/app/shared/services/bookmarks.service.ts | 6 +--
.../shared/services/collections.service.ts | 4 +-
.../shared/services/contributors.service.ts | 2 +-
src/app/shared/services/files.service.ts | 2 +-
.../shared/services/global-search.service.ts | 4 +-
.../shared/services/institutions.service.ts | 2 +-
.../shared/services/my-resources.service.ts | 4 +-
src/app/shared/services/node-links.service.ts | 2 +-
src/app/shared/services/projects.service.ts | 2 +-
.../shared/stores/addons/addons.actions.ts | 4 +-
src/app/shared/stores/addons/addons.models.ts | 2 +-
.../shared/stores/addons/addons.selectors.ts | 2 +-
.../stores/bookmarks/bookmarks.actions.ts | 2 +-
.../stores/bookmarks/bookmarks.model.ts | 2 +-
.../stores/collections/collections.model.ts | 2 +-
.../institutions-search.model.ts | 2 +-
.../institutions-search.state.ts | 2 +-
.../institutions/institutions.actions.ts | 2 +-
.../stores/institutions/institutions.model.ts | 2 +-
.../my-resources/my-resources.actions.ts | 2 +-
.../stores/my-resources/my-resources.model.ts | 2 +-
.../my-resources/my-resources.selectors.ts | 2 +-
.../stores/node-links/node-links.actions.ts | 2 +-
.../stores/projects/projects.actions.ts | 2 +-
.../shared/stores/projects/projects.model.ts | 2 +-
.../collection-submissions.mock.ts | 2 +-
src/testing/data/dashboard/dasboard.data.ts | 2 +-
.../mocks/collections-submissions.mock.ts | 2 +-
src/testing/mocks/data.mock.ts | 2 +-
src/testing/mocks/my-resources.mock.ts | 2 +-
src/testing/mocks/project-metadata.mock.ts | 2 +-
src/testing/mocks/project.mock.ts | 2 +-
src/testing/mocks/submission.mock.ts | 2 +-
...addon-operation-invocation.service.mock.ts | 2 +-
177 files changed, 184 insertions(+), 233 deletions(-)
delete mode 100644 src/app/core/animations/fade.in-out.animation.ts
rename src/app/features/collections/models/{collection-license-json-api.models.ts => collection-license-json-api.model.ts} (100%)
rename src/app/features/contributors/models/{view-only-components.models.ts => view-only-components.model.ts} (100%)
rename src/app/features/meetings/models/{meetings.models.ts => meetings.model.ts} (100%)
rename src/app/features/moderation/models/{collection-submission-review-action-json.api.ts => collection-submission-review-action-json-api.model.ts} (100%)
rename src/app/features/preprints/models/{preprint-json-api.models.ts => preprint-json-api.model.ts} (100%)
rename src/app/features/preprints/models/{preprint-licenses-json-api.models.ts => preprint-licenses-json-api.model.ts} (100%)
rename src/app/features/preprints/models/{preprint-provider-json-api.models.ts => preprint-provider-json-api.model.ts} (94%)
rename src/app/features/preprints/models/{preprint-provider.models.ts => preprint-provider.model.ts} (100%)
rename src/app/features/preprints/models/{preprint-request-action-json-api.models.ts => preprint-request-action-json-api.model.ts} (100%)
rename src/app/features/preprints/models/{preprint-request-action.models.ts => preprint-request-action.model.ts} (100%)
rename src/app/features/preprints/models/{preprint-request-json-api.models.ts => preprint-request-json-api.model.ts} (100%)
rename src/app/features/preprints/models/{preprint-request.models.ts => preprint-request.model.ts} (100%)
rename src/app/features/preprints/models/{preprint.models.ts => preprint.model.ts} (100%)
rename src/app/features/preprints/models/{submit-preprint-form.models.ts => submit-preprint-form.model.ts} (100%)
rename src/app/features/project/overview/models/{project-overview.models.ts => project-overview.model.ts} (100%)
rename src/app/shared/models/addons/{addon-json-api.models.ts => addon-json-api.model.ts} (100%)
rename src/app/shared/models/addons/{addon-operations-json-api.models.ts => addon-operations-json-api.model.ts} (100%)
rename src/app/shared/models/addons/{addon-utils.models.ts => addon-utils.model.ts} (100%)
rename src/app/shared/models/charts/{dataset-input.ts => dataset-input.model.ts} (100%)
rename src/app/shared/models/collections/{collections-json-api.models.ts => collections-json-api.model.ts} (100%)
rename src/app/shared/models/collections/{collections.models.ts => collections.model.ts} (97%)
rename src/app/shared/models/institutions/{institutions.models.ts => institutions.model.ts} (100%)
rename src/app/shared/models/license/{license-form.models.ts => license-form.model.ts} (100%)
rename src/app/shared/models/my-resources/{my-resources-search-filters.models.ts => my-resources-search-filters.model.ts} (100%)
rename src/app/shared/models/my-resources/{my-resources.models.ts => my-resources.model.ts} (100%)
rename src/app/shared/models/projects/{projects.models.ts => projects.model.ts} (100%)
rename src/app/shared/models/search/{filter-options-json-api.models.ts => filter-options-json-api.model.ts} (98%)
rename src/app/shared/models/search/{index-card-search-json-api.models.ts => index-card-search-json-api.model.ts} (100%)
rename src/app/shared/models/user/{user.models.ts => user.model.ts} (100%)
diff --git a/src/app/core/animations/fade.in-out.animation.ts b/src/app/core/animations/fade.in-out.animation.ts
deleted file mode 100644
index 7befb072b..000000000
--- a/src/app/core/animations/fade.in-out.animation.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import { animate, style, transition, trigger } from '@angular/animations';
-
-/**
- * Angular animation trigger for fading elements in and out.
- *
- * This trigger can be used with Angular structural directives like `*ngIf` or `@if`
- * to smoothly animate the appearance and disappearance of components or elements.
- *
- * ## Usage:
- *
- * In the component decorator:
- * ```ts
- * @Component({
- * selector: 'my-component',
- * templateUrl: './my.component.html',
- * animations: [fadeInOut]
- * })
- * export class MyComponent {}
- * ```
- *
- * In the template:
- * ```html
- * @if (show) {
- *
- * Fades in and out!
- *
- * }
- * ```
- *
- * ## Transitions:
- * - **:enter** — Fades in from opacity `0` to `1` over `200ms`.
- * - **:leave** — Fades out from opacity `1` to `0` over `200ms`.
- *
- * @returns An Angular `AnimationTriggerMetadata` object used for component animations.
- */
-export const fadeInOutAnimation = trigger('fadeInOut', [
- transition(':enter', [style({ opacity: 0 }), animate('200ms', style({ opacity: 1 }))]),
- transition(':leave', [animate('200ms', style({ opacity: 0 }))]),
-]);
diff --git a/src/app/core/components/osf-banners/cookie-consent-banner/cookie-consent-banner.component.spec.ts b/src/app/core/components/osf-banners/cookie-consent-banner/cookie-consent-banner.component.spec.ts
index c40290217..e6eefb1a9 100644
--- a/src/app/core/components/osf-banners/cookie-consent-banner/cookie-consent-banner.component.spec.ts
+++ b/src/app/core/components/osf-banners/cookie-consent-banner/cookie-consent-banner.component.spec.ts
@@ -20,7 +20,7 @@ describe('Component: Cookie Consent Banner', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
- imports: [OSFTestingModule, CookieConsentBannerComponent, MockComponent(IconComponent)],
+ imports: [CookieConsentBannerComponent, OSFTestingModule, MockComponent(IconComponent)],
providers: [{ provide: CookieService, useValue: cookieServiceMock }],
});
diff --git a/src/app/core/components/osf-banners/cookie-consent-banner/cookie-consent-banner.component.ts b/src/app/core/components/osf-banners/cookie-consent-banner/cookie-consent-banner.component.ts
index 30f039af9..c593da853 100644
--- a/src/app/core/components/osf-banners/cookie-consent-banner/cookie-consent-banner.component.ts
+++ b/src/app/core/components/osf-banners/cookie-consent-banner/cookie-consent-banner.component.ts
@@ -7,7 +7,6 @@ import { Message } from 'primeng/message';
import { isPlatformBrowser } from '@angular/common';
import { ChangeDetectionStrategy, Component, inject, PLATFORM_ID, signal } from '@angular/core';
-import { fadeInOutAnimation } from '@core/animations/fade.in-out.animation';
import { IconComponent } from '@osf/shared/components/icon/icon.component';
/**
@@ -15,7 +14,6 @@ import { IconComponent } from '@osf/shared/components/icon/icon.component';
*
* - Uses `ngx-cookie-service` to persist acceptance across sessions.
* - Automatically hides the banner if consent is already recorded.
- * - Animates in/out using the `fadeInOutAnimation`.
* - Supports translation via `TranslatePipe`.
*/
@Component({
@@ -23,7 +21,6 @@ import { IconComponent } from '@osf/shared/components/icon/icon.component';
templateUrl: './cookie-consent-banner.component.html',
styleUrls: ['./cookie-consent-banner.component.scss'],
imports: [Button, TranslatePipe, IconComponent, Message],
- animations: [fadeInOutAnimation],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CookieConsentBannerComponent {
diff --git a/src/app/core/components/osf-banners/maintenance-banner/maintenance-banner.component.html b/src/app/core/components/osf-banners/maintenance-banner/maintenance-banner.component.html
index a936ebefc..9dd9ed582 100644
--- a/src/app/core/components/osf-banners/maintenance-banner/maintenance-banner.component.html
+++ b/src/app/core/components/osf-banners/maintenance-banner/maintenance-banner.component.html
@@ -1,13 +1,12 @@
@if (maintenance() && !dismissed()) {
}
diff --git a/src/app/core/components/osf-banners/maintenance-banner/maintenance-banner.component.spec.ts b/src/app/core/components/osf-banners/maintenance-banner/maintenance-banner.component.spec.ts
index e617a1333..80d8d59b1 100644
--- a/src/app/core/components/osf-banners/maintenance-banner/maintenance-banner.component.spec.ts
+++ b/src/app/core/components/osf-banners/maintenance-banner/maintenance-banner.component.spec.ts
@@ -1,16 +1,15 @@
import { CookieService } from 'ngx-cookie-service';
-import { MessageModule } from 'primeng/message';
-
import { of } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
-import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { MaintenanceBannerComponent } from './maintenance-banner.component';
+import { OSFTestingModule } from '@testing/osf.testing.module';
+
describe('Component: Maintenance Banner', () => {
let fixture: ComponentFixture;
let httpClient: { get: jest.Mock };
@@ -25,7 +24,7 @@ describe('Component: Maintenance Banner', () => {
httpClient = { get: jest.fn() } as any;
await TestBed.configureTestingModule({
- imports: [MaintenanceBannerComponent, NoopAnimationsModule, MessageModule],
+ imports: [MaintenanceBannerComponent, OSFTestingModule],
providers: [
{ provide: CookieService, useValue: cookieService },
{ provide: HttpClient, useValue: httpClient },
diff --git a/src/app/core/components/osf-banners/maintenance-banner/maintenance-banner.component.ts b/src/app/core/components/osf-banners/maintenance-banner/maintenance-banner.component.ts
index 05b269412..71a328e52 100644
--- a/src/app/core/components/osf-banners/maintenance-banner/maintenance-banner.component.ts
+++ b/src/app/core/components/osf-banners/maintenance-banner/maintenance-banner.component.ts
@@ -5,8 +5,6 @@ import { MessageModule } from 'primeng/message';
import { CommonModule, isPlatformBrowser } from '@angular/common';
import { ChangeDetectionStrategy, Component, inject, OnInit, PLATFORM_ID, signal } from '@angular/core';
-import { fadeInOutAnimation } from '@core/animations/fade.in-out.animation';
-
import { MaintenanceModel } from '../models/maintenance.model';
import { MaintenanceService } from '../services/maintenance.service';
@@ -17,8 +15,6 @@ import { MaintenanceService } from '../services/maintenance.service';
* the banner. If not, it queries the maintenance status from the server and displays
* the maintenance message if one is active.
*
- * The component supports animation via `fadeInOutAnimation` and is optimized with `OnPush` change detection.
- *
* @example
* ```html
*
@@ -29,7 +25,6 @@ import { MaintenanceService } from '../services/maintenance.service';
imports: [CommonModule, MessageModule],
templateUrl: './maintenance-banner.component.html',
styleUrls: ['./maintenance-banner.component.scss'],
- animations: [fadeInOutAnimation],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MaintenanceBannerComponent implements OnInit {
diff --git a/src/app/core/services/user.service.ts b/src/app/core/services/user.service.ts
index 8de41701d..3506c67cc 100644
--- a/src/app/core/services/user.service.ts
+++ b/src/app/core/services/user.service.ts
@@ -5,9 +5,9 @@ import { inject, Injectable } from '@angular/core';
import { ENVIRONMENT } from '@core/provider/environment.provider';
import { ProfileSettingsKey } from '@osf/shared/enums/profile-settings-key.enum';
import { UserMapper } from '@osf/shared/mappers/user';
+import { UserData, UserModel } from '@osf/shared/models/user/user.model';
import { JsonApiService } from '@osf/shared/services/json-api.service';
import { ProfileSettingsUpdate } from '@shared/models/profile-settings-update.model';
-import { UserData, UserModel } from '@shared/models/user/user.models';
import {
UserAcceptedTermsOfServiceJsonApi,
UserDataJsonApi,
diff --git a/src/app/core/store/user/user.actions.ts b/src/app/core/store/user/user.actions.ts
index c645288df..9219d8847 100644
--- a/src/app/core/store/user/user.actions.ts
+++ b/src/app/core/store/user/user.actions.ts
@@ -1,7 +1,7 @@
import { Education } from '@osf/shared/models/user/education.model';
import { Employment } from '@osf/shared/models/user/employment.model';
import { SocialModel } from '@osf/shared/models/user/social.model';
-import { UserModel } from '@osf/shared/models/user/user.models';
+import { UserModel } from '@osf/shared/models/user/user.model';
export class GetCurrentUser {
static readonly type = '[User] Get Current User';
diff --git a/src/app/core/store/user/user.model.ts b/src/app/core/store/user/user.model.ts
index 35a18a34b..e006d52c2 100644
--- a/src/app/core/store/user/user.model.ts
+++ b/src/app/core/store/user/user.model.ts
@@ -1,5 +1,5 @@
import { AsyncStateModel } from '@osf/shared/models/store/async-state.model';
-import { UserModel } from '@osf/shared/models/user/user.models';
+import { UserModel } from '@osf/shared/models/user/user.model';
export interface UserStateModel {
currentUser: AsyncStateModel;
diff --git a/src/app/core/store/user/user.selectors.ts b/src/app/core/store/user/user.selectors.ts
index 7b42ca0ad..311d3eec1 100644
--- a/src/app/core/store/user/user.selectors.ts
+++ b/src/app/core/store/user/user.selectors.ts
@@ -3,7 +3,7 @@ import { Selector } from '@ngxs/store';
import { Education } from '@osf/shared/models/user/education.model';
import { Employment } from '@osf/shared/models/user/employment.model';
import { SocialModel } from '@osf/shared/models/user/social.model';
-import { UserModel } from '@osf/shared/models/user/user.models';
+import { UserModel } from '@osf/shared/models/user/user.model';
import { UserStateModel } from './user.model';
import { UserState } from './user.state';
diff --git a/src/app/core/store/user/user.state.ts b/src/app/core/store/user/user.state.ts
index a5bdb2e88..c3b65d803 100644
--- a/src/app/core/store/user/user.state.ts
+++ b/src/app/core/store/user/user.state.ts
@@ -9,8 +9,8 @@ import { UserService } from '@core/services/user.service';
import { ProfileSettingsKey } from '@osf/shared/enums/profile-settings-key.enum';
import { removeNullable } from '@osf/shared/helpers/remove-nullable.helper';
import { UserMapper } from '@osf/shared/mappers/user';
+import { UserModel } from '@osf/shared/models/user/user.model';
import { SocialModel } from '@shared/models/user/social.model';
-import { UserModel } from '@shared/models/user/user.models';
import {
AcceptTermsOfServiceByUser,
diff --git a/src/app/features/admin-institutions/pages/institutions-summary/institutions-summary.component.ts b/src/app/features/admin-institutions/pages/institutions-summary/institutions-summary.component.ts
index 500b29f19..03be73487 100644
--- a/src/app/features/admin-institutions/pages/institutions-summary/institutions-summary.component.ts
+++ b/src/app/features/admin-institutions/pages/institutions-summary/institutions-summary.component.ts
@@ -9,7 +9,7 @@ import { ActivatedRoute } from '@angular/router';
import { BarChartComponent } from '@osf/shared/components/bar-chart/bar-chart.component';
import { LoadingSpinnerComponent } from '@osf/shared/components/loading-spinner/loading-spinner.component';
import { StatisticCardComponent } from '@osf/shared/components/statistic-card/statistic-card.component';
-import { DatasetInput } from '@osf/shared/models/charts/dataset-input';
+import { DatasetInput } from '@osf/shared/models/charts/dataset-input.model';
import { SelectOption } from '@osf/shared/models/select-option.model';
import { DoughnutChartComponent } from '@shared/components/doughnut-chart/doughnut-chart.component';
diff --git a/src/app/features/admin-institutions/store/institutions-admin.model.ts b/src/app/features/admin-institutions/store/institutions-admin.model.ts
index d62a6d0eb..239078b99 100644
--- a/src/app/features/admin-institutions/store/institutions-admin.model.ts
+++ b/src/app/features/admin-institutions/store/institutions-admin.model.ts
@@ -1,4 +1,4 @@
-import { Institution } from '@osf/shared/models/institutions/institutions.models';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
import { AsyncStateModel } from '@osf/shared/models/store/async-state.model';
import { AsyncStateWithTotalCount } from '@osf/shared/models/store/async-state-with-total-count.model';
diff --git a/src/app/features/admin-institutions/store/institutions-admin.selectors.ts b/src/app/features/admin-institutions/store/institutions-admin.selectors.ts
index 4211deb98..721901e4d 100644
--- a/src/app/features/admin-institutions/store/institutions-admin.selectors.ts
+++ b/src/app/features/admin-institutions/store/institutions-admin.selectors.ts
@@ -1,6 +1,6 @@
import { Selector } from '@ngxs/store';
-import { Institution } from '@osf/shared/models/institutions/institutions.models';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
import { InstitutionDepartment, InstitutionSearchFilter, InstitutionSummaryMetrics, InstitutionUser } from '../models';
diff --git a/src/app/features/admin-institutions/store/institutions-admin.state.ts b/src/app/features/admin-institutions/store/institutions-admin.state.ts
index 6e2d720cb..8285669a1 100644
--- a/src/app/features/admin-institutions/store/institutions-admin.state.ts
+++ b/src/app/features/admin-institutions/store/institutions-admin.state.ts
@@ -6,7 +6,7 @@ import { catchError, tap, throwError } from 'rxjs';
import { inject, Injectable } from '@angular/core';
import { handleSectionError } from '@osf/shared/helpers/state-error.handler';
-import { Institution } from '@osf/shared/models/institutions/institutions.models';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
import { InstitutionsService } from '@osf/shared/services/institutions.service';
import { InstitutionsAdminService } from '../services/institutions-admin.service';
diff --git a/src/app/features/analytics/analytics.component.ts b/src/app/features/analytics/analytics.component.ts
index a24a9d66d..5eba9475b 100644
--- a/src/app/features/analytics/analytics.component.ts
+++ b/src/app/features/analytics/analytics.component.ts
@@ -30,7 +30,7 @@ import { ViewOnlyLinkMessageComponent } from '@osf/shared/components/view-only-l
import { IS_WEB } from '@osf/shared/helpers/breakpoints.tokens';
import { replaceBadEncodedChars } from '@osf/shared/helpers/format-bad-encoding.helper';
import { Primitive } from '@osf/shared/helpers/types.helper';
-import { DatasetInput } from '@osf/shared/models/charts/dataset-input';
+import { DatasetInput } from '@osf/shared/models/charts/dataset-input.model';
import { ViewOnlyLinkHelperService } from '@osf/shared/services/view-only-link-helper.service';
import { AnalyticsKpiComponent } from './components';
diff --git a/src/app/features/collections/components/add-to-collection/collection-metadata-step/collection-metadata-step.component.ts b/src/app/features/collections/components/add-to-collection/collection-metadata-step/collection-metadata-step.component.ts
index 8b17c2989..acb6a1d0b 100644
--- a/src/app/features/collections/components/add-to-collection/collection-metadata-step/collection-metadata-step.component.ts
+++ b/src/app/features/collections/components/add-to-collection/collection-metadata-step/collection-metadata-step.component.ts
@@ -14,7 +14,7 @@ import { collectionFilterTypes } from '@osf/features/collections/constants';
import { AddToCollectionSteps, CollectionFilterType } from '@osf/features/collections/enums';
import { CollectionFilterEntry } from '@osf/features/collections/models/collection-filter-entry.model';
import { AddToCollectionSelectors } from '@osf/features/collections/store/add-to-collection';
-import { CollectionSubmissionWithGuid } from '@osf/shared/models/collections/collections.models';
+import { CollectionSubmissionWithGuid } from '@osf/shared/models/collections/collections.model';
import { CollectionsSelectors, GetCollectionDetails } from '@osf/shared/stores/collections';
@Component({
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 95913df5c..2fd2b0b4c 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,7 @@ import { TruncatedTextComponent } from '@osf/shared/components/truncated-text/tr
import { InputLimits } from '@osf/shared/constants/input-limits.const';
import { ResourceType } from '@osf/shared/enums/resource-type.enum';
import { LicenseModel } from '@osf/shared/models/license/license.model';
-import { ProjectModel } from '@osf/shared/models/projects/projects.models';
+import { ProjectModel } from '@osf/shared/models/projects/projects.model';
import { InterpolatePipe } from '@osf/shared/pipes/interpolate.pipe';
import { ToastService } from '@osf/shared/services/toast.service';
import { GetAllContributors } from '@osf/shared/stores/contributors';
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 7c610f347..7658ab614 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
@@ -9,8 +9,8 @@ import { ChangeDetectionStrategy, Component, computed, input, output, signal } f
import { AddToCollectionSteps } from '@osf/features/collections/enums';
import { ProjectSelectorComponent } from '@osf/shared/components/project-selector/project-selector.component';
+import { ProjectModel } from '@osf/shared/models/projects/projects.model';
import { SetSelectedProject } from '@osf/shared/stores/projects';
-import { ProjectModel } from '@shared/models/projects/projects.models';
import { CollectionsSelectors, GetUserCollectionSubmissions } from '@shared/stores/collections';
import { ProjectsSelectors } from '@shared/stores/projects/projects.selectors';
diff --git a/src/app/features/collections/components/collections-search-result-card/collections-search-result-card.component.spec.ts b/src/app/features/collections/components/collections-search-result-card/collections-search-result-card.component.spec.ts
index f4738728c..96895956c 100644
--- a/src/app/features/collections/components/collections-search-result-card/collections-search-result-card.component.spec.ts
+++ b/src/app/features/collections/components/collections-search-result-card/collections-search-result-card.component.spec.ts
@@ -4,7 +4,7 @@ import { ComponentRef } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ContributorsListComponent } from '@osf/shared/components/contributors-list/contributors-list.component';
-import { CollectionSubmissionWithGuid } from '@osf/shared/models/collections/collections.models';
+import { CollectionSubmissionWithGuid } from '@osf/shared/models/collections/collections.model';
import { CollectionsSearchResultCardComponent } from './collections-search-result-card.component';
diff --git a/src/app/features/collections/components/collections-search-result-card/collections-search-result-card.component.ts b/src/app/features/collections/components/collections-search-result-card/collections-search-result-card.component.ts
index 08299c88f..2a7b91117 100644
--- a/src/app/features/collections/components/collections-search-result-card/collections-search-result-card.component.ts
+++ b/src/app/features/collections/components/collections-search-result-card/collections-search-result-card.component.ts
@@ -5,7 +5,7 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
import { collectionFilterNames } from '@osf/features/collections/constants';
import { ContributorsListComponent } from '@osf/shared/components/contributors-list/contributors-list.component';
-import { CollectionSubmissionWithGuid } from '@osf/shared/models/collections/collections.models';
+import { CollectionSubmissionWithGuid } from '@osf/shared/models/collections/collections.model';
@Component({
selector: 'osf-collections-search-result-card',
diff --git a/src/app/features/collections/models/collection-license-json-api.models.ts b/src/app/features/collections/models/collection-license-json-api.model.ts
similarity index 100%
rename from src/app/features/collections/models/collection-license-json-api.models.ts
rename to src/app/features/collections/models/collection-license-json-api.model.ts
diff --git a/src/app/features/collections/services/project-metadata-form.service.ts b/src/app/features/collections/services/project-metadata-form.service.ts
index f0204c614..84a563259 100644
--- a/src/app/features/collections/services/project-metadata-form.service.ts
+++ b/src/app/features/collections/services/project-metadata-form.service.ts
@@ -3,9 +3,9 @@ import { FormControl, FormGroup, Validators } from '@angular/forms';
import { ProjectMetadataFormControls } from '@osf/features/collections/enums';
import { CustomValidators } from '@osf/shared/helpers/custom-form-validators.helper';
+import { ProjectModel } from '@osf/shared/models/projects/projects.model';
import { LicenseModel } from '@shared/models/license/license.model';
import { ProjectMetadataUpdatePayload } from '@shared/models/project-metadata-update-payload.model';
-import { ProjectModel } from '@shared/models/projects/projects.models';
import { ProjectMetadataForm } from '../models/project-metadata-form.model';
diff --git a/src/app/features/collections/store/add-to-collection/add-to-collection.model.ts b/src/app/features/collections/store/add-to-collection/add-to-collection.model.ts
index 04ad27492..ba47d319d 100644
--- a/src/app/features/collections/store/add-to-collection/add-to-collection.model.ts
+++ b/src/app/features/collections/store/add-to-collection/add-to-collection.model.ts
@@ -1,4 +1,4 @@
-import { CollectionProjectSubmission } from '@osf/shared/models/collections/collections.models';
+import { CollectionProjectSubmission } from '@osf/shared/models/collections/collections.model';
import { LicenseModel } from '@shared/models/license/license.model';
import { AsyncStateModel } from '@shared/models/store/async-state.model';
diff --git a/src/app/features/contributors/models/index.ts b/src/app/features/contributors/models/index.ts
index 83d6f898d..62aef551e 100644
--- a/src/app/features/contributors/models/index.ts
+++ b/src/app/features/contributors/models/index.ts
@@ -1,2 +1,2 @@
export * from './resource-info.model';
-export * from './view-only-components.models';
+export * from './view-only-components.model';
diff --git a/src/app/features/contributors/models/view-only-components.models.ts b/src/app/features/contributors/models/view-only-components.model.ts
similarity index 100%
rename from src/app/features/contributors/models/view-only-components.models.ts
rename to src/app/features/contributors/models/view-only-components.model.ts
diff --git a/src/app/features/home/pages/dashboard/dashboard.component.ts b/src/app/features/home/pages/dashboard/dashboard.component.ts
index 17522201b..f9fa9eb5c 100644
--- a/src/app/features/home/pages/dashboard/dashboard.component.ts
+++ b/src/app/features/home/pages/dashboard/dashboard.component.ts
@@ -23,11 +23,11 @@ import { SearchInputComponent } from '@osf/shared/components/search-input/search
import { SubHeaderComponent } from '@osf/shared/components/sub-header/sub-header.component';
import { DEFAULT_TABLE_PARAMS } from '@osf/shared/constants/default-table-params.constants';
import { SortOrder } from '@osf/shared/enums/sort-order.enum';
+import { MyResourcesItem } from '@osf/shared/models/my-resources/my-resources.model';
+import { MyResourcesSearchFilters } from '@osf/shared/models/my-resources/my-resources-search-filters.model';
import { CustomDialogService } from '@osf/shared/services/custom-dialog.service';
import { ProjectRedirectDialogService } from '@osf/shared/services/project-redirect-dialog.service';
import { ClearMyResources, GetMyProjects, MyResourcesSelectors } from '@osf/shared/stores/my-resources';
-import { MyResourcesItem } from '@shared/models/my-resources/my-resources.models';
-import { MyResourcesSearchFilters } from '@shared/models/my-resources/my-resources-search-filters.models';
import { TableParameters } from '@shared/models/table-parameters.model';
@Component({
diff --git a/src/app/features/meetings/models/index.ts b/src/app/features/meetings/models/index.ts
index 4ff56dcc5..3f2965a75 100644
--- a/src/app/features/meetings/models/index.ts
+++ b/src/app/features/meetings/models/index.ts
@@ -1,4 +1,4 @@
export * from './meeting-feature-card.model';
-export * from './meetings.models';
+export * from './meetings.model';
export * from './meetings-json-api.model';
export * from './partner-organization.model';
diff --git a/src/app/features/meetings/models/meetings.models.ts b/src/app/features/meetings/models/meetings.model.ts
similarity index 100%
rename from src/app/features/meetings/models/meetings.models.ts
rename to src/app/features/meetings/models/meetings.model.ts
diff --git a/src/app/features/metadata/components/metadata-affiliated-institutions/metadata-affiliated-institutions.component.ts b/src/app/features/metadata/components/metadata-affiliated-institutions/metadata-affiliated-institutions.component.ts
index 36f21d30f..bcf1badf8 100644
--- a/src/app/features/metadata/components/metadata-affiliated-institutions/metadata-affiliated-institutions.component.ts
+++ b/src/app/features/metadata/components/metadata-affiliated-institutions/metadata-affiliated-institutions.component.ts
@@ -6,7 +6,7 @@ import { Card } from 'primeng/card';
import { ChangeDetectionStrategy, Component, input, output } from '@angular/core';
import { AffiliatedInstitutionsViewComponent } from '@osf/shared/components/affiliated-institutions-view/affiliated-institutions-view.component';
-import { Institution } from '@osf/shared/models/institutions/institutions.models';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
@Component({
selector: 'osf-metadata-affiliated-institutions',
diff --git a/src/app/features/metadata/components/metadata-collection-item/metadata-collection-item.component.spec.ts b/src/app/features/metadata/components/metadata-collection-item/metadata-collection-item.component.spec.ts
index af5c251b3..65616f04e 100644
--- a/src/app/features/metadata/components/metadata-collection-item/metadata-collection-item.component.spec.ts
+++ b/src/app/features/metadata/components/metadata-collection-item/metadata-collection-item.component.spec.ts
@@ -3,7 +3,7 @@ import { MockComponents } from 'ng-mocks';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CollectionSubmissionReviewState } from '@osf/shared/enums/collection-submission-review-state.enum';
-import { CollectionSubmission } from '@osf/shared/models/collections/collections.models';
+import { CollectionSubmission } from '@osf/shared/models/collections/collections.model';
import { MetadataCollectionItemComponent } from './metadata-collection-item.component';
diff --git a/src/app/features/metadata/components/metadata-collection-item/metadata-collection-item.component.ts b/src/app/features/metadata/components/metadata-collection-item/metadata-collection-item.component.ts
index e8ee18b6f..1c023afd9 100644
--- a/src/app/features/metadata/components/metadata-collection-item/metadata-collection-item.component.ts
+++ b/src/app/features/metadata/components/metadata-collection-item/metadata-collection-item.component.ts
@@ -8,7 +8,7 @@ import { RouterLink } from '@angular/router';
import { collectionFilterNames } from '@osf/features/collections/constants';
import { CollectionSubmissionReviewState } from '@osf/shared/enums/collection-submission-review-state.enum';
-import { CollectionSubmission } from '@osf/shared/models/collections/collections.models';
+import { CollectionSubmission } from '@osf/shared/models/collections/collections.model';
import { KeyValueModel } from '@osf/shared/models/common/key-value.model';
import { CollectionStatusSeverityPipe } from '@osf/shared/pipes/collection-status-severity.pipe';
diff --git a/src/app/features/metadata/components/metadata-collections/metadata-collections.component.ts b/src/app/features/metadata/components/metadata-collections/metadata-collections.component.ts
index daa67530d..affc90e98 100644
--- a/src/app/features/metadata/components/metadata-collections/metadata-collections.component.ts
+++ b/src/app/features/metadata/components/metadata-collections/metadata-collections.component.ts
@@ -5,7 +5,7 @@ import { Skeleton } from 'primeng/skeleton';
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
-import { CollectionSubmission } from '@osf/shared/models/collections/collections.models';
+import { CollectionSubmission } from '@osf/shared/models/collections/collections.model';
import { MetadataCollectionItemComponent } from '../metadata-collection-item/metadata-collection-item.component';
diff --git a/src/app/features/metadata/dialogs/affiliated-institutions-dialog/affiliated-institutions-dialog.component.spec.ts b/src/app/features/metadata/dialogs/affiliated-institutions-dialog/affiliated-institutions-dialog.component.spec.ts
index 2378c870a..aa55a264f 100644
--- a/src/app/features/metadata/dialogs/affiliated-institutions-dialog/affiliated-institutions-dialog.component.spec.ts
+++ b/src/app/features/metadata/dialogs/affiliated-institutions-dialog/affiliated-institutions-dialog.component.spec.ts
@@ -5,8 +5,8 @@ import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AffiliatedInstitutionSelectComponent } from '@osf/shared/components/affiliated-institution-select/affiliated-institution-select.component';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
import { InstitutionsSelectors } from '@osf/shared/stores/institutions';
-import { Institution } from '@shared/models/institutions/institutions.models';
import { AffiliatedInstitutionsDialogComponent } from './affiliated-institutions-dialog.component';
diff --git a/src/app/features/metadata/dialogs/affiliated-institutions-dialog/affiliated-institutions-dialog.component.ts b/src/app/features/metadata/dialogs/affiliated-institutions-dialog/affiliated-institutions-dialog.component.ts
index 079629c43..b05fa2e11 100644
--- a/src/app/features/metadata/dialogs/affiliated-institutions-dialog/affiliated-institutions-dialog.component.ts
+++ b/src/app/features/metadata/dialogs/affiliated-institutions-dialog/affiliated-institutions-dialog.component.ts
@@ -9,7 +9,7 @@ import { ChangeDetectionStrategy, Component, inject, OnInit, signal } from '@ang
import { ReactiveFormsModule } from '@angular/forms';
import { AffiliatedInstitutionSelectComponent } from '@osf/shared/components/affiliated-institution-select/affiliated-institution-select.component';
-import { Institution } from '@osf/shared/models/institutions/institutions.models';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
import { FetchUserInstitutions, InstitutionsSelectors } from '@osf/shared/stores/institutions';
@Component({
diff --git a/src/app/features/metadata/models/metadata.model.ts b/src/app/features/metadata/models/metadata.model.ts
index b8ce8f5cb..675ad53fe 100644
--- a/src/app/features/metadata/models/metadata.model.ts
+++ b/src/app/features/metadata/models/metadata.model.ts
@@ -1,6 +1,6 @@
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
import { UserPermissions } from '@shared/enums/user-permissions.enum';
import { IdentifierModel } from '@shared/models/identifiers/identifier.model';
-import { Institution } from '@shared/models/institutions/institutions.models';
import { LicenseModel } from '@shared/models/license/license.model';
export interface MetadataModel {
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 fd0b7ef0f..81c1c24db 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,8 +4,8 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ActivatedRoute, Router } from '@angular/router';
import { IconComponent } from '@osf/shared/components/icon/icon.component';
+import { CollectionSubmissionWithGuid } from '@osf/shared/models/collections/collections.model';
import { CollectionsSelectors } from '@osf/shared/stores/collections';
-import { CollectionSubmissionWithGuid } from '@shared/models/collections/collections.models';
import { DateAgoPipe } from '@shared/pipes/date-ago.pipe';
import { SubmissionReviewStatus } from '../../enums';
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 0f5d0a3ae..a1d475ac1 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
@@ -12,7 +12,7 @@ import { collectionFilterNames } from '@osf/features/collections/constants';
import { ContributorsListComponent } from '@osf/shared/components/contributors-list/contributors-list.component';
import { IconComponent } from '@osf/shared/components/icon/icon.component';
import { TruncatedTextComponent } from '@osf/shared/components/truncated-text/truncated-text.component';
-import { CollectionSubmissionWithGuid } from '@osf/shared/models/collections/collections.models';
+import { CollectionSubmissionWithGuid } from '@osf/shared/models/collections/collections.model';
import { DateAgoPipe } from '@osf/shared/pipes/date-ago.pipe';
import { CollectionsSelectors } from '@osf/shared/stores/collections';
diff --git a/src/app/features/moderation/components/collection-submissions-list/collection-submissions-list.component.ts b/src/app/features/moderation/components/collection-submissions-list/collection-submissions-list.component.ts
index e87f597fd..b98c947df 100644
--- a/src/app/features/moderation/components/collection-submissions-list/collection-submissions-list.component.ts
+++ b/src/app/features/moderation/components/collection-submissions-list/collection-submissions-list.component.ts
@@ -8,7 +8,7 @@ import {
GetCollectionSubmissionContributors,
LoadMoreCollectionSubmissionContributors,
} from '@osf/features/moderation/store/collections-moderation';
-import { CollectionSubmissionWithGuid } from '@shared/models/collections/collections.models';
+import { CollectionSubmissionWithGuid } from '@osf/shared/models/collections/collections.model';
import { CollectionsModerationSelectors } from '../../store/collections-moderation';
import { CollectionSubmissionItemComponent } from '../collection-submission-item/collection-submission-item.component';
diff --git a/src/app/features/moderation/models/collection-submission-review-action-json.api.ts b/src/app/features/moderation/models/collection-submission-review-action-json-api.model.ts
similarity index 100%
rename from src/app/features/moderation/models/collection-submission-review-action-json.api.ts
rename to src/app/features/moderation/models/collection-submission-review-action-json-api.model.ts
diff --git a/src/app/features/moderation/models/index.ts b/src/app/features/moderation/models/index.ts
index 302a37ead..7d32ef4a3 100644
--- a/src/app/features/moderation/models/index.ts
+++ b/src/app/features/moderation/models/index.ts
@@ -1,5 +1,5 @@
export * from './collection-submission-review-action.model';
-export * from './collection-submission-review-action-json.api';
+export * from './collection-submission-review-action-json-api.model';
export * from './invite-moderator-form.model';
export * from './moderator.model';
export * from './moderator-add.model';
diff --git a/src/app/features/moderation/services/moderators.service.ts b/src/app/features/moderation/services/moderators.service.ts
index dfb86fca8..3a184913f 100644
--- a/src/app/features/moderation/services/moderators.service.ts
+++ b/src/app/features/moderation/services/moderators.service.ts
@@ -8,7 +8,7 @@ import { parseSearchTotalCount } from '@osf/shared/helpers/search-total-count.he
import { MapResources } from '@osf/shared/mappers/search';
import { JsonApiResponse } from '@osf/shared/models/common/json-api.model';
import { PaginatedData } from '@osf/shared/models/paginated-data.model';
-import { IndexCardSearchResponseJsonApi } from '@osf/shared/models/search/index-card-search-json-api.models';
+import { IndexCardSearchResponseJsonApi } from '@osf/shared/models/search/index-card-search-json-api.model';
import { SearchUserDataModel } from '@osf/shared/models/user/search-user-data.model';
import { JsonApiService } from '@osf/shared/services/json-api.service';
import { StringOrNull } from '@shared/helpers/types.helper';
diff --git a/src/app/features/moderation/store/collections-moderation/collections-moderation.model.ts b/src/app/features/moderation/store/collections-moderation/collections-moderation.model.ts
index b685d281e..daa7cb0af 100644
--- a/src/app/features/moderation/store/collections-moderation/collections-moderation.model.ts
+++ b/src/app/features/moderation/store/collections-moderation/collections-moderation.model.ts
@@ -1,5 +1,5 @@
import { CollectionSubmissionReviewAction } from '@osf/features/moderation/models';
-import { CollectionSubmissionWithGuid } from '@osf/shared/models/collections/collections.models';
+import { CollectionSubmissionWithGuid } from '@osf/shared/models/collections/collections.model';
import { AsyncStateModel } from '@osf/shared/models/store/async-state.model';
import { AsyncStateWithTotalCount } from '@osf/shared/models/store/async-state-with-total-count.model';
diff --git a/src/app/features/my-projects/my-projects.component.ts b/src/app/features/my-projects/my-projects.component.ts
index 688499d53..8e499b175 100644
--- a/src/app/features/my-projects/my-projects.component.ts
+++ b/src/app/features/my-projects/my-projects.component.ts
@@ -32,6 +32,8 @@ import { SubHeaderComponent } from '@osf/shared/components/sub-header/sub-header
import { DEFAULT_TABLE_PARAMS } from '@osf/shared/constants/default-table-params.constants';
import { SortOrder } from '@osf/shared/enums/sort-order.enum';
import { IS_MEDIUM } from '@osf/shared/helpers/breakpoints.tokens';
+import { MyResourcesItem } from '@osf/shared/models/my-resources/my-resources.model';
+import { MyResourcesSearchFilters } from '@osf/shared/models/my-resources/my-resources-search-filters.model';
import { CustomDialogService } from '@osf/shared/services/custom-dialog.service';
import { ProjectRedirectDialogService } from '@osf/shared/services/project-redirect-dialog.service';
import { BookmarksSelectors, GetAllMyBookmarks, GetBookmarksCollectionId } from '@osf/shared/stores/bookmarks';
@@ -42,8 +44,6 @@ import {
GetMyRegistrations,
MyResourcesSelectors,
} from '@osf/shared/stores/my-resources';
-import { MyResourcesItem } from '@shared/models/my-resources/my-resources.models';
-import { MyResourcesSearchFilters } from '@shared/models/my-resources/my-resources-search-filters.models';
import { QueryParams } from '@shared/models/query-params.model';
import { TableParameters } from '@shared/models/table-parameters.model';
diff --git a/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-affiliated-institutions/preprints-affiliated-institutions.component.spec.ts b/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-affiliated-institutions/preprints-affiliated-institutions.component.spec.ts
index 8bb817170..68cff07db 100644
--- a/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-affiliated-institutions/preprints-affiliated-institutions.component.spec.ts
+++ b/src/app/features/preprints/components/stepper/preprints-metadata-step/preprints-affiliated-institutions/preprints-affiliated-institutions.component.spec.ts
@@ -6,7 +6,7 @@ import { ReviewsState } from '@osf/features/preprints/enums';
import { PreprintProviderDetails } from '@osf/features/preprints/models';
import { PreprintStepperSelectors } from '@osf/features/preprints/store/preprint-stepper';
import { AffiliatedInstitutionSelectComponent } from '@osf/shared/components/affiliated-institution-select/affiliated-institution-select.component';
-import { Institution } from '@shared/models/institutions/institutions.models';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
import { InstitutionsSelectors } from '@shared/stores/institutions';
import { PreprintsAffiliatedInstitutionsComponent } from './preprints-affiliated-institutions.component';
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 f1a315ce1..fc8444c93 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
@@ -11,7 +11,7 @@ import { PreprintModel, PreprintProviderDetails } from '@osf/features/preprints/
import { PreprintStepperSelectors, SetInstitutionsChanged } from '@osf/features/preprints/store/preprint-stepper';
import { AffiliatedInstitutionSelectComponent } from '@osf/shared/components/affiliated-institution-select/affiliated-institution-select.component';
import { ResourceType } from '@osf/shared/enums/resource-type.enum';
-import { Institution } from '@osf/shared/models/institutions/institutions.models';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
import {
FetchResourceInstitutions,
FetchUserInstitutions,
diff --git a/src/app/features/preprints/models/index.ts b/src/app/features/preprints/models/index.ts
index 9131b75cb..5c556d995 100644
--- a/src/app/features/preprints/models/index.ts
+++ b/src/app/features/preprints/models/index.ts
@@ -1,10 +1,10 @@
-export * from './preprint.models';
-export * from './preprint-json-api.models';
-export * from './preprint-licenses-json-api.models';
-export * from './preprint-provider.models';
-export * from './preprint-provider-json-api.models';
-export * from './preprint-request.models';
-export * from './preprint-request-action.models';
-export * from './preprint-request-action-json-api.models';
-export * from './preprint-request-json-api.models';
-export * from './submit-preprint-form.models';
+export * from './preprint.model';
+export * from './preprint-json-api.model';
+export * from './preprint-licenses-json-api.model';
+export * from './preprint-provider.model';
+export * from './preprint-provider-json-api.model';
+export * from './preprint-request.model';
+export * from './preprint-request-action.model';
+export * from './preprint-request-action-json-api.model';
+export * from './preprint-request-json-api.model';
+export * from './submit-preprint-form.model';
diff --git a/src/app/features/preprints/models/preprint-json-api.models.ts b/src/app/features/preprints/models/preprint-json-api.model.ts
similarity index 100%
rename from src/app/features/preprints/models/preprint-json-api.models.ts
rename to src/app/features/preprints/models/preprint-json-api.model.ts
diff --git a/src/app/features/preprints/models/preprint-licenses-json-api.models.ts b/src/app/features/preprints/models/preprint-licenses-json-api.model.ts
similarity index 100%
rename from src/app/features/preprints/models/preprint-licenses-json-api.models.ts
rename to src/app/features/preprints/models/preprint-licenses-json-api.model.ts
diff --git a/src/app/features/preprints/models/preprint-provider-json-api.models.ts b/src/app/features/preprints/models/preprint-provider-json-api.model.ts
similarity index 94%
rename from src/app/features/preprints/models/preprint-provider-json-api.models.ts
rename to src/app/features/preprints/models/preprint-provider-json-api.model.ts
index 0dffb8a70..e71cc5ee2 100644
--- a/src/app/features/preprints/models/preprint-provider-json-api.models.ts
+++ b/src/app/features/preprints/models/preprint-provider-json-api.model.ts
@@ -4,7 +4,7 @@ import { BrandDataJsonApi } from '@osf/shared/models/brand/brand.json-api.model'
import { ProviderReviewsWorkflow } from '../enums';
-import { PreprintWord } from './preprint-provider.models';
+import { PreprintWord } from './preprint-provider.model';
export interface PreprintProviderDetailsJsonApi {
id: string;
diff --git a/src/app/features/preprints/models/preprint-provider.models.ts b/src/app/features/preprints/models/preprint-provider.model.ts
similarity index 100%
rename from src/app/features/preprints/models/preprint-provider.models.ts
rename to src/app/features/preprints/models/preprint-provider.model.ts
diff --git a/src/app/features/preprints/models/preprint-request-action-json-api.models.ts b/src/app/features/preprints/models/preprint-request-action-json-api.model.ts
similarity index 100%
rename from src/app/features/preprints/models/preprint-request-action-json-api.models.ts
rename to src/app/features/preprints/models/preprint-request-action-json-api.model.ts
diff --git a/src/app/features/preprints/models/preprint-request-action.models.ts b/src/app/features/preprints/models/preprint-request-action.model.ts
similarity index 100%
rename from src/app/features/preprints/models/preprint-request-action.models.ts
rename to src/app/features/preprints/models/preprint-request-action.model.ts
diff --git a/src/app/features/preprints/models/preprint-request-json-api.models.ts b/src/app/features/preprints/models/preprint-request-json-api.model.ts
similarity index 100%
rename from src/app/features/preprints/models/preprint-request-json-api.models.ts
rename to src/app/features/preprints/models/preprint-request-json-api.model.ts
diff --git a/src/app/features/preprints/models/preprint-request.models.ts b/src/app/features/preprints/models/preprint-request.model.ts
similarity index 100%
rename from src/app/features/preprints/models/preprint-request.models.ts
rename to src/app/features/preprints/models/preprint-request.model.ts
diff --git a/src/app/features/preprints/models/preprint.models.ts b/src/app/features/preprints/models/preprint.model.ts
similarity index 100%
rename from src/app/features/preprints/models/preprint.models.ts
rename to src/app/features/preprints/models/preprint.model.ts
diff --git a/src/app/features/preprints/models/submit-preprint-form.models.ts b/src/app/features/preprints/models/submit-preprint-form.model.ts
similarity index 100%
rename from src/app/features/preprints/models/submit-preprint-form.models.ts
rename to src/app/features/preprints/models/submit-preprint-form.model.ts
diff --git a/src/app/features/preprints/services/preprints.service.ts b/src/app/features/preprints/services/preprints.service.ts
index 15e11e7bc..6b9b780e1 100644
--- a/src/app/features/preprints/services/preprints.service.ts
+++ b/src/app/features/preprints/services/preprints.service.ts
@@ -6,7 +6,7 @@ import { ENVIRONMENT } from '@core/provider/environment.provider';
import { RegistryModerationMapper } from '@osf/features/moderation/mappers';
import { ReviewActionsResponseJsonApi } from '@osf/features/moderation/models';
import { PreprintRequestActionsMapper } from '@osf/features/preprints/mappers/preprint-request-actions.mapper';
-import { PreprintRequestAction } from '@osf/features/preprints/models/preprint-request-action.models';
+import { PreprintRequestAction } from '@osf/features/preprints/models/preprint-request-action.model';
import { searchPreferencesToJsonApiQueryParams } from '@osf/shared/helpers/search-pref-to-json-api-query-params.helper';
import { StringOrNull } from '@osf/shared/helpers/types.helper';
import {
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 52adcbdb4..b209e62d7 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
@@ -7,9 +7,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EducationHistoryComponent } from '@osf/shared/components/education-history/education-history.component';
import { EmploymentHistoryComponent } from '@osf/shared/components/employment-history/employment-history.component';
import { IS_MEDIUM } from '@osf/shared/helpers/breakpoints.tokens';
-import { Institution } from '@shared/models/institutions/institutions.models';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
+import { UserModel } from '@osf/shared/models/user/user.model';
import { SocialModel } from '@shared/models/user/social.model';
-import { UserModel } from '@shared/models/user/user.models';
import { ProfileInformationComponent } from './profile-information.component';
diff --git a/src/app/features/profile/components/profile-information/profile-information.component.ts b/src/app/features/profile/components/profile-information/profile-information.component.ts
index 0740068b0..da555cac9 100644
--- a/src/app/features/profile/components/profile-information/profile-information.component.ts
+++ b/src/app/features/profile/components/profile-information/profile-information.component.ts
@@ -11,9 +11,9 @@ import { EducationHistoryComponent } from '@osf/shared/components/education-hist
import { EmploymentHistoryComponent } from '@osf/shared/components/employment-history/employment-history.component';
import { SOCIAL_LINKS } from '@osf/shared/constants/social-links.const';
import { IS_MEDIUM } from '@osf/shared/helpers/breakpoints.tokens';
-import { UserModel } from '@osf/shared/models/user/user.models';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
+import { UserModel } from '@osf/shared/models/user/user.model';
import { SortByDatePipe } from '@osf/shared/pipes/sort-by-date.pipe';
-import { Institution } from '@shared/models/institutions/institutions.models';
import { mapUserSocials } from '../../helpers';
diff --git a/src/app/features/profile/profile.component.ts b/src/app/features/profile/profile.component.ts
index 86e1afa43..907ca38d7 100644
--- a/src/app/features/profile/profile.component.ts
+++ b/src/app/features/profile/profile.component.ts
@@ -23,7 +23,7 @@ import { GlobalSearchComponent } from '@osf/shared/components/global-search/glob
import { LoadingSpinnerComponent } from '@osf/shared/components/loading-spinner/loading-spinner.component';
import { SEARCH_TAB_OPTIONS } from '@osf/shared/constants/search-tab-options.const';
import { ResourceType } from '@osf/shared/enums/resource-type.enum';
-import { UserModel } from '@osf/shared/models/user/user.models';
+import { UserModel } from '@osf/shared/models/user/user.model';
import { SetDefaultFilterValue } from '@osf/shared/stores/global-search';
import { FetchUserInstitutions, InstitutionsSelectors } from '@shared/stores/institutions';
diff --git a/src/app/features/profile/store/profile.actions.ts b/src/app/features/profile/store/profile.actions.ts
index 61269ae9e..edcdf0d64 100644
--- a/src/app/features/profile/store/profile.actions.ts
+++ b/src/app/features/profile/store/profile.actions.ts
@@ -1,4 +1,4 @@
-import { UserModel } from '@osf/shared/models/user/user.models';
+import { UserModel } from '@osf/shared/models/user/user.model';
export class FetchUserProfile {
static readonly type = '[Profile] Fetch User Profile';
diff --git a/src/app/features/profile/store/profile.model.ts b/src/app/features/profile/store/profile.model.ts
index 87d4feee1..3d11d531d 100644
--- a/src/app/features/profile/store/profile.model.ts
+++ b/src/app/features/profile/store/profile.model.ts
@@ -1,5 +1,5 @@
import { AsyncStateModel } from '@osf/shared/models/store/async-state.model';
-import { UserModel } from '@osf/shared/models/user/user.models';
+import { UserModel } from '@osf/shared/models/user/user.model';
export interface ProfileStateModel {
userProfile: AsyncStateModel;
diff --git a/src/app/features/profile/store/profile.selectors.ts b/src/app/features/profile/store/profile.selectors.ts
index 48869e8c3..db39632b9 100644
--- a/src/app/features/profile/store/profile.selectors.ts
+++ b/src/app/features/profile/store/profile.selectors.ts
@@ -1,6 +1,6 @@
import { Selector } from '@ngxs/store';
-import { UserModel } from '@osf/shared/models/user/user.models';
+import { UserModel } from '@osf/shared/models/user/user.model';
import { ProfileStateModel } from './profile.model';
import { ProfileState } from '.';
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 85fc28603..ea91f887f 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
@@ -17,11 +17,11 @@ import { UserSelectors } from '@core/store/user';
import { AffiliatedInstitutionSelectComponent } from '@osf/shared/components/affiliated-institution-select/affiliated-institution-select.component';
import { ComponentFormControls } from '@osf/shared/enums/create-component-form-controls.enum';
import { CustomValidators } from '@osf/shared/helpers/custom-form-validators.helper';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
import { ToastService } from '@osf/shared/services/toast.service';
import { FetchUserInstitutions, InstitutionsSelectors } from '@osf/shared/stores/institutions';
import { FetchRegions, RegionsSelectors } from '@osf/shared/stores/regions';
import { ComponentForm } from '@shared/models/create-component-form.model';
-import { Institution } from '@shared/models/institutions/institutions.models';
import { CreateComponent, GetComponents, ProjectOverviewSelectors } from '../../store';
diff --git a/src/app/features/project/overview/components/files-widget/files-widget.component.ts b/src/app/features/project/overview/components/files-widget/files-widget.component.ts
index c907027ac..535b01a43 100644
--- a/src/app/features/project/overview/components/files-widget/files-widget.component.ts
+++ b/src/app/features/project/overview/components/files-widget/files-widget.component.ts
@@ -38,7 +38,7 @@ import { FileModel } from '@osf/shared/models/files/file.model';
import { FileFolderModel } from '@osf/shared/models/files/file-folder.model';
import { FileLabelModel } from '@osf/shared/models/files/file-label.model';
import { NodeShortInfoModel } from '@osf/shared/models/nodes/node-with-children.model';
-import { ProjectModel } from '@osf/shared/models/projects/projects.models';
+import { ProjectModel } from '@osf/shared/models/projects/projects.model';
import { SelectOption } from '@osf/shared/models/select-option.model';
import { ViewOnlyLinkHelperService } from '@osf/shared/services/view-only-link-helper.service';
diff --git a/src/app/features/project/overview/components/link-resource-dialog/link-resource-dialog.component.spec.ts b/src/app/features/project/overview/components/link-resource-dialog/link-resource-dialog.component.spec.ts
index f94d9f4c9..b74d844cf 100644
--- a/src/app/features/project/overview/components/link-resource-dialog/link-resource-dialog.component.spec.ts
+++ b/src/app/features/project/overview/components/link-resource-dialog/link-resource-dialog.component.spec.ts
@@ -9,9 +9,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SearchInputComponent } from '@osf/shared/components/search-input/search-input.component';
import { ResourceSearchMode } from '@osf/shared/enums/resource-search-mode.enum';
import { ResourceType } from '@osf/shared/enums/resource-type.enum';
+import { MyResourcesItem } from '@osf/shared/models/my-resources/my-resources.model';
import { MyResourcesSelectors } from '@osf/shared/stores/my-resources';
import { NodeLinksSelectors } from '@osf/shared/stores/node-links';
-import { MyResourcesItem } from '@shared/models/my-resources/my-resources.models';
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 fe5542a89..279a2bc55 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
@@ -29,10 +29,10 @@ import { SearchInputComponent } from '@osf/shared/components/search-input/search
import { DEFAULT_TABLE_PARAMS } from '@osf/shared/constants/default-table-params.constants';
import { ResourceSearchMode } from '@osf/shared/enums/resource-search-mode.enum';
import { ResourceType } from '@osf/shared/enums/resource-type.enum';
+import { MyResourcesItem } from '@osf/shared/models/my-resources/my-resources.model';
+import { MyResourcesSearchFilters } from '@osf/shared/models/my-resources/my-resources-search-filters.model';
import { GetMyProjects, GetMyRegistrations, MyResourcesSelectors } from '@osf/shared/stores/my-resources';
import { CreateNodeLink, DeleteNodeLink, NodeLinksSelectors } from '@osf/shared/stores/node-links';
-import { MyResourcesItem } from '@shared/models/my-resources/my-resources.models';
-import { MyResourcesSearchFilters } from '@shared/models/my-resources/my-resources-search-filters.models';
import { TableParameters } from '@shared/models/table-parameters.model';
import { ProjectOverviewSelectors } from '../../store';
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 328898474..168a3530b 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
@@ -10,7 +10,7 @@ import { RouterLink } from '@angular/router';
import { collectionFilterNames } from '@osf/features/collections/constants';
import { StopPropagationDirective } from '@osf/shared/directives/stop-propagation.directive';
-import { CollectionSubmission } from '@osf/shared/models/collections/collections.models';
+import { CollectionSubmission } from '@osf/shared/models/collections/collections.model';
import { KeyValueModel } from '@osf/shared/models/common/key-value.model';
import { CollectionStatusSeverityPipe } from '@osf/shared/pipes/collection-status-severity.pipe';
diff --git a/src/app/features/project/overview/models/index.ts b/src/app/features/project/overview/models/index.ts
index ffa3996aa..b2f55ace8 100644
--- a/src/app/features/project/overview/models/index.ts
+++ b/src/app/features/project/overview/models/index.ts
@@ -1,4 +1,4 @@
export * from './addon-tree-item.model';
export * from './formatted-citation-item.model';
export * from './privacy-status.model';
-export * from './project-overview.models';
+export * from './project-overview.model';
diff --git a/src/app/features/project/overview/models/project-overview.models.ts b/src/app/features/project/overview/models/project-overview.model.ts
similarity index 100%
rename from src/app/features/project/overview/models/project-overview.models.ts
rename to src/app/features/project/overview/models/project-overview.model.ts
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 f923aa0a1..7133193be 100644
--- a/src/app/features/project/overview/services/project-overview.service.ts
+++ b/src/app/features/project/overview/services/project-overview.service.ts
@@ -15,6 +15,7 @@ import { NodeStorageMapper } from '@osf/shared/mappers/nodes/node-storage.mapper
import { JsonApiResponse } from '@osf/shared/models/common/json-api.model';
import { IdentifiersResponseJsonApi } from '@osf/shared/models/identifiers/identifier-json-api.model';
import { InstitutionsJsonApiResponse } from '@osf/shared/models/institutions/institution-json-api.model';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
import { LicenseResponseJsonApi } from '@osf/shared/models/license/licenses-json-api.model';
import { BaseNodeModel, NodeModel } from '@osf/shared/models/nodes/base-node.model';
import { BaseNodeDataJsonApi } from '@osf/shared/models/nodes/base-node-data-json-api.model';
@@ -26,7 +27,6 @@ import { NodeResponseJsonApi, NodesResponseJsonApi } from '@osf/shared/models/no
import { PaginatedData } from '@osf/shared/models/paginated-data.model';
import { JsonApiService } from '@osf/shared/services/json-api.service';
import { IdentifierModel } from '@shared/models/identifiers/identifier.model';
-import { Institution } from '@shared/models/institutions/institutions.models';
import { LicenseModel } from '@shared/models/license/license.model';
import { ProjectOverviewMapper } from '../mappers';
diff --git a/src/app/features/project/overview/store/project-overview.model.ts b/src/app/features/project/overview/store/project-overview.model.ts
index 8675ce272..51abff1ba 100644
--- a/src/app/features/project/overview/store/project-overview.model.ts
+++ b/src/app/features/project/overview/store/project-overview.model.ts
@@ -1,10 +1,10 @@
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
import { BaseNodeModel, NodeModel } from '@osf/shared/models/nodes/base-node.model';
import { NodePreprintModel } from '@osf/shared/models/nodes/node-preprint.model';
import { NodeStorageModel } from '@osf/shared/models/nodes/node-storage.model';
import { AsyncStateModel } from '@osf/shared/models/store/async-state.model';
import { AsyncStateWithTotalCount } from '@osf/shared/models/store/async-state-with-total-count.model';
import { IdentifierModel } from '@shared/models/identifiers/identifier.model';
-import { Institution } from '@shared/models/institutions/institutions.models';
import { LicenseModel } from '@shared/models/license/license.model';
import { ProjectOverviewModel } from '../models';
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 fa9eaa3cb..2ba414289 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
@@ -25,8 +25,8 @@ import { OperationNames } from '@osf/shared/enums/operation-names.enum';
import { ProjectAddonsStepperValue } from '@osf/shared/enums/profile-addons-stepper.enum';
import { getAddonTypeString } from '@osf/shared/helpers/addon-type.helper';
import { AddonModel } from '@osf/shared/models/addons/addon.model';
-import { AuthorizedAddonRequestJsonApi } from '@osf/shared/models/addons/addon-json-api.models';
-import { AddonTerm } from '@osf/shared/models/addons/addon-utils.models';
+import { AuthorizedAddonRequestJsonApi } from '@osf/shared/models/addons/addon-json-api.model';
+import { AddonTerm } from '@osf/shared/models/addons/addon-utils.model';
import { AuthorizedAccountModel } from '@osf/shared/models/addons/authorized-account.model';
import { AddonFormService } from '@osf/shared/services/addons/addon-form.service';
import { AddonOAuthService } from '@osf/shared/services/addons/addon-oauth.service';
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 6e12a2741..f13917010 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/institutions/institutions.model';
import { InstitutionsSelectors } from '@osf/shared/stores/institutions';
-import { Institution } from '@shared/models/institutions/institutions.models';
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 de8f86c66..112511600 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
@@ -8,7 +8,7 @@ import { Card } from 'primeng/card';
import { NgOptimizedImage } from '@angular/common';
import { ChangeDetectionStrategy, Component, computed, input, OnInit, output } from '@angular/core';
-import { Institution } from '@osf/shared/models/institutions/institutions.models';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
import { FetchUserInstitutions, InstitutionsSelectors } from '@shared/stores/institutions';
@Component({
diff --git a/src/app/features/project/settings/models/node-details.model.ts b/src/app/features/project/settings/models/node-details.model.ts
index 483791011..2f3e8bc24 100644
--- a/src/app/features/project/settings/models/node-details.model.ts
+++ b/src/app/features/project/settings/models/node-details.model.ts
@@ -1,6 +1,6 @@
import { UserPermissions } from '@osf/shared/enums/user-permissions.enum';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
import { IdNameModel } from '@shared/models/common/id-name.model';
-import { Institution } from '@shared/models/institutions/institutions.models';
export interface NodeDetailsModel {
id: string;
diff --git a/src/app/features/project/settings/settings.component.ts b/src/app/features/project/settings/settings.component.ts
index 5ad55cc42..d697fe844 100644
--- a/src/app/features/project/settings/settings.component.ts
+++ b/src/app/features/project/settings/settings.component.ts
@@ -15,7 +15,7 @@ import { SubHeaderComponent } from '@osf/shared/components/sub-header/sub-header
import { ResourceType } from '@osf/shared/enums/resource-type.enum';
import { SubscriptionEvent } from '@osf/shared/enums/subscriptions/subscription-event.enum';
import { SubscriptionFrequency } from '@osf/shared/enums/subscriptions/subscription-frequency.enum';
-import { Institution } from '@osf/shared/models/institutions/institutions.models';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
import { UpdateNodeRequestModel } from '@osf/shared/models/nodes/nodes-json-api.model';
import { ViewOnlyLinkModel } from '@osf/shared/models/view-only-links/view-only-link.model';
import { CustomConfirmationService } from '@osf/shared/services/custom-confirmation.service';
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 11741ffba..5fa7e1306 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
@@ -9,7 +9,7 @@ import { ActivatedRoute } from '@angular/router';
import { AffiliatedInstitutionSelectComponent } from '@osf/shared/components/affiliated-institution-select/affiliated-institution-select.component';
import { ResourceType } from '@osf/shared/enums/resource-type.enum';
-import { Institution } from '@osf/shared/models/institutions/institutions.models';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
import {
FetchResourceInstitutions,
FetchUserInstitutions,
diff --git a/src/app/features/registry/services/registry-overview.service.ts b/src/app/features/registry/services/registry-overview.service.ts
index 87d99fbc4..50c4d6454 100644
--- a/src/app/features/registry/services/registry-overview.service.ts
+++ b/src/app/features/registry/services/registry-overview.service.ts
@@ -13,7 +13,7 @@ import { ReviewActionsMapper } from '@osf/shared/mappers/review-actions.mapper';
import { IdentifierModel } from '@osf/shared/models/identifiers/identifier.model';
import { IdentifiersResponseJsonApi } from '@osf/shared/models/identifiers/identifier-json-api.model';
import { InstitutionsJsonApiResponse } from '@osf/shared/models/institutions/institution-json-api.model';
-import { Institution } from '@osf/shared/models/institutions/institutions.models';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
import { LicenseModel } from '@osf/shared/models/license/license.model';
import { LicenseResponseJsonApi } from '@osf/shared/models/license/licenses-json-api.model';
import { PageSchema } from '@osf/shared/models/registration/page-schema.model';
diff --git a/src/app/features/registry/store/registry/registry.model.ts b/src/app/features/registry/store/registry/registry.model.ts
index 027992ed3..098aa94cd 100644
--- a/src/app/features/registry/store/registry/registry.model.ts
+++ b/src/app/features/registry/store/registry/registry.model.ts
@@ -1,6 +1,6 @@
import { ReviewAction } from '@osf/features/moderation/models';
import { IdentifierModel } from '@osf/shared/models/identifiers/identifier.model';
-import { Institution } from '@osf/shared/models/institutions/institutions.models';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
import { LicenseModel } from '@osf/shared/models/license/license.model';
import { PageSchema } from '@osf/shared/models/registration/page-schema.model';
import { SchemaResponse } from '@osf/shared/models/registration/schema-response.model';
diff --git a/src/app/features/registry/store/registry/registry.selectors.ts b/src/app/features/registry/store/registry/registry.selectors.ts
index 3f5e6a729..8adc38841 100644
--- a/src/app/features/registry/store/registry/registry.selectors.ts
+++ b/src/app/features/registry/store/registry/registry.selectors.ts
@@ -4,9 +4,9 @@ import { ReviewAction } from '@osf/features/moderation/models';
import { RegistrationOverviewModel } from '@osf/features/registry/models';
import { UserPermissions } from '@osf/shared/enums/user-permissions.enum';
import { IdentifierModel } from '@osf/shared/models/identifiers/identifier.model';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
import { LicenseModel } from '@osf/shared/models/license/license.model';
import { SchemaResponse } from '@osf/shared/models/registration/schema-response.model';
-import { Institution } from '@shared/models/institutions/institutions.models';
import { PageSchema } from '@shared/models/registration/page-schema.model';
import { RegistryStateModel } from './registry.model';
diff --git a/src/app/features/settings/account-settings/components/affiliated-institutions/affiliated-institutions.component.ts b/src/app/features/settings/account-settings/components/affiliated-institutions/affiliated-institutions.component.ts
index 3168e20f1..3075e2606 100644
--- a/src/app/features/settings/account-settings/components/affiliated-institutions/affiliated-institutions.component.ts
+++ b/src/app/features/settings/account-settings/components/affiliated-institutions/affiliated-institutions.component.ts
@@ -10,7 +10,7 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { UserSelectors } from '@osf/core/store/user';
import { ReadonlyInputComponent } from '@osf/shared/components/readonly-input/readonly-input.component';
-import { Institution } from '@osf/shared/models/institutions/institutions.models';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
import { CustomConfirmationService } from '@osf/shared/services/custom-confirmation.service';
import { LoaderService } from '@osf/shared/services/loader.service';
import { ToastService } from '@osf/shared/services/toast.service';
diff --git a/src/app/features/settings/account-settings/services/account-settings.service.ts b/src/app/features/settings/account-settings/services/account-settings.service.ts
index 227cd29a0..f46142573 100644
--- a/src/app/features/settings/account-settings/services/account-settings.service.ts
+++ b/src/app/features/settings/account-settings/services/account-settings.service.ts
@@ -4,7 +4,7 @@ import { inject, Injectable } from '@angular/core';
import { ENVIRONMENT } from '@core/provider/environment.provider';
import { UserMapper } from '@osf/shared/mappers/user';
-import { UserModel } from '@osf/shared/models/user/user.models';
+import { UserModel } from '@osf/shared/models/user/user.model';
import { UserDataJsonApi } from '@osf/shared/models/user/user-json-api.model';
import { JsonApiService } from '@osf/shared/services/json-api.service';
diff --git a/src/app/features/settings/account-settings/store/account-settings.model.ts b/src/app/features/settings/account-settings/store/account-settings.model.ts
index a81ac14ac..68d2c764b 100644
--- a/src/app/features/settings/account-settings/store/account-settings.model.ts
+++ b/src/app/features/settings/account-settings/store/account-settings.model.ts
@@ -1,4 +1,4 @@
-import { Institution } from '@osf/shared/models/institutions/institutions.models';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
import { AsyncStateModel } from '@osf/shared/models/store/async-state.model';
import { AccountSettings, ExternalIdentity } from '../models';
diff --git a/src/app/features/settings/account-settings/store/account-settings.selectors.ts b/src/app/features/settings/account-settings/store/account-settings.selectors.ts
index bd418f0f0..e0d4e1e1b 100644
--- a/src/app/features/settings/account-settings/store/account-settings.selectors.ts
+++ b/src/app/features/settings/account-settings/store/account-settings.selectors.ts
@@ -1,6 +1,6 @@
import { Selector } from '@ngxs/store';
-import { Institution } from '@osf/shared/models/institutions/institutions.models';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
import { AccountSettings, ExternalIdentity } from '../models';
diff --git a/src/app/features/settings/profile-settings/components/citation-preview/citation-preview.component.ts b/src/app/features/settings/profile-settings/components/citation-preview/citation-preview.component.ts
index f7b4dde33..1cafd1cc2 100644
--- a/src/app/features/settings/profile-settings/components/citation-preview/citation-preview.component.ts
+++ b/src/app/features/settings/profile-settings/components/citation-preview/citation-preview.component.ts
@@ -2,7 +2,7 @@ import { TranslatePipe } from '@ngx-translate/core';
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
-import { UserModel } from '@osf/shared/models/user/user.models';
+import { UserModel } from '@osf/shared/models/user/user.model';
import { CitationFormatPipe } from '@osf/shared/pipes/citation-format.pipe';
@Component({
diff --git a/src/app/features/settings/profile-settings/components/name/name.component.ts b/src/app/features/settings/profile-settings/components/name/name.component.ts
index 4d821b540..cc50b786e 100644
--- a/src/app/features/settings/profile-settings/components/name/name.component.ts
+++ b/src/app/features/settings/profile-settings/components/name/name.component.ts
@@ -11,10 +11,10 @@ import { FormBuilder } from '@angular/forms';
import { UpdateProfileSettingsUser, UserSelectors } from '@osf/core/store/user';
import { forbiddenFileNameCharacters } from '@osf/shared/constants/input-limits.const';
import { CustomValidators } from '@osf/shared/helpers/custom-form-validators.helper';
+import { UserModel } from '@osf/shared/models/user/user.model';
import { CustomConfirmationService } from '@osf/shared/services/custom-confirmation.service';
import { LoaderService } from '@osf/shared/services/loader.service';
import { ToastService } from '@osf/shared/services/toast.service';
-import { UserModel } from '@shared/models/user/user.models';
import { hasNameChanges } from '../../helpers';
import { NameForm } from '../../models';
diff --git a/src/app/features/settings/profile-settings/helpers/name-comparison.helper.ts b/src/app/features/settings/profile-settings/helpers/name-comparison.helper.ts
index c82d60fc8..dfde09e4f 100644
--- a/src/app/features/settings/profile-settings/helpers/name-comparison.helper.ts
+++ b/src/app/features/settings/profile-settings/helpers/name-comparison.helper.ts
@@ -1,5 +1,5 @@
import { findChangedFields } from '@osf/shared/helpers/find-changed-fields';
-import { UserModel } from '@osf/shared/models/user/user.models';
+import { UserModel } from '@osf/shared/models/user/user.model';
import { NameForm } from '../models';
diff --git a/src/app/features/settings/settings-addons/components/connect-addon/connect-addon.component.ts b/src/app/features/settings/settings-addons/components/connect-addon/connect-addon.component.ts
index c1a93ab1d..20041a0b1 100644
--- a/src/app/features/settings/settings-addons/components/connect-addon/connect-addon.component.ts
+++ b/src/app/features/settings/settings-addons/components/connect-addon/connect-addon.component.ts
@@ -18,11 +18,11 @@ import { AddonServiceNames } from '@osf/shared/enums/addon-service-names.enum';
import { AddonType } from '@osf/shared/enums/addon-type.enum';
import { ProjectAddonsStepperValue } from '@osf/shared/enums/profile-addons-stepper.enum';
import { getAddonTypeString, isAuthorizedAddon } from '@osf/shared/helpers/addon-type.helper';
+import { AuthorizedAddonRequestJsonApi } from '@osf/shared/models/addons/addon-json-api.model';
+import { AddonTerm } from '@osf/shared/models/addons/addon-utils.model';
import { AddonOAuthService } from '@osf/shared/services/addons/addon-oauth.service';
import { ToastService } from '@osf/shared/services/toast.service';
import { AddonModel } from '@shared/models/addons/addon.model';
-import { AuthorizedAddonRequestJsonApi } from '@shared/models/addons/addon-json-api.models';
-import { AddonTerm } from '@shared/models/addons/addon-utils.models';
import { AuthorizedAccountModel } from '@shared/models/addons/authorized-account.model';
import { AddonsSelectors, CreateAuthorizedAddon, UpdateAuthorizedAddon } from '@shared/stores/addons';
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 fc74e9c01..412038453 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
@@ -13,11 +13,11 @@ import { FormGroup, ReactiveFormsModule } from '@angular/forms';
import { UserSelectors } from '@core/store/user';
import { ProjectFormControls } from '@osf/shared/enums/create-project-form-controls.enum';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
+import { ProjectModel } from '@osf/shared/models/projects/projects.model';
import { FetchUserInstitutions, InstitutionsSelectors } from '@osf/shared/stores/institutions';
import { FetchRegions, RegionsSelectors } from '@osf/shared/stores/regions';
-import { Institution } from '@shared/models/institutions/institutions.models';
import { ProjectForm } from '@shared/models/projects/create-project-form.model';
-import { ProjectModel } from '@shared/models/projects/projects.models';
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/addon-setup-account-form/addon-setup-account-form.component.ts b/src/app/shared/components/addons/addon-setup-account-form/addon-setup-account-form.component.ts
index d2252a038..1941cbaa7 100644
--- a/src/app/shared/components/addons/addon-setup-account-form/addon-setup-account-form.component.ts
+++ b/src/app/shared/components/addons/addon-setup-account-form/addon-setup-account-form.component.ts
@@ -11,9 +11,9 @@ import { RouterLink } from '@angular/router';
import { AddonFormControls } from '@osf/shared/enums/addon-form-controls.enum';
import { CredentialsFormat } from '@osf/shared/enums/addons-credentials-format.enum';
+import { AuthorizedAddonRequestJsonApi } from '@osf/shared/models/addons/addon-json-api.model';
+import { AddonForm } from '@osf/shared/models/addons/addon-utils.model';
import { AddonModel } from '@shared/models/addons/addon.model';
-import { AuthorizedAddonRequestJsonApi } from '@shared/models/addons/addon-json-api.models';
-import { AddonForm } from '@shared/models/addons/addon-utils.models';
import { AuthorizedAccountModel } from '@shared/models/addons/authorized-account.model';
import { AddonFormService } from '@shared/services/addons/addon-form.service';
diff --git a/src/app/shared/components/addons/addon-terms/addon-terms.component.spec.ts b/src/app/shared/components/addons/addon-terms/addon-terms.component.spec.ts
index 0633723f8..fbfeaea93 100644
--- a/src/app/shared/components/addons/addon-terms/addon-terms.component.spec.ts
+++ b/src/app/shared/components/addons/addon-terms/addon-terms.component.spec.ts
@@ -3,7 +3,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ADDON_TERMS } from '@osf/shared/constants/addon-terms.const';
import { isCitationAddon, isRedirectAddon } from '@osf/shared/helpers/addon-type.helper';
import { AddonModel } from '@osf/shared/models/addons/addon.model';
-import { AddonTerm } from '@osf/shared/models/addons/addon-utils.models';
+import { AddonTerm } from '@osf/shared/models/addons/addon-utils.model';
import { AddonTermsComponent } from './addon-terms.component';
diff --git a/src/app/shared/components/addons/addon-terms/addon-terms.component.ts b/src/app/shared/components/addons/addon-terms/addon-terms.component.ts
index 464a1f5b4..b2273ae26 100644
--- a/src/app/shared/components/addons/addon-terms/addon-terms.component.ts
+++ b/src/app/shared/components/addons/addon-terms/addon-terms.component.ts
@@ -7,8 +7,8 @@ import { Component, computed, input } from '@angular/core';
import { ADDON_TERMS } from '@osf/shared/constants/addon-terms.const';
import { isCitationAddon, isRedirectAddon } from '@osf/shared/helpers/addon-type.helper';
+import { AddonTerm } from '@osf/shared/models/addons/addon-utils.model';
import { AddonModel } from '@shared/models/addons/addon.model';
-import { AddonTerm } from '@shared/models/addons/addon-utils.models';
import { AuthorizedAccountModel } from '@shared/models/addons/authorized-account.model';
@Component({
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 3f8588d07..26291fadc 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
@@ -36,9 +36,9 @@ import { OperationNames } from '@osf/shared/enums/operation-names.enum';
import { StorageItemType } from '@osf/shared/enums/storage-item-type.enum';
import { IS_XSMALL } from '@osf/shared/helpers/breakpoints.tokens';
import { convertCamelCaseToNormal } from '@osf/shared/helpers/camel-case-to-normal.helper';
+import { OperationInvokeData } from '@osf/shared/models/addons/addon-utils.model';
import { CustomDialogService } from '@osf/shared/services/custom-dialog.service';
import { AddonsSelectors, ClearOperationInvocations } from '@osf/shared/stores/addons';
-import { OperationInvokeData } from '@shared/models/addons/addon-utils.models';
import { StorageItem } from '@shared/models/addons/storage-item.model';
import { GoogleFilePickerComponent } from '../../google-file-picker/google-file-picker.component';
diff --git a/src/app/shared/components/affiliated-institution-select/affiliated-institution-select.component.spec.ts b/src/app/shared/components/affiliated-institution-select/affiliated-institution-select.component.spec.ts
index 91a90646d..cddac3c53 100644
--- a/src/app/shared/components/affiliated-institution-select/affiliated-institution-select.component.spec.ts
+++ b/src/app/shared/components/affiliated-institution-select/affiliated-institution-select.component.spec.ts
@@ -1,6 +1,6 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { Institution } from '@shared/models/institutions/institutions.models';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
import { AffiliatedInstitutionSelectComponent } from './affiliated-institution-select.component';
diff --git a/src/app/shared/components/affiliated-institution-select/affiliated-institution-select.component.ts b/src/app/shared/components/affiliated-institution-select/affiliated-institution-select.component.ts
index 9f7b0d4e7..4fd7f261d 100644
--- a/src/app/shared/components/affiliated-institution-select/affiliated-institution-select.component.ts
+++ b/src/app/shared/components/affiliated-institution-select/affiliated-institution-select.component.ts
@@ -8,7 +8,7 @@ import { NgOptimizedImage } from '@angular/common';
import { ChangeDetectionStrategy, Component, computed, input, model } from '@angular/core';
import { FormsModule } from '@angular/forms';
-import { Institution } from '@osf/shared/models/institutions/institutions.models';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
@Component({
selector: 'osf-affiliated-institution-select',
diff --git a/src/app/shared/components/affiliated-institutions-view/affiliated-institutions-view.component.spec.ts b/src/app/shared/components/affiliated-institutions-view/affiliated-institutions-view.component.spec.ts
index 497468a59..4724ab97a 100644
--- a/src/app/shared/components/affiliated-institutions-view/affiliated-institutions-view.component.spec.ts
+++ b/src/app/shared/components/affiliated-institutions-view/affiliated-institutions-view.component.spec.ts
@@ -1,6 +1,6 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { Institution } from '@shared/models/institutions/institutions.models';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
import { AffiliatedInstitutionsViewComponent } from './affiliated-institutions-view.component';
diff --git a/src/app/shared/components/affiliated-institutions-view/affiliated-institutions-view.component.ts b/src/app/shared/components/affiliated-institutions-view/affiliated-institutions-view.component.ts
index 8edecaf23..9f8d45cce 100644
--- a/src/app/shared/components/affiliated-institutions-view/affiliated-institutions-view.component.ts
+++ b/src/app/shared/components/affiliated-institutions-view/affiliated-institutions-view.component.ts
@@ -6,7 +6,7 @@ import { Tooltip } from 'primeng/tooltip';
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
import { RouterLink } from '@angular/router';
-import { Institution } from '@osf/shared/models/institutions/institutions.models';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
@Component({
selector: 'osf-affiliated-institutions-view',
diff --git a/src/app/shared/components/bar-chart/bar-chart.component.ts b/src/app/shared/components/bar-chart/bar-chart.component.ts
index e1184f9e7..dce54adc1 100644
--- a/src/app/shared/components/bar-chart/bar-chart.component.ts
+++ b/src/app/shared/components/bar-chart/bar-chart.component.ts
@@ -7,7 +7,7 @@ import { isPlatformBrowser } from '@angular/common';
import { ChangeDetectionStrategy, Component, inject, input, OnInit, PLATFORM_ID, signal } from '@angular/core';
import { PIE_CHART_PALETTE } from '@osf/shared/constants/pie-chart-palette';
-import { DatasetInput } from '@shared/models/charts/dataset-input';
+import { DatasetInput } from '@osf/shared/models/charts/dataset-input.model';
import { LoadingSpinnerComponent } from '../loading-spinner/loading-spinner.component';
diff --git a/src/app/shared/components/doughnut-chart/doughnut-chart.component.ts b/src/app/shared/components/doughnut-chart/doughnut-chart.component.ts
index edf3b3123..c22a9cb75 100644
--- a/src/app/shared/components/doughnut-chart/doughnut-chart.component.ts
+++ b/src/app/shared/components/doughnut-chart/doughnut-chart.component.ts
@@ -7,7 +7,7 @@ import { isPlatformBrowser } from '@angular/common';
import { ChangeDetectionStrategy, Component, inject, input, OnInit, PLATFORM_ID, signal } from '@angular/core';
import { PIE_CHART_PALETTE } from '@osf/shared/constants/pie-chart-palette';
-import { DatasetInput } from '@shared/models/charts/dataset-input';
+import { DatasetInput } from '@osf/shared/models/charts/dataset-input.model';
import { LoadingSpinnerComponent } from '../loading-spinner/loading-spinner.component';
diff --git a/src/app/shared/components/license/license.component.ts b/src/app/shared/components/license/license.component.ts
index e81226cfb..6d8823827 100644
--- a/src/app/shared/components/license/license.component.ts
+++ b/src/app/shared/components/license/license.component.ts
@@ -12,8 +12,8 @@ import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angul
import { InputLimits } from '@osf/shared/constants/input-limits.const';
import { CustomValidators } from '@osf/shared/helpers/custom-form-validators.helper';
import { StringOrNullOrUndefined } from '@osf/shared/helpers/types.helper';
+import { LicenseForm } from '@osf/shared/models/license/license-form.model';
import { LicenseModel, LicenseOptions } from '@shared/models/license/license.model';
-import { LicenseForm } from '@shared/models/license/license-form.models';
import { InterpolatePipe } from '@shared/pipes/interpolate.pipe';
import { TextInputComponent } from '../text-input/text-input.component';
diff --git a/src/app/shared/components/line-chart/line-chart.component.spec.ts b/src/app/shared/components/line-chart/line-chart.component.spec.ts
index 3d2938fbf..9e148757f 100644
--- a/src/app/shared/components/line-chart/line-chart.component.spec.ts
+++ b/src/app/shared/components/line-chart/line-chart.component.spec.ts
@@ -5,7 +5,7 @@ import { ChartModule } from 'primeng/chart';
import { PLATFORM_ID } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { DatasetInput } from '@osf/shared/models/charts/dataset-input';
+import { DatasetInput } from '@osf/shared/models/charts/dataset-input.model';
import { LoadingSpinnerComponent } from '../loading-spinner/loading-spinner.component';
diff --git a/src/app/shared/components/line-chart/line-chart.component.ts b/src/app/shared/components/line-chart/line-chart.component.ts
index fac02dab3..7462312b5 100644
--- a/src/app/shared/components/line-chart/line-chart.component.ts
+++ b/src/app/shared/components/line-chart/line-chart.component.ts
@@ -14,7 +14,7 @@ import {
signal,
} from '@angular/core';
-import { DatasetInput } from '@osf/shared/models/charts/dataset-input';
+import { DatasetInput } from '@osf/shared/models/charts/dataset-input.model';
import { LoadingSpinnerComponent } from '../loading-spinner/loading-spinner.component';
diff --git a/src/app/shared/components/my-projects-table/my-projects-table.component.spec.ts b/src/app/shared/components/my-projects-table/my-projects-table.component.spec.ts
index 5aa148c0e..ffb134af6 100644
--- a/src/app/shared/components/my-projects-table/my-projects-table.component.spec.ts
+++ b/src/app/shared/components/my-projects-table/my-projects-table.component.spec.ts
@@ -3,8 +3,8 @@ import { MockComponents } from 'ng-mocks';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SortOrder } from '@osf/shared/enums/sort-order.enum';
+import { MyResourcesItem } from '@osf/shared/models/my-resources/my-resources.model';
import { TableParameters } from '@osf/shared/models/table-parameters.model';
-import { MyResourcesItem } from '@shared/models/my-resources/my-resources.models';
import { ContributorsListShortenerComponent } from '../contributors-list-shortener/contributors-list-shortener.component';
import { IconComponent } from '../icon/icon.component';
diff --git a/src/app/shared/components/my-projects-table/my-projects-table.component.ts b/src/app/shared/components/my-projects-table/my-projects-table.component.ts
index 22472efb9..0cd62cda7 100644
--- a/src/app/shared/components/my-projects-table/my-projects-table.component.ts
+++ b/src/app/shared/components/my-projects-table/my-projects-table.component.ts
@@ -8,7 +8,7 @@ import { CommonModule } from '@angular/common';
import { ChangeDetectionStrategy, Component, input, output } from '@angular/core';
import { SortOrder } from '@osf/shared/enums/sort-order.enum';
-import { MyResourcesItem } from '@osf/shared/models/my-resources/my-resources.models';
+import { MyResourcesItem } from '@osf/shared/models/my-resources/my-resources.model';
import { TableParameters } from '@osf/shared/models/table-parameters.model';
import { ContributorsListShortenerComponent } from '../contributors-list-shortener/contributors-list-shortener.component';
diff --git a/src/app/shared/components/pie-chart/pie-chart.component.spec.ts b/src/app/shared/components/pie-chart/pie-chart.component.spec.ts
index 9d0f4ef4a..0990a79d6 100644
--- a/src/app/shared/components/pie-chart/pie-chart.component.spec.ts
+++ b/src/app/shared/components/pie-chart/pie-chart.component.spec.ts
@@ -5,7 +5,7 @@ import { ChartModule } from 'primeng/chart';
import { PLATFORM_ID } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { DatasetInput } from '@shared/models/charts/dataset-input';
+import { DatasetInput } from '@osf/shared/models/charts/dataset-input.model';
import { LoadingSpinnerComponent } from '../loading-spinner/loading-spinner.component';
diff --git a/src/app/shared/components/pie-chart/pie-chart.component.ts b/src/app/shared/components/pie-chart/pie-chart.component.ts
index 2c36e139f..a1c8968a6 100644
--- a/src/app/shared/components/pie-chart/pie-chart.component.ts
+++ b/src/app/shared/components/pie-chart/pie-chart.component.ts
@@ -15,7 +15,7 @@ import {
} from '@angular/core';
import { PIE_CHART_PALETTE } from '@osf/shared/constants/pie-chart-palette';
-import { DatasetInput } from '@shared/models/charts/dataset-input';
+import { DatasetInput } from '@osf/shared/models/charts/dataset-input.model';
import { LoadingSpinnerComponent } from '../loading-spinner/loading-spinner.component';
diff --git a/src/app/shared/components/project-selector/project-selector.component.ts b/src/app/shared/components/project-selector/project-selector.component.ts
index d150dc205..5cd711e0f 100644
--- a/src/app/shared/components/project-selector/project-selector.component.ts
+++ b/src/app/shared/components/project-selector/project-selector.component.ts
@@ -22,7 +22,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { FormsModule } from '@angular/forms';
import { UserSelectors } from '@core/store/user';
-import { ProjectModel } from '@shared/models/projects/projects.models';
+import { ProjectModel } from '@osf/shared/models/projects/projects.model';
import { CustomOption } from '@shared/models/select-option.model';
import { GetProjects, ProjectsSelectors } from '@shared/stores/projects';
diff --git a/src/app/shared/constants/addon-terms.const.ts b/src/app/shared/constants/addon-terms.const.ts
index 936331982..18eeda651 100644
--- a/src/app/shared/constants/addon-terms.const.ts
+++ b/src/app/shared/constants/addon-terms.const.ts
@@ -1,4 +1,4 @@
-import { Term } from '../models/addons/addon-utils.models';
+import { Term } from '../models/addons/addon-utils.model';
export const ADDON_TERMS: Term[] = [
{
diff --git a/src/app/shared/helpers/search-total-count.helper.ts b/src/app/shared/helpers/search-total-count.helper.ts
index 49ede8e45..48a236f1f 100644
--- a/src/app/shared/helpers/search-total-count.helper.ts
+++ b/src/app/shared/helpers/search-total-count.helper.ts
@@ -1,4 +1,4 @@
-import { IndexCardSearchResponseJsonApi } from '../models/search/index-card-search-json-api.models';
+import { IndexCardSearchResponseJsonApi } from '../models/search/index-card-search-json-api.model';
export function parseSearchTotalCount(response: IndexCardSearchResponseJsonApi): number {
let totalCount = 0;
diff --git a/src/app/shared/mappers/addon.mapper.ts b/src/app/shared/mappers/addon.mapper.ts
index 3b6616c39..65d90eece 100644
--- a/src/app/shared/mappers/addon.mapper.ts
+++ b/src/app/shared/mappers/addon.mapper.ts
@@ -6,11 +6,11 @@ import {
AuthorizedAddonGetResponseJsonApi,
ConfiguredAddonGetResponseJsonApi,
IncludedAddonData,
-} from '../models/addons/addon-json-api.models';
+} from '../models/addons/addon-json-api.model';
import {
OperationInvocationResponseJsonApi,
StorageItemResponseJsonApi,
-} from '../models/addons/addon-operations-json-api.models';
+} from '../models/addons/addon-operations-json-api.model';
import { AuthorizedAccountModel } from '../models/addons/authorized-account.model';
import { ConfiguredAddonModel } from '../models/addons/configured-addon.model';
import { OperationInvocation } from '../models/addons/operation-invocation.model';
diff --git a/src/app/shared/mappers/collections/collections.mapper.ts b/src/app/shared/mappers/collections/collections.mapper.ts
index d1fd54ca5..227fbe021 100644
--- a/src/app/shared/mappers/collections/collections.mapper.ts
+++ b/src/app/shared/mappers/collections/collections.mapper.ts
@@ -11,13 +11,13 @@ import {
CollectionProvider,
CollectionSubmission,
CollectionSubmissionWithGuid,
-} from '@osf/shared/models/collections/collections.models';
+} from '@osf/shared/models/collections/collections.model';
import {
CollectionDetailsResponseJsonApi,
CollectionProviderResponseJsonApi,
CollectionSubmissionJsonApi,
CollectionSubmissionWithGuidJsonApi,
-} from '@osf/shared/models/collections/collections-json-api.models';
+} from '@osf/shared/models/collections/collections-json-api.model';
import { ResponseJsonApi } from '@osf/shared/models/common/json-api.model';
import { ContributorModel } from '@osf/shared/models/contributors/contributor.model';
import { PaginatedData } from '@osf/shared/models/paginated-data.model';
diff --git a/src/app/shared/mappers/filters/filter-option.mapper.ts b/src/app/shared/mappers/filters/filter-option.mapper.ts
index 21fd54c13..df77250b3 100644
--- a/src/app/shared/mappers/filters/filter-option.mapper.ts
+++ b/src/app/shared/mappers/filters/filter-option.mapper.ts
@@ -1,6 +1,6 @@
import { FilterOption } from '@osf/shared/models/search/discaverable-filter.model';
-import { FilterOptionItem } from '@osf/shared/models/search/filter-options-json-api.models';
-import { SearchResultDataJsonApi } from '@osf/shared/models/search/index-card-search-json-api.models';
+import { FilterOptionItem } from '@osf/shared/models/search/filter-options-json-api.model';
+import { SearchResultDataJsonApi } from '@osf/shared/models/search/index-card-search-json-api.model';
export function mapFilterOptions(
searchResultItems: SearchResultDataJsonApi[],
diff --git a/src/app/shared/mappers/filters/filters.mapper.ts b/src/app/shared/mappers/filters/filters.mapper.ts
index dde219558..6cee8c2d6 100644
--- a/src/app/shared/mappers/filters/filters.mapper.ts
+++ b/src/app/shared/mappers/filters/filters.mapper.ts
@@ -2,7 +2,7 @@ import { DiscoverableFilter, FilterOperatorOption } from '@osf/shared/models/sea
import {
IndexCardSearchResponseJsonApi,
RelatedPropertyPathDataJsonApi,
-} from '@osf/shared/models/search/index-card-search-json-api.models';
+} from '@osf/shared/models/search/index-card-search-json-api.model';
export function MapFilters(indexCardSearchResponseJsonApi: IndexCardSearchResponseJsonApi): DiscoverableFilter[] {
const relatedPropertiesIds = indexCardSearchResponseJsonApi.data.relationships.relatedProperties.data.map(
diff --git a/src/app/shared/mappers/institutions/institutions.mapper.ts b/src/app/shared/mappers/institutions/institutions.mapper.ts
index 66642d805..f3552702a 100644
--- a/src/app/shared/mappers/institutions/institutions.mapper.ts
+++ b/src/app/shared/mappers/institutions/institutions.mapper.ts
@@ -3,7 +3,7 @@ import {
InstitutionsJsonApiResponse,
InstitutionsWithMetaJsonApiResponse,
} from '@osf/shared/models/institutions/institution-json-api.model';
-import { Institution, InstitutionsWithTotalCount } from '@osf/shared/models/institutions/institutions.models';
+import { Institution, InstitutionsWithTotalCount } from '@osf/shared/models/institutions/institutions.model';
import { replaceBadEncodedChars } from '@shared/helpers/format-bad-encoding.helper';
export class InstitutionsMapper {
diff --git a/src/app/shared/mappers/my-resources.mapper.ts b/src/app/shared/mappers/my-resources.mapper.ts
index 7a2a5d5d2..d7caf4a08 100644
--- a/src/app/shared/mappers/my-resources.mapper.ts
+++ b/src/app/shared/mappers/my-resources.mapper.ts
@@ -1,6 +1,6 @@
import { replaceBadEncodedChars } from '@shared/helpers/format-bad-encoding.helper';
-import { MyResourcesItem, MyResourcesItemGetResponseJsonApi } from '../models/my-resources/my-resources.models';
+import { MyResourcesItem, MyResourcesItemGetResponseJsonApi } from '../models/my-resources/my-resources.model';
import { ContributorsMapper } from './contributors';
diff --git a/src/app/shared/mappers/projects/projects.mapper.ts b/src/app/shared/mappers/projects/projects.mapper.ts
index 26140a3b9..5f6ff0ac5 100644
--- a/src/app/shared/mappers/projects/projects.mapper.ts
+++ b/src/app/shared/mappers/projects/projects.mapper.ts
@@ -1,8 +1,8 @@
-import { CollectionSubmissionMetadataPayloadJsonApi } from '@osf/features/collections/models/collection-license-json-api.models';
+import { CollectionSubmissionMetadataPayloadJsonApi } from '@osf/features/collections/models/collection-license-json-api.model';
import { BaseNodeDataJsonApi } from '@osf/shared/models/nodes/base-node-data-json-api.model';
import { NodesResponseJsonApi } from '@osf/shared/models/nodes/nodes-json-api.model';
import { ProjectMetadataUpdatePayload } from '@osf/shared/models/project-metadata-update-payload.model';
-import { ProjectModel } from '@osf/shared/models/projects/projects.models';
+import { ProjectModel } from '@osf/shared/models/projects/projects.model';
import { replaceBadEncodedChars } from '@shared/helpers/format-bad-encoding.helper';
export class ProjectsMapper {
diff --git a/src/app/shared/mappers/search/search.mapper.ts b/src/app/shared/mappers/search/search.mapper.ts
index e0aad24e9..876048744 100644
--- a/src/app/shared/mappers/search/search.mapper.ts
+++ b/src/app/shared/mappers/search/search.mapper.ts
@@ -1,10 +1,10 @@
-import { ResourceType } from '@shared/enums/resource-type.enum';
-import { replaceBadEncodedChars } from '@shared/helpers/format-bad-encoding.helper';
import {
IndexCardDataJsonApi,
IndexCardSearchResponseJsonApi,
SearchResultDataJsonApi,
-} from '@shared/models/search/index-card-search-json-api.models';
+} from '@osf/shared/models/search/index-card-search-json-api.model';
+import { ResourceType } from '@shared/enums/resource-type.enum';
+import { replaceBadEncodedChars } from '@shared/helpers/format-bad-encoding.helper';
import { ResourceModel } from '@shared/models/search/resource.model';
export function MapResources(indexCardSearchResponseJsonApi: IndexCardSearchResponseJsonApi): ResourceModel[] {
diff --git a/src/app/shared/mappers/user/user.mapper.ts b/src/app/shared/mappers/user/user.mapper.ts
index 2a735dd38..f3df82497 100644
--- a/src/app/shared/mappers/user/user.mapper.ts
+++ b/src/app/shared/mappers/user/user.mapper.ts
@@ -1,4 +1,4 @@
-import { UserData, UserModel } from '@osf/shared/models/user/user.models';
+import { UserData, UserModel } from '@osf/shared/models/user/user.model';
import {
UserAcceptedTermsOfServiceJsonApi,
UserAttributesJsonApi,
diff --git a/src/app/shared/models/addons/addon-json-api.models.ts b/src/app/shared/models/addons/addon-json-api.model.ts
similarity index 100%
rename from src/app/shared/models/addons/addon-json-api.models.ts
rename to src/app/shared/models/addons/addon-json-api.model.ts
diff --git a/src/app/shared/models/addons/addon-operations-json-api.models.ts b/src/app/shared/models/addons/addon-operations-json-api.model.ts
similarity index 100%
rename from src/app/shared/models/addons/addon-operations-json-api.models.ts
rename to src/app/shared/models/addons/addon-operations-json-api.model.ts
diff --git a/src/app/shared/models/addons/addon-utils.models.ts b/src/app/shared/models/addons/addon-utils.model.ts
similarity index 100%
rename from src/app/shared/models/addons/addon-utils.models.ts
rename to src/app/shared/models/addons/addon-utils.model.ts
diff --git a/src/app/shared/models/charts/dataset-input.ts b/src/app/shared/models/charts/dataset-input.model.ts
similarity index 100%
rename from src/app/shared/models/charts/dataset-input.ts
rename to src/app/shared/models/charts/dataset-input.model.ts
diff --git a/src/app/shared/models/collections/collections-json-api.models.ts b/src/app/shared/models/collections/collections-json-api.model.ts
similarity index 100%
rename from src/app/shared/models/collections/collections-json-api.models.ts
rename to src/app/shared/models/collections/collections-json-api.model.ts
diff --git a/src/app/shared/models/collections/collections.models.ts b/src/app/shared/models/collections/collections.model.ts
similarity index 97%
rename from src/app/shared/models/collections/collections.models.ts
rename to src/app/shared/models/collections/collections.model.ts
index c130c21f1..5b27a3bff 100644
--- a/src/app/shared/models/collections/collections.models.ts
+++ b/src/app/shared/models/collections/collections.model.ts
@@ -3,7 +3,7 @@ import { CollectionSubmissionReviewState } from '@osf/shared/enums/collection-su
import { BrandModel } from '../brand/brand.model';
import { ContributorModel } from '../contributors/contributor.model';
-import { ProjectModel } from '../projects/projects.models';
+import { ProjectModel } from '../projects/projects.model';
import { BaseProviderModel } from '../provider/provider.model';
export interface CollectionProvider extends BaseProviderModel {
diff --git a/src/app/shared/models/institutions/institution-json-api.model.ts b/src/app/shared/models/institutions/institution-json-api.model.ts
index 16f4b4226..cedc24e36 100644
--- a/src/app/shared/models/institutions/institution-json-api.model.ts
+++ b/src/app/shared/models/institutions/institution-json-api.model.ts
@@ -1,6 +1,6 @@
import { ApiData, JsonApiResponse, ResponseJsonApi } from '../common/json-api.model';
-import { InstitutionAssets } from './institutions.models';
+import { InstitutionAssets } from './institutions.model';
export type InstitutionsJsonApiResponse = JsonApiResponse;
export type InstitutionsWithMetaJsonApiResponse = ResponseJsonApi;
diff --git a/src/app/shared/models/institutions/institutions.models.ts b/src/app/shared/models/institutions/institutions.model.ts
similarity index 100%
rename from src/app/shared/models/institutions/institutions.models.ts
rename to src/app/shared/models/institutions/institutions.model.ts
diff --git a/src/app/shared/models/license/license-form.models.ts b/src/app/shared/models/license/license-form.model.ts
similarity index 100%
rename from src/app/shared/models/license/license-form.models.ts
rename to src/app/shared/models/license/license-form.model.ts
diff --git a/src/app/shared/models/my-resources/my-resources-search-filters.models.ts b/src/app/shared/models/my-resources/my-resources-search-filters.model.ts
similarity index 100%
rename from src/app/shared/models/my-resources/my-resources-search-filters.models.ts
rename to src/app/shared/models/my-resources/my-resources-search-filters.model.ts
diff --git a/src/app/shared/models/my-resources/my-resources.models.ts b/src/app/shared/models/my-resources/my-resources.model.ts
similarity index 100%
rename from src/app/shared/models/my-resources/my-resources.models.ts
rename to src/app/shared/models/my-resources/my-resources.model.ts
diff --git a/src/app/shared/models/profile-settings-update.model.ts b/src/app/shared/models/profile-settings-update.model.ts
index a80257609..f87f5c7c8 100644
--- a/src/app/shared/models/profile-settings-update.model.ts
+++ b/src/app/shared/models/profile-settings-update.model.ts
@@ -1,7 +1,7 @@
import { Education } from './user/education.model';
import { Employment } from './user/employment.model';
import { SocialModel } from './user/social.model';
-import { UserModel } from './user/user.models';
+import { UserModel } from './user/user.model';
export type ProfileSettingsUpdate =
| Partial[]
diff --git a/src/app/shared/models/projects/projects.models.ts b/src/app/shared/models/projects/projects.model.ts
similarity index 100%
rename from src/app/shared/models/projects/projects.models.ts
rename to src/app/shared/models/projects/projects.model.ts
diff --git a/src/app/shared/models/registration/draft-registration.model.ts b/src/app/shared/models/registration/draft-registration.model.ts
index 4a18222ac..7ae2a5a0a 100644
--- a/src/app/shared/models/registration/draft-registration.model.ts
+++ b/src/app/shared/models/registration/draft-registration.model.ts
@@ -1,7 +1,7 @@
import { UserPermissions } from '@shared/enums/user-permissions.enum';
import { LicenseOptions } from '../license/license.model';
-import { ProjectModel } from '../projects/projects.models';
+import { ProjectModel } from '../projects/projects.model';
export interface DraftRegistrationModel {
id: string;
diff --git a/src/app/shared/models/request-access/request-access.model.ts b/src/app/shared/models/request-access/request-access.model.ts
index 0b3b5cf39..cf987ae92 100644
--- a/src/app/shared/models/request-access/request-access.model.ts
+++ b/src/app/shared/models/request-access/request-access.model.ts
@@ -1,6 +1,6 @@
import { ContributorPermission } from '@osf/shared/enums/contributors/contributor-permission.enum';
-import { UserModel } from '../user/user.models';
+import { UserModel } from '../user/user.model';
export interface RequestAccessModel {
id: string;
diff --git a/src/app/shared/models/search/filter-options-json-api.models.ts b/src/app/shared/models/search/filter-options-json-api.model.ts
similarity index 98%
rename from src/app/shared/models/search/filter-options-json-api.models.ts
rename to src/app/shared/models/search/filter-options-json-api.model.ts
index d9de2d9dd..dbcf6ba9a 100644
--- a/src/app/shared/models/search/filter-options-json-api.models.ts
+++ b/src/app/shared/models/search/filter-options-json-api.model.ts
@@ -1,6 +1,6 @@
import { ApiData } from '../common/json-api.model';
-import { SearchResultDataJsonApi } from './index-card-search-json-api.models';
+import { SearchResultDataJsonApi } from './index-card-search-json-api.model';
export interface FilterOptionsResponseJsonApi {
data: FilterOptionsResponseData;
diff --git a/src/app/shared/models/search/index-card-search-json-api.models.ts b/src/app/shared/models/search/index-card-search-json-api.model.ts
similarity index 100%
rename from src/app/shared/models/search/index-card-search-json-api.models.ts
rename to src/app/shared/models/search/index-card-search-json-api.model.ts
diff --git a/src/app/shared/models/user/user.models.ts b/src/app/shared/models/user/user.model.ts
similarity index 100%
rename from src/app/shared/models/user/user.models.ts
rename to src/app/shared/models/user/user.model.ts
diff --git a/src/app/shared/pipes/citation-format.pipe.ts b/src/app/shared/pipes/citation-format.pipe.ts
index bee6f5c2f..a9851bb93 100644
--- a/src/app/shared/pipes/citation-format.pipe.ts
+++ b/src/app/shared/pipes/citation-format.pipe.ts
@@ -1,6 +1,6 @@
import { Pipe, PipeTransform } from '@angular/core';
-import { UserModel } from '@osf/shared/models/user/user.models';
+import { UserModel } from '@osf/shared/models/user/user.model';
import { GENERATIONAL_SUFFIXES, ORDINAL_SUFFIXES } from '../constants/citation-suffix.const';
diff --git a/src/app/shared/services/addons/addon-form.service.ts b/src/app/shared/services/addons/addon-form.service.ts
index ce0fde96a..e02cf926b 100644
--- a/src/app/shared/services/addons/addon-form.service.ts
+++ b/src/app/shared/services/addons/addon-form.service.ts
@@ -5,12 +5,12 @@ import { AddonFormControls } from '@osf/shared/enums/addon-form-controls.enum';
import { AddonType } from '@osf/shared/enums/addon-type.enum';
import { CredentialsFormat } from '@osf/shared/enums/addons-credentials-format.enum';
import { isAuthorizedAddon } from '@osf/shared/helpers/addon-type.helper';
-import { AddonModel } from '@shared/models/addons/addon.model';
import {
AuthorizedAddonRequestJsonApi,
ConfiguredAddonRequestJsonApi,
-} from '@shared/models/addons/addon-json-api.models';
-import { AddonForm } from '@shared/models/addons/addon-utils.models';
+} from '@osf/shared/models/addons/addon-json-api.model';
+import { AddonForm } from '@osf/shared/models/addons/addon-utils.model';
+import { AddonModel } from '@shared/models/addons/addon.model';
import { AuthorizedAccountModel } from '@shared/models/addons/authorized-account.model';
import { ConfiguredAddonModel } from '@shared/models/addons/configured-addon.model';
diff --git a/src/app/shared/services/addons/addon-oauth.service.ts b/src/app/shared/services/addons/addon-oauth.service.ts
index 3ebfaae3c..f04eb88a3 100644
--- a/src/app/shared/services/addons/addon-oauth.service.ts
+++ b/src/app/shared/services/addons/addon-oauth.service.ts
@@ -4,7 +4,7 @@ import { isPlatformBrowser } from '@angular/common';
import { DestroyRef, inject, Injectable, PLATFORM_ID, signal } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
-import { OAuthCallbacks } from '@osf/shared/models/addons/addon-utils.models';
+import { OAuthCallbacks } from '@osf/shared/models/addons/addon-utils.model';
import { AuthorizedAccountModel } from '@osf/shared/models/addons/authorized-account.model';
import { AddonsSelectors, DeleteAuthorizedAddon, GetAuthorizedStorageOauthToken } from '@osf/shared/stores/addons';
diff --git a/src/app/shared/services/addons/addon-operation-invocation.service.ts b/src/app/shared/services/addons/addon-operation-invocation.service.ts
index 193f3cb57..8b0e3db38 100644
--- a/src/app/shared/services/addons/addon-operation-invocation.service.ts
+++ b/src/app/shared/services/addons/addon-operation-invocation.service.ts
@@ -3,7 +3,7 @@ import { Injectable } from '@angular/core';
import { OperationNames } from '@osf/shared/enums/operation-names.enum';
import { StorageItemType } from '@osf/shared/enums/storage-item-type.enum';
import { isCitationAddon } from '@osf/shared/helpers/addon-type.helper';
-import { OperationInvocationRequestJsonApi } from '@shared/models/addons/addon-operations-json-api.models';
+import { OperationInvocationRequestJsonApi } from '@osf/shared/models/addons/addon-operations-json-api.model';
import { AuthorizedAccountModel } from '@shared/models/addons/authorized-account.model';
import { ConfiguredAddonModel } from '@shared/models/addons/configured-addon.model';
diff --git a/src/app/shared/services/addons/addons.service.ts b/src/app/shared/services/addons/addons.service.ts
index 0ebb3460b..fb574b5fb 100644
--- a/src/app/shared/services/addons/addons.service.ts
+++ b/src/app/shared/services/addons/addons.service.ts
@@ -21,11 +21,11 @@ import {
IncludedAddonData,
ResourceReferenceJsonApi,
UserReferenceJsonApi,
-} from '@osf/shared/models/addons/addon-json-api.models';
+} from '@osf/shared/models/addons/addon-json-api.model';
import {
OperationInvocationRequestJsonApi,
OperationInvocationResponseJsonApi,
-} from '@osf/shared/models/addons/addon-operations-json-api.models';
+} from '@osf/shared/models/addons/addon-operations-json-api.model';
import { AuthorizedAccountModel } from '@osf/shared/models/addons/authorized-account.model';
import { ConfiguredAddonModel } from '@osf/shared/models/addons/configured-addon.model';
import { OperationInvocation } from '@osf/shared/models/addons/operation-invocation.model';
diff --git a/src/app/shared/services/bookmarks.service.ts b/src/app/shared/services/bookmarks.service.ts
index 8fa7885a0..be8b29daf 100644
--- a/src/app/shared/services/bookmarks.service.ts
+++ b/src/app/shared/services/bookmarks.service.ts
@@ -7,13 +7,13 @@ import { ENVIRONMENT } from '@core/provider/environment.provider';
import { ResourceType } from '../enums/resource-type.enum';
import { SortOrder } from '../enums/sort-order.enum';
import { MyResourcesMapper } from '../mappers/my-resources.mapper';
-import { SparseCollectionsResponseJsonApi } from '../models/collections/collections-json-api.models';
+import { SparseCollectionsResponseJsonApi } from '../models/collections/collections-json-api.model';
import {
MyResourcesItem,
MyResourcesItemGetResponseJsonApi,
MyResourcesResponseJsonApi,
-} from '../models/my-resources/my-resources.models';
-import { MyResourcesSearchFilters } from '../models/my-resources/my-resources-search-filters.models';
+} from '../models/my-resources/my-resources.model';
+import { MyResourcesSearchFilters } from '../models/my-resources/my-resources-search-filters.model';
import { PaginatedData } from '../models/paginated-data.model';
import { JsonApiService } from './json-api.service';
diff --git a/src/app/shared/services/collections.service.ts b/src/app/shared/services/collections.service.ts
index 6424e0f44..8b13f253a 100644
--- a/src/app/shared/services/collections.service.ts
+++ b/src/app/shared/services/collections.service.ts
@@ -23,7 +23,7 @@ import {
CollectionSubmissionActionType,
CollectionSubmissionTargetType,
CollectionSubmissionWithGuid,
-} from '../models/collections/collections.models';
+} from '../models/collections/collections.model';
import {
CollectionDetailsGetResponseJsonApi,
CollectionDetailsResponseJsonApi,
@@ -31,7 +31,7 @@ import {
CollectionSubmissionJsonApi,
CollectionSubmissionsSearchPayloadJsonApi,
CollectionSubmissionWithGuidJsonApi,
-} from '../models/collections/collections-json-api.models';
+} from '../models/collections/collections-json-api.model';
import { JsonApiResponse, ResponseJsonApi } from '../models/common/json-api.model';
import { ContributorModel } from '../models/contributors/contributor.model';
import { ContributorsResponseJsonApi } from '../models/contributors/contributor-response-json-api.model';
diff --git a/src/app/shared/services/contributors.service.ts b/src/app/shared/services/contributors.service.ts
index 3ab5d6145..80b123dcd 100644
--- a/src/app/shared/services/contributors.service.ts
+++ b/src/app/shared/services/contributors.service.ts
@@ -14,7 +14,7 @@ import { ContributorModel } from '../models/contributors/contributor.model';
import { ContributorAddModel } from '../models/contributors/contributor-add.model';
import { ContributorsResponseJsonApi } from '../models/contributors/contributor-response-json-api.model';
import { PaginatedData } from '../models/paginated-data.model';
-import { IndexCardSearchResponseJsonApi } from '../models/search/index-card-search-json-api.models';
+import { IndexCardSearchResponseJsonApi } from '../models/search/index-card-search-json-api.model';
import { SearchUserDataModel } from '../models/user/search-user-data.model';
import { JsonApiService } from './json-api.service';
diff --git a/src/app/shared/services/files.service.ts b/src/app/shared/services/files.service.ts
index 0fd3306e0..e980fbe56 100644
--- a/src/app/shared/services/files.service.ts
+++ b/src/app/shared/services/files.service.ts
@@ -23,7 +23,7 @@ import { AddonMapper } from '../mappers/addon.mapper';
import { ContributorsMapper } from '../mappers/contributors';
import { FilesMapper } from '../mappers/files/files.mapper';
import { AddonModel } from '../models/addons/addon.model';
-import { AddonGetResponseJsonApi, ConfiguredAddonGetResponseJsonApi } from '../models/addons/addon-json-api.models';
+import { AddonGetResponseJsonApi, ConfiguredAddonGetResponseJsonApi } from '../models/addons/addon-json-api.model';
import { ConfiguredAddonModel } from '../models/addons/configured-addon.model';
import { ApiData, JsonApiResponse, MetaJsonApi } from '../models/common/json-api.model';
import { ContributorModel } from '../models/contributors/contributor.model';
diff --git a/src/app/shared/services/global-search.service.ts b/src/app/shared/services/global-search.service.ts
index fa2f73cf0..626609c6e 100644
--- a/src/app/shared/services/global-search.service.ts
+++ b/src/app/shared/services/global-search.service.ts
@@ -9,11 +9,11 @@ import { mapFilterOptions } from '../mappers/filters/filter-option.mapper';
import { MapFilters } from '../mappers/filters/filters.mapper';
import { MapResources } from '../mappers/search';
import { FilterOption } from '../models/search/discaverable-filter.model';
-import { FilterOptionItem, FilterOptionsResponseJsonApi } from '../models/search/filter-options-json-api.models';
+import { FilterOptionItem, FilterOptionsResponseJsonApi } from '../models/search/filter-options-json-api.model';
import {
IndexCardSearchResponseJsonApi,
SearchResultDataJsonApi,
-} from '../models/search/index-card-search-json-api.models';
+} from '../models/search/index-card-search-json-api.model';
import { ResourcesData } from '../models/search/resource.model';
import { JsonApiService } from './json-api.service';
diff --git a/src/app/shared/services/institutions.service.ts b/src/app/shared/services/institutions.service.ts
index b90fd89ea..9fa886e97 100644
--- a/src/app/shared/services/institutions.service.ts
+++ b/src/app/shared/services/institutions.service.ts
@@ -12,7 +12,7 @@ import {
InstitutionsJsonApiResponse,
InstitutionsWithMetaJsonApiResponse,
} from '../models/institutions/institution-json-api.model';
-import { Institution, InstitutionsWithTotalCount } from '../models/institutions/institutions.models';
+import { Institution, InstitutionsWithTotalCount } from '../models/institutions/institutions.model';
import { JsonApiService } from './json-api.service';
diff --git a/src/app/shared/services/my-resources.service.ts b/src/app/shared/services/my-resources.service.ts
index b36e20f37..27b4159cd 100644
--- a/src/app/shared/services/my-resources.service.ts
+++ b/src/app/shared/services/my-resources.service.ts
@@ -14,9 +14,9 @@ import {
MyResourcesItemGetResponseJsonApi,
MyResourcesItemResponseJsonApi,
MyResourcesResponseJsonApi,
-} from '../models/my-resources/my-resources.models';
+} from '../models/my-resources/my-resources.model';
import { EndpointType } from '../models/my-resources/my-resources-endpoint.type';
-import { MyResourcesSearchFilters } from '../models/my-resources/my-resources-search-filters.models';
+import { MyResourcesSearchFilters } from '../models/my-resources/my-resources-search-filters.model';
import { CreateProjectPayloadJsoApi } from '../models/nodes/nodes-json-api.model';
import { JsonApiService } from './json-api.service';
diff --git a/src/app/shared/services/node-links.service.ts b/src/app/shared/services/node-links.service.ts
index 870e9b7db..99fd68de1 100644
--- a/src/app/shared/services/node-links.service.ts
+++ b/src/app/shared/services/node-links.service.ts
@@ -7,7 +7,7 @@ import { ENVIRONMENT } from '@core/provider/environment.provider';
import { BaseNodeMapper } from '../mappers/nodes';
import { JsonApiResponse } from '../models/common/json-api.model';
-import { MyResourcesItem } from '../models/my-resources/my-resources.models';
+import { MyResourcesItem } from '../models/my-resources/my-resources.model';
import { NodeLinkJsonApi } from '../models/node-links/node-link-json-api.model';
import { NodeModel } from '../models/nodes/base-node.model';
import { NodesResponseJsonApi } from '../models/nodes/nodes-json-api.model';
diff --git a/src/app/shared/services/projects.service.ts b/src/app/shared/services/projects.service.ts
index fba349ed6..dffc0054d 100644
--- a/src/app/shared/services/projects.service.ts
+++ b/src/app/shared/services/projects.service.ts
@@ -8,7 +8,7 @@ import { ProjectsMapper } from '../mappers/projects';
import { BaseNodeDataJsonApi } from '../models/nodes/base-node-data-json-api.model';
import { NodesResponseJsonApi } from '../models/nodes/nodes-json-api.model';
import { ProjectMetadataUpdatePayload } from '../models/project-metadata-update-payload.model';
-import { ProjectModel } from '../models/projects/projects.models';
+import { ProjectModel } from '../models/projects/projects.model';
import { JsonApiService } from './json-api.service';
diff --git a/src/app/shared/stores/addons/addons.actions.ts b/src/app/shared/stores/addons/addons.actions.ts
index 17a9c852a..05b79735f 100644
--- a/src/app/shared/stores/addons/addons.actions.ts
+++ b/src/app/shared/stores/addons/addons.actions.ts
@@ -1,8 +1,8 @@
import {
AuthorizedAddonRequestJsonApi,
ConfiguredAddonRequestJsonApi,
-} from '@osf/shared/models/addons/addon-json-api.models';
-import { OperationInvocationRequestJsonApi } from '@osf/shared/models/addons/addon-operations-json-api.models';
+} from '@osf/shared/models/addons/addon-json-api.model';
+import { OperationInvocationRequestJsonApi } from '@osf/shared/models/addons/addon-operations-json-api.model';
export class GetStorageAddons {
static readonly type = '[Addons] Get Storage Addons';
diff --git a/src/app/shared/stores/addons/addons.models.ts b/src/app/shared/stores/addons/addons.models.ts
index 14b4b37f5..a5ce2675c 100644
--- a/src/app/shared/stores/addons/addons.models.ts
+++ b/src/app/shared/stores/addons/addons.models.ts
@@ -3,7 +3,7 @@ import {
ConfiguredAddonResponseJsonApi,
ResourceReferenceJsonApi,
UserReferenceJsonApi,
-} from '@osf/shared/models/addons/addon-json-api.models';
+} from '@osf/shared/models/addons/addon-json-api.model';
import { AuthorizedAccountModel } from '@osf/shared/models/addons/authorized-account.model';
import { ConfiguredAddonModel } from '@osf/shared/models/addons/configured-addon.model';
import { OperationInvocation } from '@osf/shared/models/addons/operation-invocation.model';
diff --git a/src/app/shared/stores/addons/addons.selectors.ts b/src/app/shared/stores/addons/addons.selectors.ts
index a552ca63a..8cf6d8cad 100644
--- a/src/app/shared/stores/addons/addons.selectors.ts
+++ b/src/app/shared/stores/addons/addons.selectors.ts
@@ -5,7 +5,7 @@ import {
ConfiguredAddonResponseJsonApi,
ResourceReferenceJsonApi,
UserReferenceJsonApi,
-} from '@osf/shared/models/addons/addon-json-api.models';
+} from '@osf/shared/models/addons/addon-json-api.model';
import { AuthorizedAccountModel } from '@osf/shared/models/addons/authorized-account.model';
import { ConfiguredAddonModel } from '@osf/shared/models/addons/configured-addon.model';
import { OperationInvocation } from '@osf/shared/models/addons/operation-invocation.model';
diff --git a/src/app/shared/stores/bookmarks/bookmarks.actions.ts b/src/app/shared/stores/bookmarks/bookmarks.actions.ts
index 12759a916..3dcd59733 100644
--- a/src/app/shared/stores/bookmarks/bookmarks.actions.ts
+++ b/src/app/shared/stores/bookmarks/bookmarks.actions.ts
@@ -1,5 +1,5 @@
+import { MyResourcesSearchFilters } from '@osf/shared/models/my-resources/my-resources-search-filters.model';
import { ResourceType } from '@shared/enums/resource-type.enum';
-import { MyResourcesSearchFilters } from '@shared/models/my-resources/my-resources-search-filters.models';
export class GetBookmarksCollectionId {
static readonly type = '[Bookmarks] Get Bookmarks Collection Id';
diff --git a/src/app/shared/stores/bookmarks/bookmarks.model.ts b/src/app/shared/stores/bookmarks/bookmarks.model.ts
index 919d2b347..66a469992 100644
--- a/src/app/shared/stores/bookmarks/bookmarks.model.ts
+++ b/src/app/shared/stores/bookmarks/bookmarks.model.ts
@@ -1,4 +1,4 @@
-import { MyResourcesItem } from '@osf/shared/models/my-resources/my-resources.models';
+import { MyResourcesItem } from '@osf/shared/models/my-resources/my-resources.model';
import { AsyncStateModel } from '@osf/shared/models/store/async-state.model';
import { AsyncStateWithTotalCount } from '@osf/shared/models/store/async-state-with-total-count.model';
diff --git a/src/app/shared/stores/collections/collections.model.ts b/src/app/shared/stores/collections/collections.model.ts
index 8e17d8581..8ad7f5035 100644
--- a/src/app/shared/stores/collections/collections.model.ts
+++ b/src/app/shared/stores/collections/collections.model.ts
@@ -3,7 +3,7 @@ import {
CollectionProvider,
CollectionSubmission,
CollectionSubmissionWithGuid,
-} from '@osf/shared/models/collections/collections.models';
+} from '@osf/shared/models/collections/collections.model';
import { CollectionsFilters } from '@osf/shared/models/collections/collections-filters.model';
import { AsyncStateModel } from '@osf/shared/models/store/async-state.model';
diff --git a/src/app/shared/stores/institutions-search/institutions-search.model.ts b/src/app/shared/stores/institutions-search/institutions-search.model.ts
index c319194e2..383f424d8 100644
--- a/src/app/shared/stores/institutions-search/institutions-search.model.ts
+++ b/src/app/shared/stores/institutions-search/institutions-search.model.ts
@@ -1,4 +1,4 @@
-import { Institution } from '@osf/shared/models/institutions/institutions.models';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
import { AsyncStateModel } from '@osf/shared/models/store/async-state.model';
export interface InstitutionsSearchModel {
diff --git a/src/app/shared/stores/institutions-search/institutions-search.state.ts b/src/app/shared/stores/institutions-search/institutions-search.state.ts
index 0600b0231..192df7d87 100644
--- a/src/app/shared/stores/institutions-search/institutions-search.state.ts
+++ b/src/app/shared/stores/institutions-search/institutions-search.state.ts
@@ -6,7 +6,7 @@ import { catchError, tap } from 'rxjs';
import { inject, Injectable } from '@angular/core';
import { handleSectionError } from '@osf/shared/helpers/state-error.handler';
-import { Institution } from '@osf/shared/models/institutions/institutions.models';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
import { InstitutionsService } from '@osf/shared/services/institutions.service';
import { FetchInstitutionById } from './institutions-search.actions';
diff --git a/src/app/shared/stores/institutions/institutions.actions.ts b/src/app/shared/stores/institutions/institutions.actions.ts
index 4e7790f79..85b19d02e 100644
--- a/src/app/shared/stores/institutions/institutions.actions.ts
+++ b/src/app/shared/stores/institutions/institutions.actions.ts
@@ -1,5 +1,5 @@
import { ResourceType } from '@osf/shared/enums/resource-type.enum';
-import { Institution } from '@shared/models/institutions/institutions.models';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
export class FetchUserInstitutions {
static readonly type = '[Institutions] Fetch User Institutions';
diff --git a/src/app/shared/stores/institutions/institutions.model.ts b/src/app/shared/stores/institutions/institutions.model.ts
index 984d18a5e..939933d39 100644
--- a/src/app/shared/stores/institutions/institutions.model.ts
+++ b/src/app/shared/stores/institutions/institutions.model.ts
@@ -1,4 +1,4 @@
-import { Institution } from '@osf/shared/models/institutions/institutions.models';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
import { AsyncStateModel } from '@osf/shared/models/store/async-state.model';
import { AsyncStateWithTotalCount } from '@osf/shared/models/store/async-state-with-total-count.model';
diff --git a/src/app/shared/stores/my-resources/my-resources.actions.ts b/src/app/shared/stores/my-resources/my-resources.actions.ts
index b56720213..195ce94f3 100644
--- a/src/app/shared/stores/my-resources/my-resources.actions.ts
+++ b/src/app/shared/stores/my-resources/my-resources.actions.ts
@@ -1,5 +1,5 @@
import { ResourceSearchMode } from '@osf/shared/enums/resource-search-mode.enum';
-import { MyResourcesSearchFilters } from '@osf/shared/models/my-resources/my-resources-search-filters.models';
+import { MyResourcesSearchFilters } from '@osf/shared/models/my-resources/my-resources-search-filters.model';
export class GetMyProjects {
static readonly type = '[My Resources] Get Projects';
diff --git a/src/app/shared/stores/my-resources/my-resources.model.ts b/src/app/shared/stores/my-resources/my-resources.model.ts
index 47d41db9f..cfc32a040 100644
--- a/src/app/shared/stores/my-resources/my-resources.model.ts
+++ b/src/app/shared/stores/my-resources/my-resources.model.ts
@@ -1,4 +1,4 @@
-import { MyResourcesItem } from '@osf/shared/models/my-resources/my-resources.models';
+import { MyResourcesItem } from '@osf/shared/models/my-resources/my-resources.model';
import { AsyncStateWithTotalCount } from '@osf/shared/models/store/async-state-with-total-count.model';
export interface MyResourcesStateModel {
diff --git a/src/app/shared/stores/my-resources/my-resources.selectors.ts b/src/app/shared/stores/my-resources/my-resources.selectors.ts
index 344e64d3a..f3da50129 100644
--- a/src/app/shared/stores/my-resources/my-resources.selectors.ts
+++ b/src/app/shared/stores/my-resources/my-resources.selectors.ts
@@ -1,6 +1,6 @@
import { Selector } from '@ngxs/store';
-import { MyResourcesItem } from '@osf/shared/models/my-resources/my-resources.models';
+import { MyResourcesItem } from '@osf/shared/models/my-resources/my-resources.model';
import { MyResourcesStateModel } from './my-resources.model';
import { MyResourcesState } from './my-resources.state';
diff --git a/src/app/shared/stores/node-links/node-links.actions.ts b/src/app/shared/stores/node-links/node-links.actions.ts
index 9515fb1eb..52e21e0df 100644
--- a/src/app/shared/stores/node-links/node-links.actions.ts
+++ b/src/app/shared/stores/node-links/node-links.actions.ts
@@ -1,5 +1,5 @@
import { DEFAULT_TABLE_PARAMS } from '@osf/shared/constants/default-table-params.constants';
-import { MyResourcesItem } from '@osf/shared/models/my-resources/my-resources.models';
+import { MyResourcesItem } from '@osf/shared/models/my-resources/my-resources.model';
import { NodeModel } from '@osf/shared/models/nodes/base-node.model';
export class CreateNodeLink {
diff --git a/src/app/shared/stores/projects/projects.actions.ts b/src/app/shared/stores/projects/projects.actions.ts
index 6896ba6fe..1ce5e0c10 100644
--- a/src/app/shared/stores/projects/projects.actions.ts
+++ b/src/app/shared/stores/projects/projects.actions.ts
@@ -1,5 +1,5 @@
+import { ProjectModel } from '@osf/shared/models/projects/projects.model';
import { ProjectMetadataUpdatePayload } from '@shared/models/project-metadata-update-payload.model';
-import { ProjectModel } from '@shared/models/projects/projects.models';
export class GetProjects {
static readonly type = '[Projects] Get Projects';
diff --git a/src/app/shared/stores/projects/projects.model.ts b/src/app/shared/stores/projects/projects.model.ts
index 13e2862d0..71cbcc956 100644
--- a/src/app/shared/stores/projects/projects.model.ts
+++ b/src/app/shared/stores/projects/projects.model.ts
@@ -1,4 +1,4 @@
-import { ProjectModel } from '@osf/shared/models/projects/projects.models';
+import { ProjectModel } from '@osf/shared/models/projects/projects.model';
import { AsyncStateModel } from '@osf/shared/models/store/async-state.model';
export interface ProjectsStateModel {
diff --git a/src/testing/data/collections/collection-submissions.mock.ts b/src/testing/data/collections/collection-submissions.mock.ts
index cf812f95e..6dbba45b9 100644
--- a/src/testing/data/collections/collection-submissions.mock.ts
+++ b/src/testing/data/collections/collection-submissions.mock.ts
@@ -1,5 +1,5 @@
import { CollectionSubmissionReviewState } from '@osf/shared/enums/collection-submission-review-state.enum';
-import { CollectionSubmission } from '@osf/shared/models/collections/collections.models';
+import { CollectionSubmission } from '@osf/shared/models/collections/collections.model';
export const MOCK_PROJECT_COLLECTION_SUBMISSIONS: CollectionSubmission[] = [
{
diff --git a/src/testing/data/dashboard/dasboard.data.ts b/src/testing/data/dashboard/dasboard.data.ts
index 61ea6275a..c2e133b71 100644
--- a/src/testing/data/dashboard/dasboard.data.ts
+++ b/src/testing/data/dashboard/dasboard.data.ts
@@ -1,4 +1,4 @@
-import { MyResourcesItem } from '@shared/models/my-resources/my-resources.models';
+import { MyResourcesItem } from '@osf/shared/models/my-resources/my-resources.model';
import structuredClone from 'structured-clone';
diff --git a/src/testing/mocks/collections-submissions.mock.ts b/src/testing/mocks/collections-submissions.mock.ts
index db217ecb6..bd04c610e 100644
--- a/src/testing/mocks/collections-submissions.mock.ts
+++ b/src/testing/mocks/collections-submissions.mock.ts
@@ -1,4 +1,4 @@
-import { CollectionSubmissionWithGuid } from '@osf/shared/models/collections/collections.models';
+import { CollectionSubmissionWithGuid } from '@osf/shared/models/collections/collections.model';
export const MOCK_COLLECTION_SUBMISSION_1: CollectionSubmissionWithGuid = {
id: '1',
diff --git a/src/testing/mocks/data.mock.ts b/src/testing/mocks/data.mock.ts
index 6acecbf35..0d24b1261 100644
--- a/src/testing/mocks/data.mock.ts
+++ b/src/testing/mocks/data.mock.ts
@@ -1,4 +1,4 @@
-import { UserModel } from '@osf/shared/models/user/user.models';
+import { UserModel } from '@osf/shared/models/user/user.model';
import { UserRelatedCounts } from '@osf/shared/models/user-related-counts/user-related-counts.model';
export const MOCK_USER: UserModel = {
diff --git a/src/testing/mocks/my-resources.mock.ts b/src/testing/mocks/my-resources.mock.ts
index fff22e697..e726eb789 100644
--- a/src/testing/mocks/my-resources.mock.ts
+++ b/src/testing/mocks/my-resources.mock.ts
@@ -1,4 +1,4 @@
-import { MyResourcesItem } from '@shared/models/my-resources/my-resources.models';
+import { MyResourcesItem } from '@osf/shared/models/my-resources/my-resources.model';
import { MOCK_CONTRIBUTOR } from './contributors.mock';
diff --git a/src/testing/mocks/project-metadata.mock.ts b/src/testing/mocks/project-metadata.mock.ts
index 6972fc38a..25413d926 100644
--- a/src/testing/mocks/project-metadata.mock.ts
+++ b/src/testing/mocks/project-metadata.mock.ts
@@ -1,7 +1,7 @@
import { MetadataModel } from '@osf/features/metadata/models';
import { UserPermissions } from '@osf/shared/enums/user-permissions.enum';
+import { Institution } from '@osf/shared/models/institutions/institutions.model';
import { IdentifierModel } from '@shared/models/identifiers/identifier.model';
-import { Institution } from '@shared/models/institutions/institutions.models';
export const MOCK_PROJECT_METADATA: MetadataModel = {
id: 'project-123',
diff --git a/src/testing/mocks/project.mock.ts b/src/testing/mocks/project.mock.ts
index 9f5470f8c..f530cce13 100644
--- a/src/testing/mocks/project.mock.ts
+++ b/src/testing/mocks/project.mock.ts
@@ -1,4 +1,4 @@
-import { ProjectModel } from '@shared/models/projects/projects.models';
+import { ProjectModel } from '@osf/shared/models/projects/projects.model';
export const MOCK_PROJECT: ProjectModel = {
id: 'project-1',
diff --git a/src/testing/mocks/submission.mock.ts b/src/testing/mocks/submission.mock.ts
index d1f601f46..8c00a91f8 100644
--- a/src/testing/mocks/submission.mock.ts
+++ b/src/testing/mocks/submission.mock.ts
@@ -1,5 +1,5 @@
import { PreprintSubmissionModel } from '@osf/features/moderation/models';
-import { CollectionSubmissionWithGuid } from '@osf/shared/models/collections/collections.models';
+import { CollectionSubmissionWithGuid } from '@osf/shared/models/collections/collections.model';
import { MOCK_CONTRIBUTOR } from './contributors.mock';
diff --git a/src/testing/providers/addon-operation-invocation.service.mock.ts b/src/testing/providers/addon-operation-invocation.service.mock.ts
index 293aad2a9..8da6737c7 100644
--- a/src/testing/providers/addon-operation-invocation.service.mock.ts
+++ b/src/testing/providers/addon-operation-invocation.service.mock.ts
@@ -1,5 +1,5 @@
+import { OperationInvocationRequestJsonApi } from '@osf/shared/models/addons/addon-operations-json-api.model';
import { AddonOperationInvocationService } from '@osf/shared/services/addons/addon-operation-invocation.service';
-import { OperationInvocationRequestJsonApi } from '@shared/models/addons/addon-operations-json-api.models';
export function AddonOperationInvocationServiceMockFactory() {
return {
From ae6fc3260c8219d8f63455c96b6591960ff385ac Mon Sep 17 00:00:00 2001
From: nsemets
Date: Wed, 11 Feb 2026 16:40:34 +0200
Subject: [PATCH 09/10] [ENG-10252] Add unit tests for the previously skipped
tests in project overview and institutions (#877)
- Ticket: [ENG-10252]
- Feature flag: n/a
## Summary of Changes
1. Added unit tests for project overview and institutions.
---
.../institutions-list.component.spec.ts | 46 +--
.../add-component-dialog.component.spec.ts | 160 +++++++++-
.../delete-component-dialog.component.spec.ts | 294 +++++++++++++++++-
.../duplicate-dialog.component.spec.ts | 77 ++++-
.../overview-collections.component.spec.ts | 75 ++++-
...registration-custom-step.component.spec.ts | 121 +++++--
.../mocks/collections-submissions.mock.ts | 44 ++-
7 files changed, 758 insertions(+), 59 deletions(-)
diff --git a/src/app/features/institutions/pages/institutions-list/institutions-list.component.spec.ts b/src/app/features/institutions/pages/institutions-list/institutions-list.component.spec.ts
index 2c90dcfd1..a83e876a8 100644
--- a/src/app/features/institutions/pages/institutions-list/institutions-list.component.spec.ts
+++ b/src/app/features/institutions/pages/institutions-list/institutions-list.component.spec.ts
@@ -1,38 +1,30 @@
-import { MockComponents, MockProvider } from 'ng-mocks';
+import { Store } from '@ngxs/store';
-import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { MockComponents } from 'ng-mocks';
+
+import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { FormControl } from '@angular/forms';
-import { ActivatedRoute, Router } from '@angular/router';
import { ScheduledBannerComponent } from '@core/components/osf-banners/scheduled-banner/scheduled-banner.component';
import { LoadingSpinnerComponent } from '@osf/shared/components/loading-spinner/loading-spinner.component';
import { SearchInputComponent } from '@osf/shared/components/search-input/search-input.component';
import { SubHeaderComponent } from '@osf/shared/components/sub-header/sub-header.component';
-import { InstitutionsSelectors } from '@osf/shared/stores/institutions';
+import { FetchInstitutions, InstitutionsSelectors } from '@osf/shared/stores/institutions';
import { InstitutionsListComponent } from './institutions-list.component';
import { MOCK_INSTITUTION } from '@testing/mocks/institution.mock';
import { OSFTestingModule } from '@testing/osf.testing.module';
-import { ActivatedRouteMockBuilder } from '@testing/providers/route-provider.mock';
-import { RouterMockBuilder } from '@testing/providers/router-provider.mock';
import { provideMockStore } from '@testing/providers/store-provider.mock';
-describe.skip('Component: Institutions List', () => {
+describe('InstitutionsListComponent', () => {
let component: InstitutionsListComponent;
let fixture: ComponentFixture;
- let routerMock: ReturnType;
- let activatedRouteMock: ReturnType;
+ let store: Store;
const mockInstitutions = [MOCK_INSTITUTION];
- const mockTotalCount = 2;
beforeEach(async () => {
- routerMock = RouterMockBuilder.create().build();
- activatedRouteMock = ActivatedRouteMockBuilder.create()
- .withQueryParams({ page: '1', size: '10', search: '' })
- .build();
-
await TestBed.configureTestingModule({
imports: [
InstitutionsListComponent,
@@ -43,17 +35,15 @@ describe.skip('Component: Institutions List', () => {
provideMockStore({
signals: [
{ selector: InstitutionsSelectors.getInstitutions, value: mockInstitutions },
- { selector: InstitutionsSelectors.getInstitutionsTotalCount, value: mockTotalCount },
{ selector: InstitutionsSelectors.isInstitutionsLoading, value: false },
],
}),
- MockProvider(Router, routerMock),
- MockProvider(ActivatedRoute, activatedRouteMock),
],
}).compileComponents();
fixture = TestBed.createComponent(InstitutionsListComponent);
component = fixture.componentInstance;
+ store = TestBed.inject(Store);
fixture.detectChanges();
});
@@ -61,6 +51,26 @@ describe.skip('Component: Institutions List', () => {
expect(component).toBeTruthy();
});
+ it('should dispatch FetchInstitutions on init', () => {
+ expect(store.dispatch).toHaveBeenCalledWith(expect.any(FetchInstitutions));
+ const action = (store.dispatch as jest.Mock).mock.calls[0][0] as FetchInstitutions;
+ expect(action.searchValue).toBeUndefined();
+ });
+
+ it('should dispatch FetchInstitutions with search value after debounce', fakeAsync(() => {
+ (store.dispatch as jest.Mock).mockClear();
+ component.searchControl.setValue('test search');
+ tick(300);
+ expect(store.dispatch).toHaveBeenCalledWith(new FetchInstitutions('test search'));
+ }));
+
+ it('should dispatch FetchInstitutions with empty string when search is null', fakeAsync(() => {
+ (store.dispatch as jest.Mock).mockClear();
+ component.searchControl.setValue(null);
+ tick(300);
+ expect(store.dispatch).toHaveBeenCalledWith(new FetchInstitutions(''));
+ }));
+
it('should initialize with correct default values', () => {
expect(component.classes).toBe('flex-1 flex flex-column w-full');
expect(component.searchControl).toBeInstanceOf(FormControl);
diff --git a/src/app/features/project/overview/components/add-component-dialog/add-component-dialog.component.spec.ts b/src/app/features/project/overview/components/add-component-dialog/add-component-dialog.component.spec.ts
index 5538fd4f3..5fceb0708 100644
--- a/src/app/features/project/overview/components/add-component-dialog/add-component-dialog.component.spec.ts
+++ b/src/app/features/project/overview/components/add-component-dialog/add-component-dialog.component.spec.ts
@@ -1,26 +1,182 @@
+import { Store } from '@ngxs/store';
+
import { MockComponent } from 'ng-mocks';
+import { of } from 'rxjs';
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { UserSelectors } from '@core/store/user';
import { AffiliatedInstitutionSelectComponent } from '@osf/shared/components/affiliated-institution-select/affiliated-institution-select.component';
+import { ComponentFormControls } from '@osf/shared/enums/create-component-form-controls.enum';
+import { ToastService } from '@osf/shared/services/toast.service';
+import { FetchUserInstitutions, InstitutionsSelectors } from '@osf/shared/stores/institutions';
+import { FetchRegions, RegionsSelectors } from '@osf/shared/stores/regions';
+
+import { CreateComponent, GetComponents, ProjectOverviewSelectors } from '../../store';
import { AddComponentDialogComponent } from './add-component-dialog.component';
-describe.skip('AddComponentComponent', () => {
+import { MOCK_INSTITUTION } from '@testing/mocks/institution.mock';
+import { MOCK_PROJECT } from '@testing/mocks/project.mock';
+import { OSFTestingModule } from '@testing/osf.testing.module';
+import { provideMockStore } from '@testing/providers/store-provider.mock';
+
+describe('AddComponentDialogComponent', () => {
let component: AddComponentDialogComponent;
let fixture: ComponentFixture;
+ let store: Store;
+
+ const mockRegions = [{ id: 'region-1', name: 'Region 1' }];
+ const mockUser = { id: 'user-1', defaultRegionId: 'user-region' } as any;
+ const mockProject = { ...MOCK_PROJECT, id: 'proj-1', title: 'Project', tags: ['tag1'] };
+ const mockInstitutions = [MOCK_INSTITUTION];
+ const mockUserInstitutions = [MOCK_INSTITUTION, { ...MOCK_INSTITUTION, id: 'inst-2', name: 'Inst 2' }];
beforeEach(async () => {
await TestBed.configureTestingModule({
- imports: [AddComponentDialogComponent, MockComponent(AffiliatedInstitutionSelectComponent)],
+ imports: [AddComponentDialogComponent, OSFTestingModule, MockComponent(AffiliatedInstitutionSelectComponent)],
+ providers: [
+ provideMockStore({
+ signals: [
+ { selector: RegionsSelectors.getRegions, value: mockRegions },
+ { selector: UserSelectors.getCurrentUser, value: mockUser },
+ { selector: ProjectOverviewSelectors.getProject, value: mockProject },
+ { selector: ProjectOverviewSelectors.getInstitutions, value: mockInstitutions },
+ { selector: RegionsSelectors.areRegionsLoading, value: false },
+ { selector: ProjectOverviewSelectors.getComponentsSubmitting, value: false },
+ { selector: InstitutionsSelectors.getUserInstitutions, value: mockUserInstitutions },
+ { selector: InstitutionsSelectors.areUserInstitutionsLoading, value: false },
+ ],
+ }),
+ ],
}).compileComponents();
fixture = TestBed.createComponent(AddComponentDialogComponent);
component = fixture.componentInstance;
+ store = TestBed.inject(Store);
+ (store.dispatch as jest.Mock).mockReturnValue(of(void 0));
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
+
+ it('should initialize form with default values', () => {
+ expect(component.componentForm.get(ComponentFormControls.Title)?.value).toBe('');
+ expect(Array.isArray(component.componentForm.get(ComponentFormControls.Affiliations)?.value)).toBe(true);
+ expect(component.componentForm.get(ComponentFormControls.Description)?.value).toBe('');
+ expect(component.componentForm.get(ComponentFormControls.AddContributors)?.value).toBe(false);
+ expect(component.componentForm.get(ComponentFormControls.AddTags)?.value).toBe(false);
+ expect(['', 'user-region']).toContain(component.componentForm.get(ComponentFormControls.StorageLocation)?.value);
+ });
+
+ it('should dispatch FetchRegions and FetchUserInstitutions on init', () => {
+ expect(store.dispatch).toHaveBeenCalledWith(expect.any(FetchRegions));
+ expect(store.dispatch).toHaveBeenCalledWith(expect.any(FetchUserInstitutions));
+ });
+
+ it('should return store values from selectors', () => {
+ expect(component.storageLocations()).toEqual(mockRegions);
+ expect(component.currentUser()).toEqual(mockUser);
+ expect(component.currentProject()).toEqual(mockProject);
+ expect(component.institutions()).toEqual(mockInstitutions);
+ expect(component.areRegionsLoading()).toBe(false);
+ expect(component.isSubmitting()).toBe(false);
+ expect(component.userInstitutions()).toEqual(mockUserInstitutions);
+ expect(component.areUserInstitutionsLoading()).toBe(false);
+ });
+
+ it('should set affiliations form control from selected institutions', () => {
+ const institutions = [MOCK_INSTITUTION];
+ component.setSelectedInstitutions(institutions);
+ expect(component.componentForm.get(ComponentFormControls.Affiliations)?.value).toEqual([MOCK_INSTITUTION.id]);
+ });
+
+ it('should mark form as touched and not dispatch when submitForm with invalid form', () => {
+ (store.dispatch as jest.Mock).mockClear();
+ component.componentForm.get(ComponentFormControls.Title)?.setValue('');
+ component.submitForm();
+ expect(component.componentForm.touched).toBe(true);
+ const createCalls = (store.dispatch as jest.Mock).mock.calls.filter((c) => c[0] instanceof CreateComponent);
+ expect(createCalls.length).toBe(0);
+ });
+
+ it('should dispatch CreateComponent and on success close dialog, getComponents, showSuccess', () => {
+ component.componentForm.get(ComponentFormControls.Title)?.setValue('New Component');
+ component.componentForm.get(ComponentFormControls.StorageLocation)?.setValue('region-1');
+ component.componentForm.get(ComponentFormControls.Affiliations)?.setValue([MOCK_INSTITUTION.id]);
+ (store.dispatch as jest.Mock).mockClear();
+
+ component.submitForm();
+
+ expect(store.dispatch).toHaveBeenCalledWith(
+ new CreateComponent(mockProject.id, 'New Component', '', [], 'region-1', [MOCK_INSTITUTION.id], false)
+ );
+ expect(component.dialogRef.close).toHaveBeenCalled();
+ expect(store.dispatch).toHaveBeenCalledWith(expect.any(GetComponents));
+ expect(TestBed.inject(ToastService).showSuccess).toHaveBeenCalledWith(
+ 'project.overview.dialog.toast.addComponent.success'
+ );
+ });
+
+ it('should pass project tags when addTags is true', () => {
+ component.componentForm.get(ComponentFormControls.Title)?.setValue('With Tags');
+ component.componentForm.get(ComponentFormControls.StorageLocation)?.setValue('region-1');
+ component.componentForm.get(ComponentFormControls.Affiliations)?.setValue([]);
+ component.componentForm.get(ComponentFormControls.AddTags)?.setValue(true);
+ (store.dispatch as jest.Mock).mockClear();
+
+ component.submitForm();
+
+ expect(store.dispatch).toHaveBeenCalledWith(
+ new CreateComponent(mockProject.id, 'With Tags', '', mockProject.tags, 'region-1', [], false)
+ );
+ });
+
+ it('should set storage location to user default region when control empty and regions loaded', () => {
+ fixture = TestBed.createComponent(AddComponentDialogComponent);
+ component = fixture.componentInstance;
+ component.componentForm.get(ComponentFormControls.StorageLocation)?.setValue('');
+ fixture.detectChanges();
+ expect(component.componentForm.get(ComponentFormControls.StorageLocation)?.value).toBe('user-region');
+ });
+});
+
+describe('AddComponentDialogComponent when user has no default region', () => {
+ let component: AddComponentDialogComponent;
+ let fixture: ComponentFixture;
+
+ const mockRegions = [{ id: 'region-1', name: 'Region 1' }];
+ const mockProject = { ...MOCK_PROJECT, id: 'proj-1', title: 'Project', tags: ['tag1'] };
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [AddComponentDialogComponent, OSFTestingModule, MockComponent(AffiliatedInstitutionSelectComponent)],
+ providers: [
+ provideMockStore({
+ signals: [
+ { selector: RegionsSelectors.getRegions, value: mockRegions },
+ { selector: UserSelectors.getCurrentUser, value: null },
+ { selector: ProjectOverviewSelectors.getProject, value: mockProject },
+ { selector: ProjectOverviewSelectors.getInstitutions, value: [] },
+ { selector: RegionsSelectors.areRegionsLoading, value: false },
+ { selector: ProjectOverviewSelectors.getComponentsSubmitting, value: false },
+ { selector: InstitutionsSelectors.getUserInstitutions, value: [] },
+ { selector: InstitutionsSelectors.areUserInstitutionsLoading, value: false },
+ ],
+ }),
+ ],
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(AddComponentDialogComponent);
+ component = fixture.componentInstance;
+ component.componentForm.get(ComponentFormControls.StorageLocation)?.setValue('');
+ fixture.detectChanges();
+ });
+
+ it('should set storage location to first region when control empty', () => {
+ expect(component.componentForm.get(ComponentFormControls.StorageLocation)?.value).toBe('region-1');
+ });
});
diff --git a/src/app/features/project/overview/components/delete-component-dialog/delete-component-dialog.component.spec.ts b/src/app/features/project/overview/components/delete-component-dialog/delete-component-dialog.component.spec.ts
index 73c88f51e..b2954f518 100644
--- a/src/app/features/project/overview/components/delete-component-dialog/delete-component-dialog.component.spec.ts
+++ b/src/app/features/project/overview/components/delete-component-dialog/delete-component-dialog.component.spec.ts
@@ -1,22 +1,312 @@
+import { Store } from '@ngxs/store';
+
+import { DynamicDialogConfig } from 'primeng/dynamicdialog';
+
+import { of } from 'rxjs';
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { DeleteProject, SettingsSelectors } from '@osf/features/project/settings/store';
+import { RegistrySelectors } from '@osf/features/registry/store/registry';
+import { ScientistsNames } from '@osf/shared/constants/scientists.const';
+import { ResourceType } from '@osf/shared/enums/resource-type.enum';
+import { UserPermissions } from '@osf/shared/enums/user-permissions.enum';
+import { ToastService } from '@osf/shared/services/toast.service';
+import { CurrentResourceSelectors } from '@osf/shared/stores/current-resource';
+
+import { GetComponents, ProjectOverviewSelectors } from '../../store';
+
import { DeleteComponentDialogComponent } from './delete-component-dialog.component';
-describe.skip('DeleteComponentDialogComponent', () => {
+import { MOCK_NODE_WITH_ADMIN } from '@testing/mocks/node.mock';
+import { OSFTestingModule } from '@testing/osf.testing.module';
+import { provideMockStore } from '@testing/providers/store-provider.mock';
+
+const mockComponentsWithAdmin = [
+ { id: 'comp-1', title: 'Component 1', isPublic: true, permissions: [UserPermissions.Admin] },
+ { id: 'comp-2', title: 'Component 2', isPublic: false, permissions: [UserPermissions.Admin] },
+];
+
+const mockComponentsWithoutAdmin = [
+ { id: 'comp-1', title: 'Component 1', isPublic: true, permissions: [UserPermissions.Read] },
+];
+
+describe('DeleteComponentDialogComponent', () => {
let component: DeleteComponentDialogComponent;
let fixture: ComponentFixture;
+ let store: Store;
+ let dialogConfig: DynamicDialogConfig;
+
+ const mockProject = { ...MOCK_NODE_WITH_ADMIN, id: 'proj-1' };
beforeEach(async () => {
+ dialogConfig = { data: { resourceType: ResourceType.Project } };
+
await TestBed.configureTestingModule({
- imports: [DeleteComponentDialogComponent],
+ imports: [DeleteComponentDialogComponent, OSFTestingModule],
+ providers: [
+ provideMockStore({
+ signals: [
+ { selector: ProjectOverviewSelectors.getProject, value: mockProject },
+ { selector: RegistrySelectors.getRegistry, value: null },
+ { selector: SettingsSelectors.isSettingsSubmitting, value: false },
+ { selector: CurrentResourceSelectors.isResourceWithChildrenLoading, value: false },
+ { selector: CurrentResourceSelectors.getResourceWithChildren, value: mockComponentsWithAdmin },
+ ],
+ }),
+ { provide: DynamicDialogConfig, useValue: dialogConfig },
+ ],
}).compileComponents();
fixture = TestBed.createComponent(DeleteComponentDialogComponent);
component = fixture.componentInstance;
+ store = TestBed.inject(Store);
+ (store.dispatch as jest.Mock).mockReturnValue(of(void 0));
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
+
+ it('should return store values from selectors', () => {
+ expect(component.project()).toEqual(mockProject);
+ expect(component.registration()).toBeNull();
+ expect(component.isSubmitting()).toBe(false);
+ expect(component.isLoading()).toBe(false);
+ expect(component.components()).toEqual(mockComponentsWithAdmin);
+ });
+
+ it('should have selectedScientist as one of ScientistsNames', () => {
+ expect(ScientistsNames).toContain(component.selectedScientist());
+ });
+
+ it('should compute currentResource as project when resourceType is Project', () => {
+ expect(component.currentResource()).toEqual(mockProject);
+ });
+
+ it('should compute hasAdminAccessForAllComponents true when all components have Admin', () => {
+ expect(component.hasAdminAccessForAllComponents()).toBe(true);
+ });
+
+ it('should compute hasSubComponents true when more than one component', () => {
+ expect(component.hasSubComponents()).toBe(true);
+ });
+
+ it('should return isInputValid true when userInput matches selectedScientist', () => {
+ const scientist = component.selectedScientist();
+ component.onInputChange(scientist);
+ expect(component.isInputValid()).toBe(true);
+ });
+
+ it('should return isInputValid false when userInput does not match', () => {
+ component.onInputChange('wrong');
+ expect(component.isInputValid()).toBe(false);
+ });
+
+ it('should set userInput on onInputChange', () => {
+ component.onInputChange('test');
+ expect(component.userInput()).toBe('test');
+ });
+
+ it('should dispatch DeleteProject with components and on success close, getComponents, showSuccess', () => {
+ const scientist = component.selectedScientist();
+ component.onInputChange(scientist);
+ (store.dispatch as jest.Mock).mockClear();
+
+ component.handleDeleteComponent();
+
+ expect(store.dispatch).toHaveBeenCalledWith(expect.any(DeleteProject));
+ const deleteCall = (store.dispatch as jest.Mock).mock.calls.find((c) => c[0] instanceof DeleteProject);
+ expect(deleteCall[0].projects).toEqual(mockComponentsWithAdmin);
+ expect(component.dialogRef.close).toHaveBeenCalledWith({ success: true });
+ expect(store.dispatch).toHaveBeenCalledWith(expect.any(GetComponents));
+ expect(TestBed.inject(ToastService).showSuccess).toHaveBeenCalledWith(
+ 'project.overview.dialog.toast.deleteComponent.success'
+ );
+ });
+});
+
+describe('DeleteComponentDialogComponent when not all components have Admin', () => {
+ let component: DeleteComponentDialogComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [DeleteComponentDialogComponent, OSFTestingModule],
+ providers: [
+ provideMockStore({
+ signals: [
+ { selector: ProjectOverviewSelectors.getProject, value: { ...MOCK_NODE_WITH_ADMIN, id: 'proj-1' } },
+ { selector: RegistrySelectors.getRegistry, value: null },
+ { selector: SettingsSelectors.isSettingsSubmitting, value: false },
+ { selector: CurrentResourceSelectors.isResourceWithChildrenLoading, value: false },
+ { selector: CurrentResourceSelectors.getResourceWithChildren, value: mockComponentsWithoutAdmin },
+ ],
+ }),
+ { provide: DynamicDialogConfig, useValue: { data: { resourceType: ResourceType.Project } } },
+ ],
+ }).compileComponents();
+ fixture = TestBed.createComponent(DeleteComponentDialogComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should compute hasAdminAccessForAllComponents false', () => {
+ expect(component.hasAdminAccessForAllComponents()).toBe(false);
+ });
+});
+
+describe('DeleteComponentDialogComponent when single component', () => {
+ let component: DeleteComponentDialogComponent;
+ let fixture: ComponentFixture;
+
+ const singleComponent = [
+ { id: 'comp-1', title: 'Component 1', isPublic: true, permissions: [UserPermissions.Admin] },
+ ];
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [DeleteComponentDialogComponent, OSFTestingModule],
+ providers: [
+ provideMockStore({
+ signals: [
+ { selector: ProjectOverviewSelectors.getProject, value: { ...MOCK_NODE_WITH_ADMIN, id: 'proj-1' } },
+ { selector: RegistrySelectors.getRegistry, value: null },
+ { selector: SettingsSelectors.isSettingsSubmitting, value: false },
+ { selector: CurrentResourceSelectors.isResourceWithChildrenLoading, value: false },
+ { selector: CurrentResourceSelectors.getResourceWithChildren, value: singleComponent },
+ ],
+ }),
+ { provide: DynamicDialogConfig, useValue: { data: { resourceType: ResourceType.Project } } },
+ ],
+ }).compileComponents();
+ fixture = TestBed.createComponent(DeleteComponentDialogComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should compute hasSubComponents false', () => {
+ expect(component.hasSubComponents()).toBe(false);
+ });
+});
+
+describe('DeleteComponentDialogComponent when no components', () => {
+ let component: DeleteComponentDialogComponent;
+ let fixture: ComponentFixture;
+ let store: Store;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [DeleteComponentDialogComponent, OSFTestingModule],
+ providers: [
+ provideMockStore({
+ signals: [
+ { selector: ProjectOverviewSelectors.getProject, value: { ...MOCK_NODE_WITH_ADMIN, id: 'proj-1' } },
+ { selector: RegistrySelectors.getRegistry, value: null },
+ { selector: SettingsSelectors.isSettingsSubmitting, value: false },
+ { selector: CurrentResourceSelectors.isResourceWithChildrenLoading, value: false },
+ { selector: CurrentResourceSelectors.getResourceWithChildren, value: [] },
+ ],
+ }),
+ { provide: DynamicDialogConfig, useValue: { data: { resourceType: ResourceType.Project } } },
+ ],
+ }).compileComponents();
+ fixture = TestBed.createComponent(DeleteComponentDialogComponent);
+ component = fixture.componentInstance;
+ store = TestBed.inject(Store);
+ (store.dispatch as jest.Mock).mockClear();
+ fixture.detectChanges();
+ });
+
+ it('should not dispatch when handleDeleteComponent', () => {
+ component.handleDeleteComponent();
+ expect(store.dispatch).not.toHaveBeenCalled();
+ });
+});
+
+describe('DeleteComponentDialogComponent when resourceType is Registration', () => {
+ let component: DeleteComponentDialogComponent;
+ let fixture: ComponentFixture;
+
+ const mockRegistration = { ...MOCK_NODE_WITH_ADMIN, id: 'reg-1' };
+ const mockComponentsWithAdmin = [
+ { id: 'comp-1', title: 'Component 1', isPublic: true, permissions: [UserPermissions.Admin] },
+ ];
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [DeleteComponentDialogComponent, OSFTestingModule],
+ providers: [
+ provideMockStore({
+ signals: [
+ { selector: ProjectOverviewSelectors.getProject, value: null },
+ { selector: RegistrySelectors.getRegistry, value: mockRegistration },
+ { selector: SettingsSelectors.isSettingsSubmitting, value: false },
+ { selector: CurrentResourceSelectors.isResourceWithChildrenLoading, value: false },
+ { selector: CurrentResourceSelectors.getResourceWithChildren, value: mockComponentsWithAdmin },
+ ],
+ }),
+ { provide: DynamicDialogConfig, useValue: { data: { resourceType: ResourceType.Registration } } },
+ ],
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(DeleteComponentDialogComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should compute currentResource as registration', () => {
+ expect(component.currentResource()).toEqual(mockRegistration);
+ });
+});
+
+describe('DeleteComponentDialogComponent isForksContext', () => {
+ let component: DeleteComponentDialogComponent;
+ let fixture: ComponentFixture;
+ let store: Store;
+
+ const mockProject = { ...MOCK_NODE_WITH_ADMIN, id: 'proj-1' };
+ const mockComponentsWithAdmin = [
+ { id: 'comp-1', title: 'Component 1', isPublic: true, permissions: [UserPermissions.Admin] },
+ ];
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [DeleteComponentDialogComponent, OSFTestingModule],
+ providers: [
+ provideMockStore({
+ signals: [
+ { selector: ProjectOverviewSelectors.getProject, value: mockProject },
+ { selector: RegistrySelectors.getRegistry, value: null },
+ { selector: SettingsSelectors.isSettingsSubmitting, value: false },
+ { selector: CurrentResourceSelectors.isResourceWithChildrenLoading, value: false },
+ { selector: CurrentResourceSelectors.getResourceWithChildren, value: mockComponentsWithAdmin },
+ ],
+ }),
+ {
+ provide: DynamicDialogConfig,
+ useValue: { data: { resourceType: ResourceType.Project, isForksContext: true } },
+ },
+ ],
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(DeleteComponentDialogComponent);
+ component = fixture.componentInstance;
+ store = TestBed.inject(Store);
+ (store.dispatch as jest.Mock).mockReturnValue(of(void 0));
+ fixture.detectChanges();
+ });
+
+ it('should not dispatch GetComponents when isForksContext', () => {
+ const scientist = component.selectedScientist();
+ component.onInputChange(scientist);
+ (store.dispatch as jest.Mock).mockClear();
+
+ component.handleDeleteComponent();
+
+ expect(store.dispatch).toHaveBeenCalledWith(expect.any(DeleteProject));
+ const getComponentsCalls = (store.dispatch as jest.Mock).mock.calls.filter((c) => c[0] instanceof GetComponents);
+ expect(getComponentsCalls.length).toBe(0);
+ });
});
diff --git a/src/app/features/project/overview/components/duplicate-dialog/duplicate-dialog.component.spec.ts b/src/app/features/project/overview/components/duplicate-dialog/duplicate-dialog.component.spec.ts
index 5d78cf850..62f5c009a 100644
--- a/src/app/features/project/overview/components/duplicate-dialog/duplicate-dialog.component.spec.ts
+++ b/src/app/features/project/overview/components/duplicate-dialog/duplicate-dialog.component.spec.ts
@@ -1,22 +1,95 @@
+import { Store } from '@ngxs/store';
+
+import { of } from 'rxjs';
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ToastService } from '@osf/shared/services/toast.service';
+
+import { DuplicateProject, ProjectOverviewSelectors } from '../../store';
+
import { DuplicateDialogComponent } from './duplicate-dialog.component';
-describe.skip('DuplicateDialogComponent', () => {
+import { MOCK_NODE_WITH_ADMIN } from '@testing/mocks/node.mock';
+import { OSFTestingModule } from '@testing/osf.testing.module';
+import { provideMockStore } from '@testing/providers/store-provider.mock';
+
+describe('DuplicateDialogComponent', () => {
let component: DuplicateDialogComponent;
let fixture: ComponentFixture;
+ let store: Store;
+
+ const mockProject = { ...MOCK_NODE_WITH_ADMIN, id: 'proj-1', title: 'Test Project' };
beforeEach(async () => {
await TestBed.configureTestingModule({
- imports: [DuplicateDialogComponent],
+ imports: [DuplicateDialogComponent, OSFTestingModule],
+ providers: [
+ provideMockStore({
+ signals: [
+ { selector: ProjectOverviewSelectors.getProject, value: mockProject },
+ { selector: ProjectOverviewSelectors.getDuplicateProjectSubmitting, value: false },
+ ],
+ }),
+ ],
}).compileComponents();
fixture = TestBed.createComponent(DuplicateDialogComponent);
component = fixture.componentInstance;
+ store = TestBed.inject(Store);
+ (store.dispatch as jest.Mock).mockReturnValue(of(void 0));
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
+
+ it('should return project and isSubmitting from store', () => {
+ expect(component.project()).toEqual(mockProject);
+ expect(component.isSubmitting()).toBe(false);
+ });
+
+ it('should dispatch DuplicateProject and on success close dialog and showSuccess', () => {
+ (store.dispatch as jest.Mock).mockClear();
+
+ component.handleDuplicateConfirm();
+
+ expect(store.dispatch).toHaveBeenCalledWith(new DuplicateProject(mockProject.id, mockProject.title));
+ expect(component.dialogRef.close).toHaveBeenCalled();
+ expect(TestBed.inject(ToastService).showSuccess).toHaveBeenCalledWith(
+ 'project.overview.dialog.toast.duplicate.success'
+ );
+ });
+});
+
+describe('DuplicateDialogComponent when no project', () => {
+ let component: DuplicateDialogComponent;
+ let fixture: ComponentFixture;
+ let store: Store;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [DuplicateDialogComponent, OSFTestingModule],
+ providers: [
+ provideMockStore({
+ signals: [
+ { selector: ProjectOverviewSelectors.getProject, value: null },
+ { selector: ProjectOverviewSelectors.getDuplicateProjectSubmitting, value: false },
+ ],
+ }),
+ ],
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(DuplicateDialogComponent);
+ component = fixture.componentInstance;
+ store = TestBed.inject(Store);
+ (store.dispatch as jest.Mock).mockClear();
+ fixture.detectChanges();
+ });
+
+ it('should not dispatch when handleDuplicateConfirm', () => {
+ component.handleDuplicateConfirm();
+ expect(store.dispatch).not.toHaveBeenCalled();
+ });
});
diff --git a/src/app/features/project/overview/components/overview-collections/overview-collections.component.spec.ts b/src/app/features/project/overview/components/overview-collections/overview-collections.component.spec.ts
index cbc3f5cf2..4809b1a72 100644
--- a/src/app/features/project/overview/components/overview-collections/overview-collections.component.spec.ts
+++ b/src/app/features/project/overview/components/overview-collections/overview-collections.component.spec.ts
@@ -1,14 +1,26 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { collectionFilterNames } from '@osf/features/collections/constants';
+import { CollectionSubmission } from '@osf/shared/models/collections/collections.models';
+
import { OverviewCollectionsComponent } from './overview-collections.component';
-describe.skip('OverviewCollectionsComponent', () => {
+import {
+ MOCK_COLLECTION_SUBMISSION_EMPTY_FILTERS,
+ MOCK_COLLECTION_SUBMISSION_SINGLE_FILTER,
+ MOCK_COLLECTION_SUBMISSION_STRINGIFY,
+ MOCK_COLLECTION_SUBMISSION_WITH_FILTERS,
+ MOCK_COLLECTION_SUBMISSIONS,
+} from '@testing/mocks/collections-submissions.mock';
+import { OSFTestingModule } from '@testing/osf.testing.module';
+
+describe('OverviewCollectionsComponent', () => {
let component: OverviewCollectionsComponent;
let fixture: ComponentFixture;
beforeEach(async () => {
await TestBed.configureTestingModule({
- imports: [OverviewCollectionsComponent],
+ imports: [OverviewCollectionsComponent, OSFTestingModule],
}).compileComponents();
fixture = TestBed.createComponent(OverviewCollectionsComponent);
@@ -19,4 +31,63 @@ describe.skip('OverviewCollectionsComponent', () => {
it('should create', () => {
expect(component).toBeTruthy();
});
+
+ it('should have default input values', () => {
+ expect(component.projectSubmissions()).toBeNull();
+ expect(component.isProjectSubmissionsLoading()).toBe(false);
+ });
+
+ it('should accept projectSubmissions and isProjectSubmissionsLoading via setInput', () => {
+ const submissions: CollectionSubmission[] = MOCK_COLLECTION_SUBMISSIONS.map((s) => ({
+ ...s,
+ collectionTitle: s.title,
+ collectionId: `col-${s.id}`,
+ })) as CollectionSubmission[];
+ fixture.componentRef.setInput('projectSubmissions', submissions);
+ fixture.componentRef.setInput('isProjectSubmissionsLoading', true);
+ fixture.detectChanges();
+ expect(component.projectSubmissions()).toEqual(submissions);
+ expect(component.isProjectSubmissionsLoading()).toBe(true);
+ });
+
+ it('should return empty array from getSubmissionAttributes when submission has no filter values', () => {
+ expect(component.getSubmissionAttributes(MOCK_COLLECTION_SUBMISSION_EMPTY_FILTERS)).toEqual([]);
+ });
+
+ it('should return attributes for truthy filter keys from getSubmissionAttributes', () => {
+ const result = component.getSubmissionAttributes(MOCK_COLLECTION_SUBMISSION_WITH_FILTERS);
+ const programAreaFilter = collectionFilterNames.find((f) => f.key === 'programArea');
+ const collectedTypeFilter = collectionFilterNames.find((f) => f.key === 'collectedType');
+ const statusFilter = collectionFilterNames.find((f) => f.key === 'status');
+ expect(result).toContainEqual({
+ key: 'programArea',
+ label: programAreaFilter?.label,
+ value: 'Health',
+ });
+ expect(result).toContainEqual({
+ key: 'collectedType',
+ label: collectedTypeFilter?.label,
+ value: 'Article',
+ });
+ expect(result).toContainEqual({
+ key: 'status',
+ label: statusFilter?.label,
+ value: 'Published',
+ });
+ expect(result.length).toBe(3);
+ });
+
+ it('should exclude falsy values from getSubmissionAttributes', () => {
+ const result = component.getSubmissionAttributes(MOCK_COLLECTION_SUBMISSION_SINGLE_FILTER);
+ expect(result).toHaveLength(1);
+ expect(result[0].key).toBe('collectedType');
+ expect(result[0].value).toBe('Article');
+ });
+
+ it('should stringify numeric-like values in getSubmissionAttributes', () => {
+ const result = component.getSubmissionAttributes(MOCK_COLLECTION_SUBMISSION_STRINGIFY);
+ const statusAttr = result.find((a) => a.key === 'status');
+ expect(statusAttr?.value).toBe('1');
+ expect(typeof statusAttr?.value).toBe('string');
+ });
});
diff --git a/src/app/features/registries/pages/draft-registration-custom-step/draft-registration-custom-step.component.spec.ts b/src/app/features/registries/pages/draft-registration-custom-step/draft-registration-custom-step.component.spec.ts
index b446b84b5..0534020da 100644
--- a/src/app/features/registries/pages/draft-registration-custom-step/draft-registration-custom-step.component.spec.ts
+++ b/src/app/features/registries/pages/draft-registration-custom-step/draft-registration-custom-step.component.spec.ts
@@ -1,44 +1,51 @@
-import { MockComponent } from 'ng-mocks';
+import { Store } from '@ngxs/store';
+
+import { MockComponent, MockProvider } from 'ng-mocks';
+
+import { of } from 'rxjs';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ActivatedRoute, Router } from '@angular/router';
-import { RegistriesSelectors } from '@osf/features/registries/store';
+import { DraftRegistrationAttributesJsonApi } from '@osf/shared/models/registration/registration-json-api.model';
import { CustomStepComponent } from '../../components/custom-step/custom-step.component';
+import { RegistriesSelectors, UpdateDraft } from '../../store';
import { DraftRegistrationCustomStepComponent } from './draft-registration-custom-step.component';
-import { MOCK_REGISTRIES_PAGE } from '@testing/mocks/registries.mock';
import { OSFTestingModule } from '@testing/osf.testing.module';
import { ActivatedRouteMockBuilder } from '@testing/providers/route-provider.mock';
import { RouterMockBuilder } from '@testing/providers/router-provider.mock';
import { provideMockStore } from '@testing/providers/store-provider.mock';
-describe.skip('DraftRegistrationCustomStepComponent', () => {
+describe('DraftRegistrationCustomStepComponent', () => {
let component: DraftRegistrationCustomStepComponent;
let fixture: ComponentFixture;
- let mockActivatedRoute: ReturnType;
+ let store: Store;
let mockRouter: ReturnType;
+ let mockActivatedRoute: ReturnType;
+
+ const mockStepsData = { stepKey: { field: 'value' } };
+ const mockDraftRegistration = {
+ id: 'draft-1',
+ providerId: 'prov-1',
+ branchedFrom: { id: 'proj-1', filesLink: '/project/proj-1/files/' },
+ };
beforeEach(async () => {
- mockActivatedRoute = ActivatedRouteMockBuilder.create().withParams({ id: 'draft-1', step: '1' }).build();
- mockRouter = RouterMockBuilder.create().withUrl('/registries/prov-1/draft/draft-1/custom').build();
+ mockRouter = RouterMockBuilder.create().build();
+ mockActivatedRoute = ActivatedRouteMockBuilder.create().withParams({ id: 'draft-1' }).build();
await TestBed.configureTestingModule({
imports: [DraftRegistrationCustomStepComponent, OSFTestingModule, MockComponent(CustomStepComponent)],
providers: [
- { provide: ActivatedRoute, useValue: mockActivatedRoute },
- { provide: Router, useValue: mockRouter },
+ MockProvider(Router, mockRouter),
+ MockProvider(ActivatedRoute, mockActivatedRoute),
provideMockStore({
signals: [
- { selector: RegistriesSelectors.getStepsData, value: {} },
- {
- selector: RegistriesSelectors.getDraftRegistration,
- value: { id: 'draft-1', providerId: 'prov-1', branchedFrom: { id: 'node-1', filesLink: '/files' } },
- },
- { selector: RegistriesSelectors.getPagesSchema, value: [MOCK_REGISTRIES_PAGE] },
- { selector: RegistriesSelectors.getStepsState, value: { 1: { invalid: false } } },
+ { selector: RegistriesSelectors.getStepsData, value: mockStepsData },
+ { selector: RegistriesSelectors.getDraftRegistration, value: mockDraftRegistration },
],
}),
],
@@ -46,6 +53,8 @@ describe.skip('DraftRegistrationCustomStepComponent', () => {
fixture = TestBed.createComponent(DraftRegistrationCustomStepComponent);
component = fixture.componentInstance;
+ store = TestBed.inject(Store);
+ (store.dispatch as jest.Mock).mockReturnValue(of(void 0));
fixture.detectChanges();
});
@@ -53,29 +62,81 @@ describe.skip('DraftRegistrationCustomStepComponent', () => {
expect(component).toBeTruthy();
});
- it('should compute inputs from draft registration', () => {
- expect(component.filesLink()).toBe('/files');
+ it('should return stepsData and draftRegistration from store', () => {
+ expect(component.stepsData()).toEqual(mockStepsData);
+ expect(component.draftRegistration()).toEqual(mockDraftRegistration);
+ });
+
+ it('should compute filesLink from draftRegistration branchedFrom', () => {
+ expect(component.filesLink()).toBe('/project/proj-1/files/');
+ });
+
+ it('should compute provider from draftRegistration providerId', () => {
expect(component.provider()).toBe('prov-1');
- expect(component.projectId()).toBe('node-1');
});
- it('should dispatch updateDraft on onUpdateAction', () => {
- const actionsMock = { updateDraft: jest.fn() } as any;
- Object.defineProperty(component, 'actions', { value: actionsMock });
+ it('should compute projectId from draftRegistration branchedFrom id', () => {
+ expect(component.projectId()).toBe('proj-1');
+ });
+
+ it('should dispatch UpdateDraft with id and registration_responses payload on onUpdateAction', () => {
+ const attributes: Partial = {
+ registration_responses: { field1: 'value1' },
+ };
+ (store.dispatch as jest.Mock).mockClear();
- component.onUpdateAction({ a: 1 } as any);
- expect(actionsMock.updateDraft).toHaveBeenCalledWith('draft-1', { registration_responses: { a: 1 } });
+ component.onUpdateAction(attributes);
+
+ expect(store.dispatch).toHaveBeenCalledWith(expect.any(UpdateDraft));
+ const call = (store.dispatch as jest.Mock).mock.calls.find((c) => c[0] instanceof UpdateDraft);
+ expect(call[0].draftId).toBe('draft-1');
+ expect(call[0].attributes).toEqual({ registration_responses: { registration_responses: { field1: 'value1' } } });
});
- it('should navigate back to metadata on onBack', () => {
- const navigateSpy = jest.spyOn(TestBed.inject(Router), 'navigate');
+ it('should navigate to ../metadata on onBack', () => {
component.onBack();
- expect(navigateSpy).toHaveBeenCalledWith(['../', 'metadata'], { relativeTo: TestBed.inject(ActivatedRoute) });
+ expect(mockRouter.navigate).toHaveBeenCalledWith(
+ ['../', 'metadata'],
+ expect.objectContaining({ relativeTo: expect.anything() })
+ );
});
- it('should navigate to review on onNext', () => {
- const navigateSpy = jest.spyOn(TestBed.inject(Router), 'navigate');
+ it('should navigate to ../review on onNext', () => {
component.onNext();
- expect(navigateSpy).toHaveBeenCalledWith(['../', 'review'], { relativeTo: TestBed.inject(ActivatedRoute) });
+ expect(mockRouter.navigate).toHaveBeenCalledWith(
+ ['../', 'review'],
+ expect.objectContaining({ relativeTo: expect.anything() })
+ );
+ });
+});
+
+describe('DraftRegistrationCustomStepComponent when no draft registration', () => {
+ let component: DraftRegistrationCustomStepComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [DraftRegistrationCustomStepComponent, OSFTestingModule, MockComponent(CustomStepComponent)],
+ providers: [
+ MockProvider(Router, RouterMockBuilder.create().build()),
+ MockProvider(ActivatedRoute, ActivatedRouteMockBuilder.create().withParams({ id: 'draft-1' }).build()),
+ provideMockStore({
+ signals: [
+ { selector: RegistriesSelectors.getStepsData, value: {} },
+ { selector: RegistriesSelectors.getDraftRegistration, value: null },
+ ],
+ }),
+ ],
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(DraftRegistrationCustomStepComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should compute empty filesLink provider and projectId', () => {
+ expect(component.filesLink()).toBe('');
+ expect(component.provider()).toBe('');
+ expect(component.projectId()).toBe('');
});
});
diff --git a/src/testing/mocks/collections-submissions.mock.ts b/src/testing/mocks/collections-submissions.mock.ts
index bd04c610e..fcf93c12e 100644
--- a/src/testing/mocks/collections-submissions.mock.ts
+++ b/src/testing/mocks/collections-submissions.mock.ts
@@ -1,4 +1,5 @@
-import { CollectionSubmissionWithGuid } from '@osf/shared/models/collections/collections.model';
+import { CollectionSubmissionReviewState } from '@osf/shared/enums/collection-submission-review-state.enum';
+import { CollectionSubmission, CollectionSubmissionWithGuid } from '@osf/shared/models/collections/collections.model';
export const MOCK_COLLECTION_SUBMISSION_1: CollectionSubmissionWithGuid = {
id: '1',
@@ -11,7 +12,7 @@ export const MOCK_COLLECTION_SUBMISSION_1: CollectionSubmissionWithGuid = {
dateCreated: '2024-01-01T00:00:00Z',
dateModified: '2024-01-02T00:00:00Z',
public: false,
- reviewsState: 'pending',
+ reviewsState: CollectionSubmissionReviewState.Pending,
collectedType: 'preprint',
status: 'pending',
volume: '1',
@@ -54,7 +55,7 @@ export const MOCK_COLLECTION_SUBMISSION_2: CollectionSubmissionWithGuid = {
dateCreated: '2024-01-02T00:00:00Z',
dateModified: '2024-01-03T00:00:00Z',
public: true,
- reviewsState: 'approved',
+ reviewsState: CollectionSubmissionReviewState.Accepted,
collectedType: 'preprint',
status: 'approved',
volume: '2',
@@ -87,3 +88,40 @@ export const MOCK_COLLECTION_SUBMISSION_2: CollectionSubmissionWithGuid = {
};
export const MOCK_COLLECTION_SUBMISSIONS = [MOCK_COLLECTION_SUBMISSION_1, MOCK_COLLECTION_SUBMISSION_2];
+
+export const MOCK_COLLECTION_SUBMISSION_EMPTY_FILTERS: CollectionSubmission = {
+ id: 'sub-1',
+ type: 'collection-submissions',
+ collectionTitle: 'Collection',
+ collectionId: 'col-1',
+ reviewsState: CollectionSubmissionReviewState.Pending,
+ collectedType: '',
+ status: '',
+ volume: '',
+ issue: '',
+ programArea: '',
+ schoolType: '',
+ studyDesign: '',
+ dataType: '',
+ disease: '',
+ gradeLevels: '',
+};
+
+export const MOCK_COLLECTION_SUBMISSION_WITH_FILTERS: CollectionSubmission = {
+ ...MOCK_COLLECTION_SUBMISSION_EMPTY_FILTERS,
+ reviewsState: CollectionSubmissionReviewState.Accepted,
+ collectedType: 'Article',
+ status: 'Published',
+ programArea: 'Health',
+};
+
+export const MOCK_COLLECTION_SUBMISSION_SINGLE_FILTER: CollectionSubmission = {
+ ...MOCK_COLLECTION_SUBMISSION_EMPTY_FILTERS,
+ collectedType: 'Article',
+};
+
+export const MOCK_COLLECTION_SUBMISSION_STRINGIFY: CollectionSubmission = {
+ ...MOCK_COLLECTION_SUBMISSION_EMPTY_FILTERS,
+ collectedType: 'Article',
+ status: '1',
+};
From 294f1e10a02ff6e257f61ce8d1c61bf568b21bc9 Mon Sep 17 00:00:00 2001
From: Ihor Sokhan
Date: Tue, 17 Feb 2026 13:44:07 +0200
Subject: [PATCH 10/10] fix(ENG-10262): fixed login url encoding
---
src/app/core/services/auth.service.ts | 11 +++++++++--
src/app/shared/helpers/url-param.helper.ts | 12 ++++++++++++
2 files changed, 21 insertions(+), 2 deletions(-)
diff --git a/src/app/core/services/auth.service.ts b/src/app/core/services/auth.service.ts
index b51cb58b9..da0063ce7 100644
--- a/src/app/core/services/auth.service.ts
+++ b/src/app/core/services/auth.service.ts
@@ -8,7 +8,7 @@ import { inject, Injectable, PLATFORM_ID } from '@angular/core';
import { SignUpModel } from '@core/models/sign-up.model';
import { ENVIRONMENT } from '@core/provider/environment.provider';
import { ClearCurrentUser } from '@osf/core/store/user';
-import { urlParam } from '@osf/shared/helpers/url-param.helper';
+import { localUrlParam, urlParam } from '@osf/shared/helpers/url-param.helper';
import { JsonApiService } from '@osf/shared/services/json-api.service';
import { LoaderService } from '@osf/shared/services/loader.service';
@@ -41,7 +41,14 @@ export class AuthService {
}
this.loaderService.show();
- const loginUrl = `${this.casUrl}/login?${urlParam({ service: `${this.webUrl}/login`, next: window.location.href })}`;
+ let loginUrl = null;
+ if (this.environment.webUrl.includes('localhost')) {
+ // CAS should handle auth instead of angular, so we need to pass the next param
+ // in the service param to ensure the user is redirected back to the correct page after login
+ loginUrl = `${this.casUrl}/login?${localUrlParam({ service: `${this.webUrl.replace('4200', '5000')}/login`, next: window.location.href })}`;
+ } else {
+ loginUrl = `${this.casUrl}/login?${urlParam({ service: `${this.webUrl}/login`, next: window.location.href })}`;
+ }
window.location.href = loginUrl;
}
diff --git a/src/app/shared/helpers/url-param.helper.ts b/src/app/shared/helpers/url-param.helper.ts
index 02e7e98c8..295bd001b 100644
--- a/src/app/shared/helpers/url-param.helper.ts
+++ b/src/app/shared/helpers/url-param.helper.ts
@@ -3,3 +3,15 @@ export const urlParam = (params: Record) => {
.map((entry) => entry.map((comp) => encodeURIComponent(comp)).join('='))
.join('&');
};
+
+export const localUrlParam = (params: { service: string; next?: string }): string => {
+ const { service, next } = params;
+
+ if (!next) {
+ return `service=${encodeURIComponent(service)}`;
+ }
+
+ const encodedNext = encodeURIComponent(next);
+ const valueAfterService = `${service}?next=${encodedNext}`;
+ return `service=${encodeURIComponent(valueAfterService)}`;
+};