diff --git a/src/app/core/constants/ngxs-states.constant.ts b/src/app/core/constants/ngxs-states.constant.ts index c16df5cb4..1454fd718 100644 --- a/src/app/core/constants/ngxs-states.constant.ts +++ b/src/app/core/constants/ngxs-states.constant.ts @@ -8,7 +8,8 @@ import { RegistrationsState } from '@osf/features/project/registrations/store'; import { AccountSettingsState } from '@osf/features/settings/account-settings/store/account-settings.state'; import { DeveloperAppsState } from '@osf/features/settings/developer-apps/store'; import { NotificationSubscriptionState } from '@osf/features/settings/notifications/store'; -import { AddonsState, InstitutionsState, WikiState } from '@osf/shared/stores'; +import { AddonsState, WikiState } from '@osf/shared/stores'; +import { InstitutionsState } from '@shared/stores/institutions'; import { LicensesState } from '@shared/stores/licenses'; import { MyResourcesState } from '@shared/stores/my-resources'; import { RegionsState } from '@shared/stores/regions'; diff --git a/src/app/features/home/pages/dashboard/dashboard.component.ts b/src/app/features/home/pages/dashboard/dashboard.component.ts index 66608b15f..a36a91d20 100644 --- a/src/app/features/home/pages/dashboard/dashboard.component.ts +++ b/src/app/features/home/pages/dashboard/dashboard.component.ts @@ -21,7 +21,7 @@ import { MY_PROJECTS_TABLE_PARAMS } from '@osf/shared/constants'; import { SortOrder } from '@osf/shared/enums'; import { IS_MEDIUM } from '@osf/shared/helpers'; import { MyResourcesItem, MyResourcesSearchFilters, TableParameters } from '@osf/shared/models'; -import { ClearMyResources, FetchUserInstitutions, GetMyProjects, MyResourcesSelectors } from '@shared/stores'; +import { ClearMyResources, GetMyProjects, MyResourcesSelectors } from '@shared/stores'; import { ConfirmEmailComponent } from '../../components'; @@ -73,7 +73,6 @@ export class DashboardComponent implements OnInit { ngOnInit() { this.setupQueryParamsSubscription(); - this.store.dispatch(new FetchUserInstitutions()); this.route.params.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((params) => { const userId = params['userId']; diff --git a/src/app/features/institutions/pages/institutions-list/institutions-list.component.ts b/src/app/features/institutions/pages/institutions-list/institutions-list.component.ts index b0adeb8e9..07ea9fbc9 100644 --- a/src/app/features/institutions/pages/institutions-list/institutions-list.component.ts +++ b/src/app/features/institutions/pages/institutions-list/institutions-list.component.ts @@ -30,7 +30,7 @@ import { import { TABLE_PARAMS } from '@shared/constants'; import { parseQueryFilterParams } from '@shared/helpers'; import { QueryParams } from '@shared/models'; -import { FetchInstitutions, InstitutionsSelectors } from '@shared/stores'; +import { FetchInstitutions, InstitutionsSelectors } from '@shared/stores/institutions'; @Component({ selector: 'osf-institutions-list', diff --git a/src/app/features/preprints/components/preprint-details/general-information/general-information.component.html b/src/app/features/preprints/components/preprint-details/general-information/general-information.component.html index 5b4ea4c1e..acc12f774 100644 --- a/src/app/features/preprints/components/preprint-details/general-information/general-information.component.html +++ b/src/app/features/preprints/components/preprint-details/general-information/general-information.component.html @@ -8,15 +8,7 @@

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

@if (affiliatedInstitutions().length) { -
-

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

- -
- @for (institution of affiliatedInstitutions(); track institution.id) { - Institution logo - } -
-
+ } @if (preprintValue.nodeId) { diff --git a/src/app/features/preprints/components/preprint-details/general-information/general-information.component.ts b/src/app/features/preprints/components/preprint-details/general-information/general-information.component.ts index 757e4d53e..a4a2a176c 100644 --- a/src/app/features/preprints/components/preprint-details/general-information/general-information.component.ts +++ b/src/app/features/preprints/components/preprint-details/general-information/general-information.component.ts @@ -5,7 +5,7 @@ import { TranslatePipe } from '@ngx-translate/core'; import { Card } from 'primeng/card'; import { Skeleton } from 'primeng/skeleton'; -import { ChangeDetectionStrategy, Component, computed, effect, input, OnDestroy, output, signal } from '@angular/core'; +import { ChangeDetectionStrategy, Component, computed, effect, input, OnDestroy, output } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { RouterLink } from '@angular/router'; @@ -13,10 +13,10 @@ import { PreprintDoiSectionComponent } from '@osf/features/preprints/components/ import { ApplicabilityStatus, PreregLinkInfo } from '@osf/features/preprints/enums'; import { PreprintProviderDetails } from '@osf/features/preprints/models'; import { FetchPreprintById, PreprintSelectors } from '@osf/features/preprints/store/preprint'; -import { IconComponent, TruncatedTextComponent } from '@shared/components'; +import { AffiliatedInstitutionsViewComponent, IconComponent, TruncatedTextComponent } from '@shared/components'; import { ResourceType } from '@shared/enums'; -import { Institution } from '@shared/models'; import { ContributorsSelectors, GetAllContributors, ResetContributorsState } from '@shared/stores'; +import { FetchResourceInstitutions, InstitutionsSelectors } from '@shared/stores/institutions'; import { environment } from 'src/environments/environment'; @@ -31,6 +31,7 @@ import { environment } from 'src/environments/environment'; PreprintDoiSectionComponent, RouterLink, IconComponent, + AffiliatedInstitutionsViewComponent, ], templateUrl: './general-information.component.html', styleUrl: './general-information.component.scss', @@ -44,6 +45,7 @@ export class GeneralInformationComponent implements OnDestroy { getContributors: GetAllContributors, resetContributorsState: ResetContributorsState, fetchPreprintById: FetchPreprintById, + fetchResourceInstitutions: FetchResourceInstitutions, }); protected readonly environment = environment; @@ -53,8 +55,7 @@ export class GeneralInformationComponent implements OnDestroy { preprint = select(PreprintSelectors.getPreprint); isPreprintLoading = select(PreprintSelectors.isPreprintLoading); - //[RNi] TODO: Implement when institutions available - affiliatedInstitutions = signal([]); + affiliatedInstitutions = select(InstitutionsSelectors.getResourceInstitutions); contributors = select(ContributorsSelectors.getContributors); areContributorsLoading = select(ContributorsSelectors.isContributorsLoading); @@ -74,6 +75,7 @@ export class GeneralInformationComponent implements OnDestroy { if (!preprint) return; this.actions.getContributors(this.preprint()!.id, ResourceType.Preprint); + this.actions.fetchResourceInstitutions(this.preprint()!.id, ResourceType.Preprint); }); } diff --git a/src/app/features/preprints/components/stepper/metadata-step/metadata-step.component.html b/src/app/features/preprints/components/stepper/metadata-step/metadata-step.component.html index a7538e5eb..e5a5af081 100644 --- a/src/app/features/preprints/components/stepper/metadata-step/metadata-step.component.html +++ b/src/app/features/preprints/components/stepper/metadata-step/metadata-step.component.html @@ -41,6 +41,10 @@

{{ 'preprints.preprintStepper.metadata.publicationDoi.title' | translate }}< +
+ +
+
diff --git a/src/app/features/preprints/components/stepper/metadata-step/metadata-step.component.ts b/src/app/features/preprints/components/stepper/metadata-step/metadata-step.component.ts index bb3372240..be4dc02d2 100644 --- a/src/app/features/preprints/components/stepper/metadata-step/metadata-step.component.ts +++ b/src/app/features/preprints/components/stepper/metadata-step/metadata-step.component.ts @@ -9,12 +9,11 @@ import { InputText } from 'primeng/inputtext'; import { Message } from 'primeng/message'; import { Tooltip } from 'primeng/tooltip'; -import { ChangeDetectionStrategy, Component, inject, OnInit, output } from '@angular/core'; +import { ChangeDetectionStrategy, Component, inject, input, OnInit, output } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; -import { PreprintsSubjectsComponent } from '@osf/features/preprints/components/stepper/metadata-step/preprints-subjects/preprints-subjects.component'; import { formInputLimits } from '@osf/features/preprints/constants'; -import { MetadataForm, Preprint } from '@osf/features/preprints/models'; +import { MetadataForm, Preprint, PreprintProviderDetails } from '@osf/features/preprints/models'; import { CreatePreprint, FetchLicenses, @@ -29,6 +28,8 @@ import { License, LicenseOptions } from '@shared/models'; import { CustomConfirmationService, ToastService } from '@shared/services'; import { ContributorsComponent } from './contributors/contributors.component'; +import { PreprintsAffiliatedInstitutionsComponent } from './preprints-affiliated-institutions/preprints-affiliated-institutions.component'; +import { PreprintsSubjectsComponent } from './preprints-subjects/preprints-subjects.component'; @Component({ selector: 'osf-preprint-metadata', @@ -47,6 +48,7 @@ import { ContributorsComponent } from './contributors/contributors.component'; LicenseComponent, TagsInputComponent, PreprintsSubjectsComponent, + PreprintsAffiliatedInstitutionsComponent, ], templateUrl: './metadata-step.component.html', styleUrl: './metadata-step.component.scss', @@ -71,6 +73,7 @@ export class MetadataStepComponent implements OnInit { createdPreprint = select(PreprintStepperSelectors.getPreprint); isUpdatingPreprint = select(PreprintStepperSelectors.isPreprintSubmitting); + provider = input.required(); nextClicked = output(); backClicked = output(); diff --git a/src/app/features/preprints/components/stepper/metadata-step/preprints-affiliated-institutions/preprints-affiliated-institutions.component.html b/src/app/features/preprints/components/stepper/metadata-step/preprints-affiliated-institutions/preprints-affiliated-institutions.component.html new file mode 100644 index 000000000..513d64186 --- /dev/null +++ b/src/app/features/preprints/components/stepper/metadata-step/preprints-affiliated-institutions/preprints-affiliated-institutions.component.html @@ -0,0 +1,18 @@ + +
+

{{ 'preprints.preprintStepper.metadata.affiliatedInstitutionsTitle' | translate }}

+

+ {{ + 'preprints.preprintStepper.metadata.affiliatedInstitutionsDescription' + | translate: { preprintWord: provider()?.preprintWord } + }} +

+ +
+ +
+
+
diff --git a/src/app/features/preprints/components/stepper/metadata-step/preprints-affiliated-institutions/preprints-affiliated-institutions.component.scss b/src/app/features/preprints/components/stepper/metadata-step/preprints-affiliated-institutions/preprints-affiliated-institutions.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/features/preprints/components/stepper/metadata-step/preprints-affiliated-institutions/preprints-affiliated-institutions.component.spec.ts b/src/app/features/preprints/components/stepper/metadata-step/preprints-affiliated-institutions/preprints-affiliated-institutions.component.spec.ts new file mode 100644 index 000000000..031eda263 --- /dev/null +++ b/src/app/features/preprints/components/stepper/metadata-step/preprints-affiliated-institutions/preprints-affiliated-institutions.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PreprintsAffiliatedInstitutionsComponent } from './preprints-affiliated-institutions.component'; + +describe.skip('PreprintsAffiliatedInstitutionsComponent', () => { + let component: PreprintsAffiliatedInstitutionsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PreprintsAffiliatedInstitutionsComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(PreprintsAffiliatedInstitutionsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/preprints/components/stepper/metadata-step/preprints-affiliated-institutions/preprints-affiliated-institutions.component.ts b/src/app/features/preprints/components/stepper/metadata-step/preprints-affiliated-institutions/preprints-affiliated-institutions.component.ts new file mode 100644 index 000000000..bf64dbebb --- /dev/null +++ b/src/app/features/preprints/components/stepper/metadata-step/preprints-affiliated-institutions/preprints-affiliated-institutions.component.ts @@ -0,0 +1,38 @@ +import { createDispatchMap } from '@ngxs/store'; + +import { TranslatePipe } from '@ngx-translate/core'; + +import { Card } from 'primeng/card'; + +import { ChangeDetectionStrategy, Component, input, OnInit } from '@angular/core'; + +import { PreprintProviderDetails } from '@osf/features/preprints/models'; +import { AffiliatedInstitutionSelectComponent } from '@shared/components'; +import { ResourceType } from '@shared/enums'; +import { Institution } from '@shared/models'; +import { FetchResourceInstitutions, UpdateResourceInstitutions } from '@shared/stores/institutions'; + +@Component({ + selector: 'osf-preprints-affiliated-institutions', + imports: [AffiliatedInstitutionSelectComponent, Card, TranslatePipe], + templateUrl: './preprints-affiliated-institutions.component.html', + styleUrl: './preprints-affiliated-institutions.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class PreprintsAffiliatedInstitutionsComponent implements OnInit { + preprintId = input(); + provider = input.required(); + + private actions = createDispatchMap({ + fetchResourceInstitutions: FetchResourceInstitutions, + updateResourceInstitutions: UpdateResourceInstitutions, + }); + + ngOnInit() { + this.actions.fetchResourceInstitutions(this.preprintId()!, ResourceType.Preprint); + } + + institutionsSelected(institutions: Institution[]) { + this.actions.updateResourceInstitutions(this.preprintId()!, ResourceType.Preprint, institutions); + } +} diff --git a/src/app/features/preprints/components/stepper/review-step/review-step.component.html b/src/app/features/preprints/components/stepper/review-step/review-step.component.html index a1448ae20..42b38be03 100644 --- a/src/app/features/preprints/components/stepper/review-step/review-step.component.html +++ b/src/app/features/preprints/components/stepper/review-step/review-step.component.html @@ -63,15 +63,7 @@

{{ 'preprints.preprintStepper.review.sections.metadata.contributors' | trans @if (affiliatedInstitutions().length) { -
-

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

- -
- @for (institution of affiliatedInstitutions(); track institution.id) { - Institution logo - } -
-
+ }
diff --git a/src/app/features/preprints/components/stepper/review-step/review-step.component.ts b/src/app/features/preprints/components/stepper/review-step/review-step.component.ts index e4a131298..bb01c7d70 100644 --- a/src/app/features/preprints/components/stepper/review-step/review-step.component.ts +++ b/src/app/features/preprints/components/stepper/review-step/review-step.component.ts @@ -8,7 +8,7 @@ import { Card } from 'primeng/card'; import { Tag } from 'primeng/tag'; import { DatePipe, TitleCasePipe } from '@angular/common'; -import { ChangeDetectionStrategy, Component, computed, inject, input, OnInit, signal } from '@angular/core'; +import { ChangeDetectionStrategy, Component, computed, inject, input, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { ApplicabilityStatus, PreregLinkInfo, ReviewsState } from '@osf/features/preprints/enums'; @@ -19,12 +19,12 @@ import { PreprintStepperSelectors, SubmitPreprint, } from '@osf/features/preprints/store/preprint-stepper'; -import { TruncatedTextComponent } from '@shared/components'; +import { AffiliatedInstitutionsViewComponent, TruncatedTextComponent } from '@shared/components'; import { ResourceType } from '@shared/enums'; -import { Institution } from '@shared/models'; import { InterpolatePipe } from '@shared/pipes'; import { ToastService } from '@shared/services'; import { ContributorsSelectors, FetchSelectedSubjects, GetAllContributors, SubjectsSelectors } from '@shared/stores'; +import { FetchResourceInstitutions, InstitutionsSelectors } from '@shared/stores/institutions'; @Component({ selector: 'osf-review-step', @@ -41,6 +41,7 @@ import { ContributorsSelectors, FetchSelectedSubjects, GetAllContributors, Subje AccordionHeader, AccordionPanel, InterpolatePipe, + AffiliatedInstitutionsViewComponent, ], templateUrl: './review-step.component.html', styleUrl: './review-step.component.scss', @@ -55,6 +56,7 @@ export class ReviewStepComponent implements OnInit { fetchLicenses: FetchLicenses, fetchPreprintProject: FetchPreprintProject, submitPreprint: SubmitPreprint, + fetchResourceInstitutions: FetchResourceInstitutions, }); provider = input.required(); preprint = select(PreprintStepperSelectors.getPreprint); @@ -65,7 +67,7 @@ export class ReviewStepComponent implements OnInit { return this.contributors().filter((contributor) => contributor.isBibliographic); }); subjects = select(SubjectsSelectors.getSelectedSubjects); - affiliatedInstitutions = signal([]); + affiliatedInstitutions = select(InstitutionsSelectors.getResourceInstitutions); license = select(PreprintStepperSelectors.getPreprintLicense); preprintProject = select(PreprintStepperSelectors.getPreprintProject); licenseOptionsRecord = computed(() => { @@ -80,6 +82,7 @@ export class ReviewStepComponent implements OnInit { this.actions.fetchSubjects(this.preprint()!.id, ResourceType.Preprint); this.actions.fetchLicenses(); this.actions.fetchPreprintProject(); + this.actions.fetchResourceInstitutions(this.preprint()!.id, ResourceType.Preprint); } submitPreprint() { diff --git a/src/app/features/preprints/pages/submit-preprint-stepper/submit-preprint-stepper.component.html b/src/app/features/preprints/pages/submit-preprint-stepper/submit-preprint-stepper.component.html index a970fb38c..d700c0810 100644 --- a/src/app/features/preprints/pages/submit-preprint-stepper/submit-preprint-stepper.component.html +++ b/src/app/features/preprints/pages/submit-preprint-stepper/submit-preprint-stepper.component.html @@ -43,7 +43,11 @@

/> } @case (SubmitStepsEnum.Metadata) { - + } @case (SubmitStepsEnum.AuthorAssertions) { diff --git a/src/app/features/preprints/pages/update-preprint-stepper/update-preprint-stepper.component.html b/src/app/features/preprints/pages/update-preprint-stepper/update-preprint-stepper.component.html index 093754596..7528acfe6 100644 --- a/src/app/features/preprints/pages/update-preprint-stepper/update-preprint-stepper.component.html +++ b/src/app/features/preprints/pages/update-preprint-stepper/update-preprint-stepper.component.html @@ -36,7 +36,11 @@

} @case (PreprintSteps.Metadata) { - + } @case (PreprintSteps.AuthorAssertions) { diff --git a/src/app/features/project/metadata/mappers/project-metadata.mapper.ts b/src/app/features/project/metadata/mappers/project-metadata.mapper.ts index c2ea123b4..4ec6b35bb 100644 --- a/src/app/features/project/metadata/mappers/project-metadata.mapper.ts +++ b/src/app/features/project/metadata/mappers/project-metadata.mapper.ts @@ -1,4 +1,6 @@ import { ProjectOverview, ProjectOverviewContributor } from '@osf/features/project/overview/models'; +import { InstitutionsMapper } from '@shared/mappers'; +import { InstitutionsJsonApiResponse } from '@shared/models'; export class ProjectMetadataMapper { static fromMetadataApiResponse(response: Record): ProjectOverview { @@ -40,6 +42,9 @@ export class ProjectMetadataMapper { isCollection: attributes['collection'] as boolean, accessRequestsEnabled: attributes['access_requests_enabled'] as boolean, wikiEnabled: attributes['wiki_enabled'] as boolean, + affiliatedInstitutions: InstitutionsMapper.fromInstitutionsResponse( + embeds['affiliated_institutions'] as InstitutionsJsonApiResponse + ), currentUserCanComment: attributes['current_user_can_comment'] as boolean, currentUserPermissions: (attributes['current_user_permissions'] as string[]) || [], currentUserIsContributor: attributes['current_user_is_contributor'] as boolean, diff --git a/src/app/features/project/metadata/services/metadata.service.ts b/src/app/features/project/metadata/services/metadata.service.ts index effa0ec31..2905e7d26 100644 --- a/src/app/features/project/metadata/services/metadata.service.ts +++ b/src/app/features/project/metadata/services/metadata.service.ts @@ -81,7 +81,6 @@ export class MetadataService { getProjectForMetadata(projectId: string): Observable { const params: Record = { 'embed[]': ['contributors', 'affiliated_institutions', 'identifiers', 'license', 'subjects_acceptable'], - 'fields[institutions]': 'assets,description,name', 'fields[users]': 'family_name,full_name,given_name,middle_name', 'fields[subjects]': 'text,taxonomy', }; diff --git a/src/app/features/project/overview/components/add-component-dialog/add-component-dialog.component.html b/src/app/features/project/overview/components/add-component-dialog/add-component-dialog.component.html index 664cd9c6a..28e444a87 100644 --- a/src/app/features/project/overview/components/add-component-dialog/add-component-dialog.component.html +++ b/src/app/features/project/overview/components/add-component-dialog/add-component-dialog.component.html @@ -42,7 +42,7 @@

[inputId]="affiliation.id" [name]="'affiliations'" /> - OSF Logo + OSF Logo } diff --git a/src/app/features/project/overview/mappers/project-overview.mapper.ts b/src/app/features/project/overview/mappers/project-overview.mapper.ts index 5f69d1907..7488be1d0 100644 --- a/src/app/features/project/overview/mappers/project-overview.mapper.ts +++ b/src/app/features/project/overview/mappers/project-overview.mapper.ts @@ -1,3 +1,4 @@ +import { InstitutionsMapper } from '@shared/mappers'; import { License } from '@shared/models'; import { ProjectOverview, ProjectOverviewGetResponseJsoApi } from '../models'; @@ -44,13 +45,7 @@ export class ProjectOverviewMapper { middleName: contributor.embeds.users.data.attributes.middle_name, type: contributor.embeds.users.data.type, })), - affiliatedInstitutions: response.embeds.affiliated_institutions?.data.map((institution) => ({ - id: institution.id, - type: institution.type, - logo: institution.attributes.assets.logo, - description: institution.attributes.description, - name: institution.attributes.name, - })), + affiliatedInstitutions: InstitutionsMapper.fromInstitutionsResponse(response.embeds.affiliated_institutions), identifiers: response.embeds.identifiers?.data.map((identifier) => ({ id: identifier.id, type: identifier.type, diff --git a/src/app/features/project/overview/models/project-overview.models.ts b/src/app/features/project/overview/models/project-overview.models.ts index b5f1f3b86..94440b041 100644 --- a/src/app/features/project/overview/models/project-overview.models.ts +++ b/src/app/features/project/overview/models/project-overview.models.ts @@ -1,5 +1,5 @@ import { UserPermissions } from '@osf/shared/enums'; -import { JsonApiResponse } from '@osf/shared/models'; +import { Institution, InstitutionsJsonApiResponse, JsonApiResponse } from '@osf/shared/models'; import { License } from '@shared/models'; export interface ProjectOverviewContributor { @@ -54,7 +54,7 @@ export interface ProjectOverview { id: string; type: string; }; - affiliatedInstitutions?: ProjectAffiliatedInstitutions[]; + affiliatedInstitutions?: Institution[]; forksCount: number; viewOnlyLinksCount: number; links: { @@ -100,19 +100,7 @@ export interface ProjectOverviewGetResponseJsoApi { custom_citation: string | null; }; embeds: { - affiliated_institutions: { - data: { - id: string; - type: string; - attributes: { - assets: { - logo: string; - }; - description: string; - name: string; - }; - }[]; - }; + affiliated_institutions: InstitutionsJsonApiResponse; identifiers: { data: { id: string; @@ -228,14 +216,6 @@ export interface ProjectIdentifiers { value: string; } -export interface ProjectAffiliatedInstitutions { - id: string; - type: string; - name: string; - description: string; - logo: string; -} - export interface ProjectSupplements { id: string; type: string; diff --git a/src/app/features/registry/models/get-registry-institutions-json-api.model.ts b/src/app/features/registry/models/get-registry-institutions-json-api.model.ts deleted file mode 100644 index 7ca64bae5..000000000 --- a/src/app/features/registry/models/get-registry-institutions-json-api.model.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ApiData, JsonApiResponse } from '@shared/models'; - -export type GetRegistryInstitutionsJsonApi = JsonApiResponse< - ApiData[], - null ->; - -export interface RegistryInstitutionsAttributes { - assets: { - banner: string; - logo: string; - logo_rounded: string; - }; -} diff --git a/src/app/features/registry/models/index.ts b/src/app/features/registry/models/index.ts index 36d0bdff4..cb8464af7 100644 --- a/src/app/features/registry/models/index.ts +++ b/src/app/features/registry/models/index.ts @@ -1,5 +1,4 @@ export * from './bibliographic-contributors.models'; -export * from './get-registry-institutions-json-api.model'; export * from './get-registry-overview-json-api.model'; export * from './get-resource-subjects-json-api.model'; export * from './linked-nodes.models'; @@ -9,8 +8,6 @@ export * from './linked-response.models'; export * from './registry-components.models'; export * from './registry-components-json-api.model'; export * from './registry-contributor-json-api.model'; -export * from './registry-institution.model'; -export * from './registry-institutions-json-api.model'; export * from './registry-metadata.models'; export * from './registry-overview.models'; export * from './registry-subject.model'; @@ -22,4 +19,3 @@ export * from './resources/get-registry-resources-json-api.model'; export * from './resources/get-registry-resources-json-api.model'; export * from './resources/registry-resource.model'; export * from './resources/registry-resource.model'; -export * from './view-schema-block.model'; diff --git a/src/app/features/registry/models/registry-institution.model.ts b/src/app/features/registry/models/registry-institution.model.ts deleted file mode 100644 index 786c3397f..000000000 --- a/src/app/features/registry/models/registry-institution.model.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface RegistryInstitution { - id: string; - logo: string; - logoRounded?: string; -} diff --git a/src/app/features/registry/models/registry-institutions-json-api.model.ts b/src/app/features/registry/models/registry-institutions-json-api.model.ts deleted file mode 100644 index e44a7f77a..000000000 --- a/src/app/features/registry/models/registry-institutions-json-api.model.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { MetaJsonApi } from '@osf/shared/models'; - -export interface RegistryInstitutionJsonApi { - id: string; - type: string; - attributes: { - name: string; - }; - links: { - self: string; - html: string; - iri: string; - }; -} - -export interface RegistryInstitutionsJsonApiResponse { - data: RegistryInstitutionJsonApi[]; - links: { - first: string | null; - last: string | null; - prev: string | null; - next: string | null; - }; - meta: MetaJsonApi; -} diff --git a/src/app/features/registry/models/view-schema-block.model.ts b/src/app/features/registry/models/view-schema-block.model.ts deleted file mode 100644 index 432753f5d..000000000 --- a/src/app/features/registry/models/view-schema-block.model.ts +++ /dev/null @@ -1,8 +0,0 @@ -export interface ViewSchemaBlock { - value: string; - values: string[]; - files?: { id: string; name: string }[]; - required: boolean; - html?: string; - type: string; -} diff --git a/src/app/features/registry/pages/registry-metadata/registry-metadata.component.ts b/src/app/features/registry/pages/registry-metadata/registry-metadata.component.ts index e0056983f..03ab6a8c2 100644 --- a/src/app/features/registry/pages/registry-metadata/registry-metadata.component.ts +++ b/src/app/features/registry/pages/registry-metadata/registry-metadata.component.ts @@ -432,15 +432,9 @@ export class RegistryMetadataComponent implements OnInit { const registry = this.currentRegistry(); const institutions = this.institutions(); - const institutionsFormatted = - institutions?.map((inst) => ({ - id: inst.id, - name: inst.attributes.name, - })) || []; - return { ...registry, - institutions: institutionsFormatted, + institutions, } as unknown as ProjectOverview; } diff --git a/src/app/features/registry/services/registry-metadata.service.ts b/src/app/features/registry/services/registry-metadata.service.ts index 0ca2e11b1..c472629b7 100644 --- a/src/app/features/registry/services/registry-metadata.service.ts +++ b/src/app/features/registry/services/registry-metadata.service.ts @@ -9,7 +9,8 @@ import { CedarMetadataTemplateJsonApi, } from '@osf/features/project/metadata/models'; import { JsonApiService } from '@osf/shared/services'; -import { License } from '@shared/models'; +import { InstitutionsMapper } from '@shared/mappers'; +import { Institution, InstitutionsJsonApiResponse, License } from '@shared/models'; import { RegistryMetadataMapper } from '../mappers'; import { @@ -20,7 +21,6 @@ import { RegistryContributorAddRequest, RegistryContributorJsonApiResponse, RegistryContributorUpdateRequest, - RegistryInstitutionsJsonApiResponse, RegistryOverview, RegistrySubjectsJsonApi, UserInstitutionsResponse, @@ -189,18 +189,15 @@ export class RegistryMetadataService { ); } - getRegistryInstitutions( - registryId: string, - page = 1, - pageSize = 100 - ): Observable { + getRegistryInstitutions(registryId: string, page = 1, pageSize = 100): Observable { const params: Record = { - 'fields[institutions]': 'name', page: page, 'page[size]': pageSize, }; - return this.jsonApiService.get(`${this.apiUrl}/registrations/${registryId}/institutions/`, params); + return this.jsonApiService + .get(`${this.apiUrl}/registrations/${registryId}/institutions/`, params) + .pipe(map((response) => InstitutionsMapper.fromInstitutionsResponse(response))); } updateRegistryContributor( diff --git a/src/app/features/registry/services/registry-overview.service.ts b/src/app/features/registry/services/registry-overview.service.ts index 900abd0e7..933c04f65 100644 --- a/src/app/features/registry/services/registry-overview.service.ts +++ b/src/app/features/registry/services/registry-overview.service.ts @@ -6,17 +6,15 @@ import { RegistryModerationMapper } from '@osf/features/moderation/mappers'; import { ReviewAction, ReviewActionsResponseJsonApi } from '@osf/features/moderation/models'; import { MapRegistryOverview } from '@osf/features/registry/mappers'; import { - GetRegistryInstitutionsJsonApi, GetRegistryOverviewJsonApi, GetResourceSubjectsJsonApi, - RegistryInstitution, RegistryOverview, RegistryOverviewJsonApiData, RegistrySubject, } from '@osf/features/registry/models'; -import { ReviewActionsMapper } from '@osf/shared/mappers'; +import { InstitutionsMapper, ReviewActionsMapper } from '@osf/shared/mappers'; import { PageSchemaMapper } from '@osf/shared/mappers/registration'; -import { PageSchema, SchemaBlocksResponseJsonApi } from '@osf/shared/models'; +import { Institution, InstitutionsJsonApiResponse, PageSchema, SchemaBlocksResponseJsonApi } from '@osf/shared/models'; import { ReviewActionPayload } from '@osf/shared/models/review-action'; import { JsonApiService } from '@shared/services'; @@ -59,22 +57,14 @@ export class RegistryOverviewService { .pipe(map((response) => response.data.map((subject) => ({ id: subject.id, text: subject.attributes.text })))); } - getInstitutions(registryId: string): Observable { + getInstitutions(registryId: string): Observable { const params = { 'page[size]': 100, }; return this.jsonApiService - .get(`${environment.apiUrl}/registrations/${registryId}/institutions/`, params) - .pipe( - map((response) => - response.data.map((institution) => ({ - id: institution.id, - logo: institution.attributes.assets.logo, - logoRounded: institution.attributes.assets.logo_rounded, - })) - ) - ); + .get(`${environment.apiUrl}/registrations/${registryId}/institutions/`, params) + .pipe(map((response) => InstitutionsMapper.fromInstitutionsResponse(response))); } getSchemaBlocks(schemaLink: string): Observable { @@ -84,7 +74,7 @@ export class RegistryOverviewService { }; return this.jsonApiService - .get(`${schemaLink}schema_blocks`, params) + .get(`${schemaLink}schema_blocks/`, params) .pipe(map((response) => PageSchemaMapper.fromSchemaBlocksResponse(response))); } diff --git a/src/app/features/registry/store/registry-metadata/registry-metadata.model.ts b/src/app/features/registry/store/registry-metadata/registry-metadata.model.ts index a55821781..75138ab22 100644 --- a/src/app/features/registry/store/registry-metadata/registry-metadata.model.ts +++ b/src/app/features/registry/store/registry-metadata/registry-metadata.model.ts @@ -3,12 +3,11 @@ import { CedarMetadataRecordData, CedarMetadataTemplateJsonApi, } from '@osf/features/project/metadata/models'; -import { AsyncStateModel, License } from '@shared/models'; +import { AsyncStateModel, Institution, License } from '@shared/models'; import { BibliographicContributor, CustomItemMetadataRecord, - RegistryInstitutionJsonApi, RegistryOverview, RegistrySubjectData, UserInstitution, @@ -19,7 +18,7 @@ export interface RegistryMetadataStateModel { bibliographicContributors: AsyncStateModel; customItemMetadata: AsyncStateModel; userInstitutions: AsyncStateModel; - institutions: AsyncStateModel; + institutions: AsyncStateModel; subjects: AsyncStateModel; cedarTemplates: AsyncStateModel; cedarRecord: AsyncStateModel; diff --git a/src/app/features/registry/store/registry-metadata/registry-metadata.state.ts b/src/app/features/registry/store/registry-metadata/registry-metadata.state.ts index b8717ccb9..5fe6151da 100644 --- a/src/app/features/registry/store/registry-metadata/registry-metadata.state.ts +++ b/src/app/features/registry/store/registry-metadata/registry-metadata.state.ts @@ -144,10 +144,10 @@ export class RegistryMetadataState { }); return this.registryMetadataService.getRegistryInstitutions(action.registryId, action.page, action.pageSize).pipe( - tap((response) => { + tap((institutions) => { ctx.patchState({ institutions: { - data: response.data, + data: institutions, isLoading: false, error: null, }, diff --git a/src/app/features/registry/store/registry-overview/registry-overview.model.ts b/src/app/features/registry/store/registry-overview/registry-overview.model.ts index 6d25c5715..64b23328e 100644 --- a/src/app/features/registry/store/registry-overview/registry-overview.model.ts +++ b/src/app/features/registry/store/registry-overview/registry-overview.model.ts @@ -1,12 +1,12 @@ import { ReviewAction } from '@osf/features/moderation/models'; -import { RegistryInstitution, RegistryOverview, RegistrySubject } from '@osf/features/registry/models'; -import { PageSchema } from '@osf/shared/models'; +import { RegistryOverview, RegistrySubject } from '@osf/features/registry/models'; +import { Institution, PageSchema } from '@osf/shared/models'; import { AsyncStateModel } from '@shared/models'; export interface RegistryOverviewStateModel { registry: AsyncStateModel; subjects: AsyncStateModel; - institutions: AsyncStateModel; + institutions: AsyncStateModel; schemaBlocks: AsyncStateModel; moderationActions: AsyncStateModel; } diff --git a/src/app/features/registry/store/registry-overview/registry-overview.selectors.ts b/src/app/features/registry/store/registry-overview/registry-overview.selectors.ts index 3b3913f95..05c6b4bc7 100644 --- a/src/app/features/registry/store/registry-overview/registry-overview.selectors.ts +++ b/src/app/features/registry/store/registry-overview/registry-overview.selectors.ts @@ -1,8 +1,8 @@ import { Selector } from '@ngxs/store'; import { ReviewAction } from '@osf/features/moderation/models'; -import { RegistryInstitution, RegistryOverview, RegistrySubject } from '@osf/features/registry/models'; -import { PageSchema } from '@osf/shared/models'; +import { RegistryOverview, RegistrySubject } from '@osf/features/registry/models'; +import { Institution, PageSchema } from '@osf/shared/models'; import { RegistryOverviewStateModel } from './registry-overview.model'; import { RegistryOverviewState } from './registry-overview.state'; @@ -29,7 +29,7 @@ export class RegistryOverviewSelectors { } @Selector([RegistryOverviewState]) - static getInstitutions(state: RegistryOverviewStateModel): RegistryInstitution[] | null { + static getInstitutions(state: RegistryOverviewStateModel): Institution[] | null { return state.institutions.data; } diff --git a/src/app/shared/components/add-project-form/add-project-form.component.html b/src/app/shared/components/add-project-form/add-project-form.component.html index 45df6704c..ee76fb2c5 100644 --- a/src/app/shared/components/add-project-form/add-project-form.component.html +++ b/src/app/shared/components/add-project-form/add-project-form.component.html @@ -20,43 +20,15 @@ /> - @if (affiliations().length) { -
-
-

- {{ 'myProjects.createProject.affiliation.title' | translate }} -

-
- - -
-
- -
- @for (affiliation of affiliations(); track affiliation.id) { -
- - OSF Logo -
- } -
-
- } +
+

+ {{ 'myProjects.createProject.affiliation.title' | translate }} +

+ +
-
-

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

- -
- @if (resource.affiliatedInstitutions?.length) { - @for (institution of resource.affiliatedInstitutions; track institution.id) { - institution logo - } - } @else { -

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

- } -
-
+ @if (resource.type === resourceTypes.Projects) { diff --git a/src/app/shared/components/resource-metadata/resource-metadata.component.ts b/src/app/shared/components/resource-metadata/resource-metadata.component.ts index e02b71111..e4dce6506 100644 --- a/src/app/shared/components/resource-metadata/resource-metadata.component.ts +++ b/src/app/shared/components/resource-metadata/resource-metadata.component.ts @@ -8,11 +8,12 @@ import { ChangeDetectionStrategy, Component, input, output } from '@angular/core import { RouterLink } from '@angular/router'; import { OverviewCollectionsComponent } from '@osf/features/project/overview/components/overview-collections/overview-collections.component'; -import { ResourceCitationsComponent } from '@shared/components/resource-citations/resource-citations.component'; -import { TruncatedTextComponent } from '@shared/components/truncated-text/truncated-text.component'; +import { AffiliatedInstitutionsViewComponent, TruncatedTextComponent } from '@shared/components'; import { OsfResourceTypes } from '@shared/constants'; import { ResourceOverview } from '@shared/models'; +import { ResourceCitationsComponent } from '../resource-citations/resource-citations.component'; + @Component({ selector: 'osf-resource-metadata', imports: [ @@ -24,6 +25,7 @@ import { ResourceOverview } from '@shared/models'; DatePipe, ResourceCitationsComponent, OverviewCollectionsComponent, + AffiliatedInstitutionsViewComponent, ], templateUrl: './resource-metadata.component.html', styleUrl: './resource-metadata.component.scss', diff --git a/src/app/shared/components/shared-metadata/components/project-metadata-affiliated-institutions/project-metadata-affiliated-institutions.component.html b/src/app/shared/components/shared-metadata/components/project-metadata-affiliated-institutions/project-metadata-affiliated-institutions.component.html index af6b5d311..4a994d1a5 100644 --- a/src/app/shared/components/shared-metadata/components/project-metadata-affiliated-institutions/project-metadata-affiliated-institutions.component.html +++ b/src/app/shared/components/shared-metadata/components/project-metadata-affiliated-institutions/project-metadata-affiliated-institutions.component.html @@ -11,25 +11,5 @@

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

} - @if (affiliatedInstitutions()) { -
- @for (institution of affiliatedInstitutions(); track institution.id) { -
- @if (institution.logo) { - - } -
-

{{ institution.name }}

- @if (institution.description) { -

{{ institution.description }}

- } -
-
- } -
- } @else { -
-

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

-
- } + diff --git a/src/app/shared/components/shared-metadata/components/project-metadata-affiliated-institutions/project-metadata-affiliated-institutions.component.ts b/src/app/shared/components/shared-metadata/components/project-metadata-affiliated-institutions/project-metadata-affiliated-institutions.component.ts index fe0c2fa0d..d11c5c11c 100644 --- a/src/app/shared/components/shared-metadata/components/project-metadata-affiliated-institutions/project-metadata-affiliated-institutions.component.ts +++ b/src/app/shared/components/shared-metadata/components/project-metadata-affiliated-institutions/project-metadata-affiliated-institutions.component.ts @@ -5,17 +5,18 @@ import { Card } from 'primeng/card'; import { ChangeDetectionStrategy, Component, input, output } from '@angular/core'; -import { ProjectAffiliatedInstitutions } from '@osf/features/project/overview/models'; +import { AffiliatedInstitutionsViewComponent } from '@shared/components'; +import { Institution } from '@shared/models'; @Component({ selector: 'osf-project-metadata-affiliated-institutions', - imports: [Button, Card, TranslatePipe], + imports: [Button, Card, TranslatePipe, AffiliatedInstitutionsViewComponent], templateUrl: './project-metadata-affiliated-institutions.component.html', changeDetection: ChangeDetectionStrategy.OnPush, }) export class ProjectMetadataAffiliatedInstitutionsComponent { openEditAffiliatedInstitutionsDialog = output(); - affiliatedInstitutions = input([]); + affiliatedInstitutions = input([]); readonly = input(false); } diff --git a/src/app/shared/mappers/institutions/general-institution.mapper.ts b/src/app/shared/mappers/institutions/general-institution.mapper.ts deleted file mode 100644 index 065edc5b0..000000000 --- a/src/app/shared/mappers/institutions/general-institution.mapper.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { FetchInstitutionsJsonApi, GetGeneralInstitutionsResponse, Institution, InstitutionData } from '@shared/models'; - -export class GeneralInstitutionMapper { - static adaptInstitution(data: InstitutionData): Institution { - return { - id: data.id, - type: data.type, - name: data.attributes.name, - description: data.attributes.description, - iri: data.attributes.iri, - rorIri: data.attributes.ror_iri, - iris: data.attributes.iris, - assets: data.attributes.assets, - institutionalRequestAccessEnabled: data.attributes.institutional_request_access_enabled, - logoPath: data.attributes.logo_path, - userMetricsUrl: data.relationships.user_metrics.links.related.href, - linkToExternalReportsArchive: data.attributes.link_to_external_reports_archive, - }; - } - - static adaptInstitutions(response: FetchInstitutionsJsonApi): GetGeneralInstitutionsResponse { - return { - data: response.data.map((institution) => this.adaptInstitution(institution)), - total: response.meta.total, - }; - } -} diff --git a/src/app/shared/mappers/institutions/index.ts b/src/app/shared/mappers/institutions/index.ts index 7931a9fad..d43e7a986 100644 --- a/src/app/shared/mappers/institutions/index.ts +++ b/src/app/shared/mappers/institutions/index.ts @@ -1,2 +1 @@ -export * from './general-institution.mapper'; -export * from './user-institutions.mapper'; +export * from './institutions.mapper'; diff --git a/src/app/shared/mappers/institutions/institutions.mapper.ts b/src/app/shared/mappers/institutions/institutions.mapper.ts new file mode 100644 index 000000000..1c5ddb58e --- /dev/null +++ b/src/app/shared/mappers/institutions/institutions.mapper.ts @@ -0,0 +1,37 @@ +import { + Institution, + InstitutionDataJsonApi, + InstitutionsJsonApiResponse, + InstitutionsWithMetaJsonApiResponse, + InstitutionsWithTotalCount, +} from '@shared/models'; + +export class InstitutionsMapper { + static fromInstitutionsResponse(response: InstitutionsJsonApiResponse): Institution[] { + return response.data.map((data) => this.fromInstitutionData(data)); + } + + static fromInstitutionData(data: InstitutionDataJsonApi): Institution { + return { + id: data.id, + type: data.type, + name: data.attributes.name, + description: data.attributes.description, + iri: data.attributes.iri, + rorIri: data.attributes.ror_iri, + iris: data.attributes.iris, + assets: data.attributes.assets, + institutionalRequestAccessEnabled: data.attributes.institutional_request_access_enabled, + logoPath: data.attributes.logo_path, + userMetricsUrl: data.relationships?.user_metrics?.links?.related?.href, + linkToExternalReportsArchive: data.attributes.link_to_external_reports_archive, + }; + } + + static fromResponseWithMeta(response: InstitutionsWithMetaJsonApiResponse): InstitutionsWithTotalCount { + return { + data: this.fromInstitutionsResponse(response), + total: response.meta.total, + }; + } +} diff --git a/src/app/shared/mappers/institutions/user-institutions.mapper.ts b/src/app/shared/mappers/institutions/user-institutions.mapper.ts deleted file mode 100644 index c9d12cb8f..000000000 --- a/src/app/shared/mappers/institutions/user-institutions.mapper.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Institution, UserInstitutionGetResponse } from '@shared/models'; - -export class UserInstitutionsMapper { - static fromResponse(response: UserInstitutionGetResponse): Institution { - return { - id: response.id, - type: response.type, - name: response.attributes.name, - description: response.attributes.description, - iri: response.attributes.iri, - rorIri: response.attributes.ror_iri, - iris: response.attributes.iris, - assets: response.attributes.assets, - institutionalRequestAccessEnabled: response.attributes.institutional_request_access_enabled, - logoPath: response.attributes.logo_path, - }; - } -} diff --git a/src/app/shared/mappers/resource-overview.mappers.ts b/src/app/shared/mappers/resource-overview.mappers.ts index b8bc641b4..e422d7fd5 100644 --- a/src/app/shared/mappers/resource-overview.mappers.ts +++ b/src/app/shared/mappers/resource-overview.mappers.ts @@ -1,7 +1,7 @@ import { ProjectOverview } from '@osf/features/project/overview/models'; -import { RegistryInstitution, RegistryOverview, RegistrySubject } from '@osf/features/registry/models'; +import { RegistryOverview, RegistrySubject } from '@osf/features/registry/models'; -import { ResourceOverview } from '../models'; +import { Institution, ResourceOverview } from '../models'; export function MapProjectOverview(project: ProjectOverview): ResourceOverview { return { @@ -43,7 +43,7 @@ export function MapProjectOverview(project: ProjectOverview): ResourceOverview { export function MapRegistryOverview( registry: RegistryOverview, subjects: RegistrySubject[], - institutions: RegistryInstitution[] + institutions: Institution[] ): ResourceOverview { return { id: registry.id, diff --git a/src/app/shared/models/institutions/index.ts b/src/app/shared/models/institutions/index.ts index 9dfd7f83d..1078a676e 100644 --- a/src/app/shared/models/institutions/index.ts +++ b/src/app/shared/models/institutions/index.ts @@ -1,3 +1,2 @@ export * from './institution-json-api.model'; export * from './institutions.models'; -export * from './institutions-json-api.model'; 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 2b824d75e..9cadd88df 100644 --- a/src/app/shared/models/institutions/institution-json-api.model.ts +++ b/src/app/shared/models/institutions/institution-json-api.model.ts @@ -1,5 +1,65 @@ -import { InstitutionData } from '@shared/models'; +import { ApiData, InstitutionAssets, JsonApiResponse, ResponseJsonApi } from '@shared/models'; -export interface InstitutionJsonApiModel { - data: InstitutionData; +export type InstitutionsJsonApiResponse = JsonApiResponse; +export type InstitutionsWithMetaJsonApiResponse = ResponseJsonApi; +export type InstitutionJsonApiResponse = JsonApiResponse; + +export type InstitutionDataJsonApi = ApiData< + InstitutionAttributesJsonApi, + null, + InstitutionRelationshipsJsonApi, + InstitutionLinksJsonApi +>; + +interface InstitutionAttributesJsonApi { + name: string; + description: string; + iri: string; + ror_iri: string | null; + iris: string[]; + assets: InstitutionAssets; + institutional_request_access_enabled: boolean; + logo_path: string; + link_to_external_reports_archive: string; +} + +interface InstitutionLinksJsonApi { + self: string; + html: string; + iri: string; +} + +interface InstitutionRelationshipsJsonApi { + nodes: { + links: { + related: { + href: string; + meta: Record; + }; + }; + }; + registrations: { + links: { + related: { + href: string; + meta: Record; + }; + }; + }; + users: { + links: { + related: { + href: string; + meta: Record; + }; + }; + }; + user_metrics: { + links: { + related: { + href: string; + meta: Record; + }; + }; + }; } diff --git a/src/app/shared/models/institutions/institutions-json-api.model.ts b/src/app/shared/models/institutions/institutions-json-api.model.ts deleted file mode 100644 index 4d1c0d9bd..000000000 --- a/src/app/shared/models/institutions/institutions-json-api.model.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { ResponseJsonApi } from '@osf/shared/models'; -import { Institution, InstitutionAttributes } from '@shared/models'; - -export interface InstitutionRelationships { - nodes: { - links: { - related: { - href: string; - meta: Record; - }; - }; - }; - registrations: { - links: { - related: { - href: string; - meta: Record; - }; - }; - }; - users: { - links: { - related: { - href: string; - meta: Record; - }; - }; - }; - user_metrics: { - links: { - related: { - href: string; - meta: Record; - }; - }; - }; -} - -export interface InstitutionLinks { - self: string; - html: string; - iri: string; -} - -export interface InstitutionData { - id: string; - type: string; - attributes: InstitutionAttributes; - relationships: InstitutionRelationships; - links: InstitutionLinks; -} - -export interface InstitutionsResponseLinks { - first: string | null; - last: string | null; - prev: string | null; - next: string | null; -} - -export interface FetchInstitutionsJsonApi extends ResponseJsonApi { - links: InstitutionsResponseLinks; -} - -export interface GetGeneralInstitutionsResponse { - data: Institution[]; - total: number; -} diff --git a/src/app/shared/models/institutions/institutions.models.ts b/src/app/shared/models/institutions/institutions.models.ts index 7c42fa874..f00b4a278 100644 --- a/src/app/shared/models/institutions/institutions.models.ts +++ b/src/app/shared/models/institutions/institutions.models.ts @@ -1,25 +1,6 @@ -export interface InstitutionAssets { - logo: string; - logo_rounded: string; - banner: string; -} - -export interface InstitutionAttributes { - name: string; - description: string; - iri: string; - ror_iri: string | null; - iris: string[]; - assets: InstitutionAssets; - institutional_request_access_enabled: boolean; - logo_path: string; - link_to_external_reports_archive: string; -} - -export interface UserInstitutionGetResponse { - id: string; - type: string; - attributes: InstitutionAttributes; +export interface InstitutionsWithTotalCount { + data: Institution[]; + total: number; } export interface Institution { @@ -36,3 +17,9 @@ export interface Institution { userMetricsUrl?: string; linkToExternalReportsArchive?: string; } + +export interface InstitutionAssets { + logo: string; + logo_rounded: string; + banner: string; +} diff --git a/src/app/shared/models/resource-overview.model.ts b/src/app/shared/models/resource-overview.model.ts index ad81f1de9..8e555ff7f 100644 --- a/src/app/shared/models/resource-overview.model.ts +++ b/src/app/shared/models/resource-overview.model.ts @@ -1,5 +1,6 @@ import { ProjectOverviewContributor } from '@osf/features/project/overview/models'; import { RegistrySubject } from '@osf/features/registry/models'; +import { Institution } from '@shared/models/institutions'; export interface ResourceOverview { id: string; @@ -59,11 +60,7 @@ export interface ResourceOverview { id: string; type: string; }; - affiliatedInstitutions?: { - id: string; - logo: string; - logoRounded?: string; - }[]; + affiliatedInstitutions?: Institution[]; forksCount: number; viewOnlyLinksCount?: number; associatedProjectId?: string; diff --git a/src/app/shared/services/institutions.service.ts b/src/app/shared/services/institutions.service.ts index 4a8afd5b7..0cc3d2c0d 100644 --- a/src/app/shared/services/institutions.service.ts +++ b/src/app/shared/services/institutions.service.ts @@ -3,14 +3,14 @@ import { map } from 'rxjs/operators'; import { inject, Injectable } from '@angular/core'; -import { GeneralInstitutionMapper, UserInstitutionsMapper } from '@shared/mappers'; +import { ResourceType } from '@shared/enums'; +import { InstitutionsMapper } from '@shared/mappers'; import { - FetchInstitutionsJsonApi, - GetGeneralInstitutionsResponse, Institution, - InstitutionJsonApiModel, - JsonApiResponse, - UserInstitutionGetResponse, + InstitutionJsonApiResponse, + InstitutionsJsonApiResponse, + InstitutionsWithMetaJsonApiResponse, + InstitutionsWithTotalCount, } from '@shared/models'; import { JsonApiService } from '@shared/services'; @@ -21,12 +21,12 @@ import { environment } from 'src/environments/environment'; }) export class InstitutionsService { private readonly jsonApiService = inject(JsonApiService); + private readonly urlMap = new Map([ + [ResourceType.Preprint, 'preprints'], + [ResourceType.Agent, 'users'], + ]); - getInstitutions( - pageNumber: number, - pageSize: number, - searchValue?: string - ): Observable { + getInstitutions(pageNumber: number, pageSize: number, searchValue?: string): Observable { const params: Record = {}; if (pageNumber) { @@ -42,22 +42,22 @@ export class InstitutionsService { } return this.jsonApiService - .get(`${environment.apiUrl}/institutions`, params) - .pipe(map((response) => GeneralInstitutionMapper.adaptInstitutions(response))); + .get(`${environment.apiUrl}/institutions/`, params) + .pipe(map((response) => InstitutionsMapper.fromResponseWithMeta(response))); } getUserInstitutions(): Observable { const url = `${environment.apiUrl}/users/me/institutions/`; return this.jsonApiService - .get>(url) - .pipe(map((response) => response.data.map((item) => UserInstitutionsMapper.fromResponse(item)))); + .get(url) + .pipe(map((response) => InstitutionsMapper.fromInstitutionsResponse(response))); } getInstitutionById(institutionId: string): Observable { return this.jsonApiService - .get(`${environment.apiUrl}/institutions/${institutionId}/`) - .pipe(map((result) => GeneralInstitutionMapper.adaptInstitution(result.data))); + .get(`${environment.apiUrl}/institutions/${institutionId}/`) + .pipe(map((response) => InstitutionsMapper.fromInstitutionData(response.data))); } deleteUserInstitution(id: string, userId: string): Observable { @@ -66,4 +66,25 @@ export class InstitutionsService { }; return this.jsonApiService.delete(`${environment.apiUrl}/users/${userId}/relationships/institutions/`, payload); } + + getResourceInstitutions(resourceId: string, resourceType: ResourceType): Observable { + const url = `${environment.apiUrl}/${this.urlMap.get(resourceType)}/${resourceId}/institutions/`; + + return this.jsonApiService + .get(url) + .pipe(map((response) => InstitutionsMapper.fromInstitutionsResponse(response))); + } + + updateResourceInstitutions( + resourceId: string, + resourceType: ResourceType, + institutions: Institution[] + ): Observable { + const baseUrl = `${environment.apiUrl}/${this.urlMap.get(resourceType)}/${resourceId}/relationships/institutions/`; + const payload = { + data: institutions.map((item) => ({ id: item.id, type: 'institutions' })), + }; + + return this.jsonApiService.put(baseUrl, payload); + } } diff --git a/src/app/shared/stores/index.ts b/src/app/shared/stores/index.ts index 66f706c5b..fd2d02ae5 100644 --- a/src/app/shared/stores/index.ts +++ b/src/app/shared/stores/index.ts @@ -4,7 +4,6 @@ export * from './citations'; export * from './collections'; export * from './contributors'; export * from './duplicates'; -export * from './institutions'; export * from './institutions-search'; export * from './licenses'; export * from './my-resources'; diff --git a/src/app/shared/stores/institutions/institutions.actions.ts b/src/app/shared/stores/institutions/institutions.actions.ts index f8f6c03a5..8e359a433 100644 --- a/src/app/shared/stores/institutions/institutions.actions.ts +++ b/src/app/shared/stores/institutions/institutions.actions.ts @@ -1,3 +1,6 @@ +import { ResourceType } from '@shared/enums'; +import { Institution } from '@shared/models'; + export class FetchUserInstitutions { static readonly type = '[Institutions] Fetch User Institutions'; } @@ -11,3 +14,22 @@ export class FetchInstitutions { public searchValue?: string ) {} } + +export class FetchResourceInstitutions { + static readonly type = '[Institutions] Fetch Resource Institutions'; + + constructor( + public resourceId: string, + public resourceType: ResourceType + ) {} +} + +export class UpdateResourceInstitutions { + static readonly type = '[Institutions] Update Resource Institutions'; + + constructor( + public resourceId: string, + public resourceType: ResourceType, + public institutions: Institution[] + ) {} +} diff --git a/src/app/shared/stores/institutions/institutions.model.ts b/src/app/shared/stores/institutions/institutions.model.ts index 13fb7e854..27fe568bf 100644 --- a/src/app/shared/stores/institutions/institutions.model.ts +++ b/src/app/shared/stores/institutions/institutions.model.ts @@ -1,6 +1,26 @@ -import { AsyncStateWithTotalCount, Institution } from '@osf/shared/models'; +import { AsyncStateModel, AsyncStateWithTotalCount, Institution } from '@osf/shared/models'; export interface InstitutionsStateModel { - userInstitutions: Institution[]; + userInstitutions: AsyncStateModel; institutions: AsyncStateWithTotalCount; + resourceInstitutions: AsyncStateModel; } + +export const DefaultState = { + userInstitutions: { + data: [], + isLoading: false, + error: null, + }, + institutions: { + data: [], + isLoading: false, + error: null, + totalCount: 0, + }, + resourceInstitutions: { + data: [], + isLoading: false, + error: null, + }, +}; diff --git a/src/app/shared/stores/institutions/institutions.selectors.ts b/src/app/shared/stores/institutions/institutions.selectors.ts index 9414d163d..e45a590c7 100644 --- a/src/app/shared/stores/institutions/institutions.selectors.ts +++ b/src/app/shared/stores/institutions/institutions.selectors.ts @@ -6,7 +6,12 @@ import { InstitutionsState } from './institutions.state'; export class InstitutionsSelectors { @Selector([InstitutionsState]) static getUserInstitutions(state: InstitutionsStateModel) { - return state.userInstitutions; + return state.userInstitutions.data; + } + + @Selector([InstitutionsState]) + static areUserInstitutionsLoading(state: InstitutionsStateModel) { + return state.userInstitutions.isLoading; } @Selector([InstitutionsState]) @@ -23,4 +28,19 @@ export class InstitutionsSelectors { static getInstitutionsTotalCount(state: InstitutionsStateModel): number { return state.institutions.totalCount; } + + @Selector([InstitutionsState]) + static getResourceInstitutions(state: InstitutionsStateModel) { + return state.resourceInstitutions.data; + } + + @Selector([InstitutionsState]) + static areResourceInstitutionsLoading(state: InstitutionsStateModel) { + return state.resourceInstitutions.isLoading; + } + + @Selector([InstitutionsState]) + static areResourceInstitutionsSubmitting(state: InstitutionsStateModel) { + return state.resourceInstitutions.isSubmitting; + } } diff --git a/src/app/shared/stores/institutions/institutions.state.ts b/src/app/shared/stores/institutions/institutions.state.ts index 29728f2bb..69874ded3 100644 --- a/src/app/shared/stores/institutions/institutions.state.ts +++ b/src/app/shared/stores/institutions/institutions.state.ts @@ -1,26 +1,24 @@ import { Action, State, StateContext } from '@ngxs/store'; import { patch } from '@ngxs/store/operators'; -import { catchError, tap, throwError } from 'rxjs'; +import { catchError, Observable, tap, throwError } from 'rxjs'; import { inject, Injectable } from '@angular/core'; import { InstitutionsService } from '@osf/shared/services'; +import { handleSectionError } from '@shared/helpers'; -import { FetchInstitutions, FetchUserInstitutions } from './institutions.actions'; -import { InstitutionsStateModel } from './institutions.model'; +import { + FetchInstitutions, + FetchResourceInstitutions, + FetchUserInstitutions, + UpdateResourceInstitutions, +} from './institutions.actions'; +import { DefaultState, InstitutionsStateModel } from './institutions.model'; @State({ name: 'institutions', - defaults: { - userInstitutions: [], - institutions: { - data: [], - isLoading: false, - error: null, - totalCount: 0, - }, - }, + defaults: { ...DefaultState }, }) @Injectable() export class InstitutionsState { @@ -28,12 +26,20 @@ export class InstitutionsState { @Action(FetchUserInstitutions) getUserInstitutions(ctx: StateContext) { + ctx.setState(patch({ userInstitutions: patch({ isLoading: true }) })); + return this.institutionsService.getUserInstitutions().pipe( tap((institutions) => { - ctx.patchState({ - userInstitutions: institutions, - }); - }) + ctx.setState( + patch({ + userInstitutions: patch({ + isLoading: false, + data: institutions, + }), + }) + ); + }), + catchError((error) => handleSectionError(ctx, 'userInstitutions', error)) ); } @@ -73,4 +79,48 @@ export class InstitutionsState { }) ); } + + @Action(FetchResourceInstitutions) + fetchResourceInstitutions(ctx: StateContext, action: FetchResourceInstitutions) { + ctx.setState(patch({ resourceInstitutions: patch({ isLoading: true }) })); + + return this.institutionsService.getResourceInstitutions(action.resourceId, action.resourceType).pipe( + tap((institutions) => { + ctx.setState( + patch({ + resourceInstitutions: patch({ + data: institutions, + isLoading: false, + error: null, + }), + }) + ); + }), + catchError((error) => handleSectionError(ctx, 'resourceInstitutions', error)) + ); + } + + @Action(UpdateResourceInstitutions) + updateResourceInstitutions( + ctx: StateContext, + action: UpdateResourceInstitutions + ): Observable { + ctx.setState(patch({ resourceInstitutions: patch({ isSubmitting: true }) })); + + return this.institutionsService + .updateResourceInstitutions(action.resourceId, action.resourceType, action.institutions) + .pipe( + tap(() => { + ctx.setState( + patch({ + resourceInstitutions: patch({ + data: action.institutions, + isSubmitting: false, + }), + }) + ); + }), + catchError((error) => handleSectionError(ctx, 'resourceInstitutions', error)) + ); + } } diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 6e1be8e00..e1bfc9fcc 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -1888,7 +1888,9 @@ }, "tagsTitle": "Tags (optional)", "publicationDateTitle": "Publication Date (optional)", - "publicationCitationTitle": "Publication Citation (optional)" + "publicationCitationTitle": "Publication Citation (optional)", + "affiliatedInstitutionsTitle": "Affiliated Institutions", + "affiliatedInstitutionsDescription": "You can affiliate your {{preprintWord}} with your institution if it is an OSF institutional member and has worked with the Center for Open Science to create a dedicated institutional OSF landing page." }, "authorAssertions": { "title": "Author Assertions", @@ -2765,4 +2767,4 @@ "software": "Software", "other": "Other" } -} \ No newline at end of file +}