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({